├── .gitignore
├── favicon.ico
├── assets
└── images
│ ├── logo.png
│ ├── quit.png
│ ├── sound.png
│ ├── button.png
│ ├── fullscreen.png
│ ├── 540x960-guide.png
│ └── 640x960-guide.png
├── README.md
├── utils
├── buttons.js
└── screen.js
├── package.json
├── index.html
├── scenes
├── title.js
├── handler.js
├── preload.js
└── hub.js
└── main.js
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 |
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shimozurdo/mobile-game-base-phaser3/HEAD/favicon.ico
--------------------------------------------------------------------------------
/assets/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shimozurdo/mobile-game-base-phaser3/HEAD/assets/images/logo.png
--------------------------------------------------------------------------------
/assets/images/quit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shimozurdo/mobile-game-base-phaser3/HEAD/assets/images/quit.png
--------------------------------------------------------------------------------
/assets/images/sound.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shimozurdo/mobile-game-base-phaser3/HEAD/assets/images/sound.png
--------------------------------------------------------------------------------
/assets/images/button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shimozurdo/mobile-game-base-phaser3/HEAD/assets/images/button.png
--------------------------------------------------------------------------------
/assets/images/fullscreen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shimozurdo/mobile-game-base-phaser3/HEAD/assets/images/fullscreen.png
--------------------------------------------------------------------------------
/assets/images/540x960-guide.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shimozurdo/mobile-game-base-phaser3/HEAD/assets/images/540x960-guide.png
--------------------------------------------------------------------------------
/assets/images/640x960-guide.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shimozurdo/mobile-game-base-phaser3/HEAD/assets/images/640x960-guide.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Mobile game with phaser 3
2 | Base project for make a mobile game using Phaser 3
3 |
4 | []()
5 |
6 | If you use this code in one of your games and you need to convert to an old javascript version using babel and webpack, check out my other game project.
7 |
8 | https://github.com/shimozurdo/math-attack
9 |
10 |
--------------------------------------------------------------------------------
/utils/buttons.js:
--------------------------------------------------------------------------------
1 | function pointerOver(gameObjet, hex = 0xEFF0F1) {
2 | gameObjet.on('pointerover', function () {
3 | this.setTint(hex);
4 | });
5 | pointerOut(gameObjet);
6 | }
7 |
8 | function pointerOut(gameObjet) {
9 | gameObjet.on('pointerout', function () {
10 | this.clearTint();
11 | });
12 | }
13 |
14 | function pointerUp(res = () => { }, gameObjet) {
15 | gameObjet.on('pointerup', () => {
16 | res();
17 | });
18 | }
19 |
20 | export {
21 | pointerOver,
22 | pointerOut,
23 | pointerUp
24 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mobile-game-base-phaser3",
3 | "version": "1.0.0",
4 | "description": "Base project for make a mobile game using Phaser 3",
5 | "main": "main.js",
6 | "scripts": {
7 | "start": "live-server --open=index.html",
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/shimozurdo/mobile-game-base-phaser3.git"
13 | },
14 | "keywords": [],
15 | "author": "",
16 | "license": "ISC",
17 | "bugs": {
18 | "url": "https://github.com/shimozurdo/mobile-game-base-phaser3/issues"
19 | },
20 | "homepage": "https://github.com/shimozurdo/mobile-game-base-phaser3#readme",
21 | "dependencies": {
22 | "live-server": "^1.2.1"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
22 | Game
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/scenes/title.js:
--------------------------------------------------------------------------------
1 | export default class Title extends Phaser.Scene {
2 |
3 | // Vars
4 | handlerScene = false
5 | sceneStopped = false
6 |
7 | constructor() {
8 | super({ key: 'title' })
9 | }
10 |
11 | preload() {
12 | this.sceneStopped = false
13 | this.width = this.game.screenBaseSize.width
14 | this.height = this.game.screenBaseSize.height
15 | this.handlerScene = this.scene.get('handler')
16 | this.handlerScene.sceneRunning = 'title'
17 | }
18 |
19 | create() {
20 | const { width, height } = this
21 | // CONFIG SCENE
22 | this.handlerScene.updateResize(this)
23 | if (this.game.debugMode)
24 | this.add.image(0, 0, 'guide').setOrigin(0).setDepth(1)
25 | // CONFIG SCENE
26 |
27 | // GAME OBJECTS
28 | this.playBtn = this.add.image(width / 2, height / 2, 'button').setOrigin(.5)
29 | // GAME OBJECTS
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/utils/screen.js:
--------------------------------------------------------------------------------
1 | function fullScreen() {
2 | this.scale.fullscreenTarget = document.getElementById('game')
3 | let F11Key = this.input.keyboard.addKey('F11')
4 | F11Key.on('down', () => {
5 | if (this.scale.isFullscreen) {
6 | this.scale.stopFullscreen()
7 | console.log('Stop fullscreen')
8 | }
9 | else {
10 | this.scale.startFullscreen()
11 | console.log('Start fullscreen')
12 | }
13 | })
14 |
15 | document.addEventListener('fullscreenchange', exitHandler)
16 | document.addEventListener('webkitfullscreenchange', exitHandler)
17 | document.addEventListener('mozfullscreenchange', exitHandler)
18 | document.addEventListener('MSFullscreenChange', exitHandler)
19 |
20 | function exitHandler() {
21 | if (!document.fullscreenElement && !document.webkitIsFullScreen && !document.mozFullScreen && !document.msFullscreenElement) {
22 | console.log('Catch key escape event')
23 | }
24 | }
25 | }
26 |
27 | export {
28 | fullScreen
29 | }
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | import Handler from './scenes/handler.js'
2 | import Title from './scenes/title.js'
3 | import Preload from './scenes/preload.js'
4 | import Hub from './scenes/hub.js'
5 |
6 | // Aspect Ratio 16:9 - Portrait
7 | const MAX_SIZE_WIDTH_SCREEN = 1920
8 | const MAX_SIZE_HEIGHT_SCREEN = 1080
9 | const MIN_SIZE_WIDTH_SCREEN = 270
10 | const MIN_SIZE_HEIGHT_SCREEN = 480
11 | const SIZE_WIDTH_SCREEN = 540
12 | const SIZE_HEIGHT_SCREEN = 960
13 |
14 | const config = {
15 | type: Phaser.AUTO,
16 | scale: {
17 | mode: Phaser.Scale.RESIZE,
18 | parent: 'game',
19 | width: SIZE_WIDTH_SCREEN,
20 | height: SIZE_HEIGHT_SCREEN,
21 | min: {
22 | width: MIN_SIZE_WIDTH_SCREEN,
23 | height: MIN_SIZE_HEIGHT_SCREEN
24 | },
25 | max: {
26 | width: MAX_SIZE_WIDTH_SCREEN,
27 | height: MAX_SIZE_HEIGHT_SCREEN
28 | }
29 | },
30 | dom: {
31 | createContainer: true
32 | },
33 | scene: [Handler, Hub, Preload, Title]
34 |
35 | }
36 |
37 | const game = new Phaser.Game(config)
38 |
39 | // Global
40 | game.debugMode = true
41 | game.embedded = false // game is embedded into a html iframe/object
42 |
43 | game.screenBaseSize = {
44 | maxWidth: MAX_SIZE_WIDTH_SCREEN,
45 | maxHeight: MAX_SIZE_HEIGHT_SCREEN,
46 | minWidth: MIN_SIZE_WIDTH_SCREEN,
47 | minHeight: MIN_SIZE_HEIGHT_SCREEN,
48 | width: SIZE_WIDTH_SCREEN,
49 | height: SIZE_HEIGHT_SCREEN
50 | }
51 |
52 | game.orientation = "portrait"
--------------------------------------------------------------------------------
/scenes/handler.js:
--------------------------------------------------------------------------------
1 | export default class Handler extends Phaser.Scene {
2 |
3 | // Vars
4 | sceneRunning = null
5 |
6 | constructor() {
7 | super('handler')
8 | }
9 |
10 | create() {
11 | this.cameras.main.setBackgroundColor('#FFF')
12 | this.launchScene('preload')
13 | this.launchScene('hub')
14 | }
15 |
16 | launchScene(scene, data) {
17 | this.scene.launch(scene, data)
18 | this.gameScene = this.scene.get(scene)
19 | }
20 |
21 | updateResize(scene) {
22 | scene.scale.on('resize', this.resize, scene)
23 |
24 | const scaleWidth = scene.scale.gameSize.width
25 | const scaleHeight = scene.scale.gameSize.height
26 |
27 | scene.parent = new Phaser.Structs.Size(scaleWidth, scaleHeight)
28 | scene.sizer = new Phaser.Structs.Size(scene.width, scene.height, Phaser.Structs.Size.FIT, scene.parent)
29 |
30 | scene.parent.setSize(scaleWidth, scaleHeight)
31 | scene.sizer.setSize(scaleWidth, scaleHeight)
32 |
33 | this.updateCamera(scene)
34 | }
35 |
36 | resize(gameSize) {
37 | // 'this' means to the current scene that is running
38 | if (!this.sceneStopped) {
39 | const width = gameSize.width
40 | const height = gameSize.height
41 |
42 | this.parent.setSize(width, height)
43 | this.sizer.setSize(width, height)
44 |
45 | // updateCamera - TO DO: Improve the next code because it is duplicated
46 | const camera = this.cameras.main
47 | const scaleX = this.sizer.width / this.game.screenBaseSize.width
48 | const scaleY = this.sizer.height / this.game.screenBaseSize.height
49 |
50 | camera.setZoom(Math.max(scaleX, scaleY))
51 | camera.centerOn(this.game.screenBaseSize.width / 2, this.game.screenBaseSize.height / 2)
52 | }
53 | }
54 |
55 | updateCamera(scene) {
56 | const camera = scene.cameras.main
57 | const scaleX = scene.sizer.width / this.game.screenBaseSize.width
58 | const scaleY = scene.sizer.height / this.game.screenBaseSize.height
59 |
60 | camera.setZoom(Math.max(scaleX, scaleY))
61 | camera.centerOn(this.game.screenBaseSize.width / 2, this.game.screenBaseSize.height / 2)
62 | }
63 |
64 | }
--------------------------------------------------------------------------------
/scenes/preload.js:
--------------------------------------------------------------------------------
1 | export default class Preload extends Phaser.Scene {
2 |
3 | width = null
4 | height = null
5 | handlerScene = null
6 | sceneStopped = false
7 |
8 | constructor() {
9 | super({ key: 'preload' })
10 | }
11 |
12 | preload() {
13 | // Images
14 | this.load.image('logo', 'assets/images/logo.png')
15 | this.load.image('guide', 'assets/images/540x960-guide.png')
16 | this.load.image('button', 'assets/images/button.png')
17 | //---------------------------------------------------------------------->
18 | this.canvasWidth = this.sys.game.canvas.width
19 | this.canvasHeight = this.sys.game.canvas.height
20 |
21 | this.width = this.game.screenBaseSize.width
22 | this.height = this.game.screenBaseSize.height
23 |
24 | this.handlerScene = this.scene.get('handler')
25 | this.handlerScene.sceneRunning = 'preload'
26 | this.sceneStopped = false
27 |
28 | let progressBox = this.add.graphics()
29 | progressBox.fillStyle(0x000, 0.8)
30 | progressBox.fillRect((this.canvasWidth / 2) - (210 / 2), (this.canvasHeight / 2) - 5, 210, 30)
31 | let progressBar = this.add.graphics()
32 |
33 | this.load.on('progress', (value) => {
34 | progressBar.clear()
35 | progressBar.fillStyle(0xFF5758, 1)
36 | progressBar.fillRect((this.canvasWidth / 2) - (200 / 2), (this.canvasHeight / 2), 200 * value, 20)
37 | })
38 |
39 | this.load.on('complete', () => {
40 | progressBar.destroy()
41 | progressBox.destroy()
42 | this.time.addEvent({
43 | delay: this.game.debugMode ? 3000 : 4000,
44 | callback: () => {
45 | this.sceneStopped = true
46 | this.scene.stop('preload')
47 | this.handlerScene.cameras.main.setBackgroundColor("#020079")
48 | this.handlerScene.launchScene('title')
49 | },
50 | loop: false
51 | })
52 | })
53 | }
54 |
55 | create() {
56 | const { width, height } = this
57 | // CONFIG SCENE
58 | this.handlerScene.updateResize(this)
59 | if (this.game.debugMode)
60 | this.add.image(0, 0, 'guide').setOrigin(0).setDepth(1)
61 | // CONFIG SCENE
62 |
63 | // GAME OBJECTS
64 | this.add.image(width / 2, height / 2, 'logo').setOrigin(.5)
65 | // GAME OBJECTS
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/scenes/hub.js:
--------------------------------------------------------------------------------
1 | import { fullScreen } from '../utils/screen.js'
2 | import { pointerUp } from '../utils/buttons.js'
3 |
4 | export default class Hub extends Phaser.Scene {
5 |
6 | // Vars
7 | handlerScene = null
8 |
9 | constructor() {
10 | super('hub')
11 | }
12 |
13 | preload() {
14 | // Images
15 | this.load.image('quit', 'assets/images/quit.png')
16 | this.load.spritesheet('fullscreen', 'assets/images/fullscreen.png', { frameWidth: 48, frameHeight: 48 })
17 | this.load.spritesheet("sound", "assets/images/sound.png", { frameWidth: 48, frameHeight: 48 })
18 | //---------------------------------------------------------------------->
19 | this.canvasWidth = this.sys.game.canvas.width
20 | this.canvasHeight = this.sys.game.canvas.height
21 | this.handlerScene = this.scene.get('handler')
22 | //Orientation
23 | this.scale.lockOrientation(this.game.orientation)
24 |
25 | // Bindings
26 | this.pointerUp = pointerUp.bind(this)
27 | if (!this.game.embedded)
28 | fullScreen.call(this)
29 | this.creditsTxt = this.add.text(this.canvasWidth / 2, this.canvasHeight - 22, 'Shimozurdo Games 2021', { fontFamily: 'Arial', fontSize: '18px', color: '#000', }).setOrigin(.5).setDepth(1)
30 | }
31 |
32 | create() {
33 |
34 | let posItemHubBase = 32
35 | this.quitBtn = this.add.image(posItemHubBase, posItemHubBase, "quit").setOrigin(.5).setDepth(1).setInteractive({ cursor: "pointer" })
36 | this.quitBtn.visible = false
37 |
38 | this.pointerUp(() => {
39 | this.clickBackScene(this.handlerScene.sceneRunning)
40 | }, this.quitBtn)
41 |
42 | let multiplePosY = this.game.embedded ? 1 : 3
43 | this.soundBtn = this.add.image(this.canvasWidth - posItemHubBase, posItemHubBase * multiplePosY, "sound").setOrigin(.5).setDepth(1).setInteractive({ cursor: "pointer" })
44 | this.soundBtn.visible = false
45 |
46 | if (this.game.debugMode) {
47 |
48 | this.soundBtn.setFrame(1)
49 | } else {
50 | //this.music.play()
51 | this.soundBtn.setFrame(0)
52 | }
53 |
54 | this.soundBtn.on("pointerup", () => {
55 | if (this.soundBtn.frame.name === 0) {
56 | this.soundBtn.setFrame(1)
57 |
58 | }
59 | else {
60 | this.soundBtn.setFrame(0)
61 |
62 | }
63 | })
64 |
65 | if (!this.game.embedded) {
66 | multiplePosY = this.game.embedded ? 3 : 1
67 | this.fullscreenBtn = this.add.image(this.canvasWidth - posItemHubBase, posItemHubBase * multiplePosY, "fullscreen", 0).setOrigin(.5).setDepth(1).setInteractive({ cursor: "pointer" })
68 |
69 | this.fullscreenBtn.on("pointerup", () => {
70 | if (this.scale.isFullscreen) {
71 | this.fullscreenBtn.setFrame(0)
72 | this.scale.stopFullscreen()
73 | }
74 | else {
75 | this.fullscreenBtn.setFrame(1)
76 | this.scale.startFullscreen()
77 | }
78 | })
79 | }
80 | this.scale.on("resize", this.resize, this)
81 | }
82 |
83 |
84 | update() {
85 | if (this.handlerScene.sceneRunning === 'title') {
86 | this.soundBtn.visible = true
87 | this.quitBtn.visible = false
88 | this.creditsTxt.visible = false
89 | }
90 | }
91 |
92 | clickBackScene(sceneTxt) {
93 | const scene = this.scene.get(sceneTxt)
94 | let gotoScene
95 | let bgColorScene
96 |
97 | switch (sceneTxt) {
98 | case "title":
99 | this.creditsTxt.visible = false
100 | return
101 | }
102 |
103 | scene.sceneStopped = true
104 | scene.scene.stop(sceneTxt)
105 | this.handlerScene.cameras.main.setBackgroundColor(bgColorScene)
106 | this.handlerScene.launchScene(gotoScene)
107 | }
108 |
109 | resize() {
110 | if (!this.game.embedded)
111 | this.fullscreenBtn.x = this.scale.gameSize.width - 30
112 | this.soundBtn.x = this.scale.gameSize.width - 30
113 | this.creditsTxt.x = this.scale.gameSize.width / 2
114 | this.creditsTxt.y = this.scale.gameSize.height - 30
115 | }
116 | }
--------------------------------------------------------------------------------