{"id":1082,"date":"2019-02-08T12:53:16","date_gmt":"2019-02-08T12:53:16","guid":{"rendered":"http:\/\/www.netexl.com\/blog\/?p=1082"},"modified":"2019-02-08T12:53:16","modified_gmt":"2019-02-08T12:53:16","slug":"exploring-phaser-3-with-a-game-adding-tweens","status":"publish","type":"post","link":"https:\/\/www.netexl.com\/blog\/exploring-phaser-3-with-a-game-adding-tweens\/","title":{"rendered":"Exploring Phaser 3 with a Game: Adding Tweens"},"content":{"rendered":"<p>In previous articles (<a href=\"https:\/\/www.netexl.com\/blog\/exploring-phaser-3-with-a-game\/\" target=\"_blank\">part 1<\/a> and <a href=\"https:\/\/www.netexl.com\/blog\/exploring-phaser-3-with-a-game-adding-multiple-scenes\/\" target=\"_blank\">part 2<\/a>) we created a game with multiple scenes. Now we are going to add some tweens to it to make pegs jump over the holes.<\/p>\n<pre class=\"lang:default mark:78-80,197-228,162,174 decode:true \">let loadGame = function () {\r\n    let config = {\r\n        type: Phaser.AUTO,\r\n        width: 500,\r\n        height: 500,\r\n        backgroundColor: 0xFF0000,\r\n        scene: TheGame\r\n    }\r\n\r\n    let game = new Phaser.Game(config);\r\n};\r\n\r\nif (document.readyState === \"complete\" || (document.readyState !== \"loading\" &amp;&amp; !document.documentElement.doScroll)) {\r\n    loadGame();\r\n} else {\r\n    document.addEventListener(\"DOMContentLoaded\", loadGame);\r\n}\r\n\r\nclass TheGame extends Phaser.Scene {\r\n\r\n    constructor() {\r\n        super(\"TheGame\");\r\n    }\r\n\r\n    preload() {\r\n        this.load.spritesheet(\"pegs\", \"images\/pegs.png\", {\r\n            frameWidth: 60,\r\n            frameHeight: 60\r\n        });\r\n    }\r\n\r\n    create() {\r\n        this.boardDef = [\r\n            [-1, -1, 1, 1, 1, -1, -1],\r\n            [-1, -1, 1, 1, 1, -1, -1],\r\n            [1, 1, 1, 1, 1, 1, 1],\r\n            [1, 1, 1, 0, 1, 1, 1],\r\n            [1, 1, 1, 1, 1, 1, 1],\r\n            [-1, -1, 1, 1, 1, -1, -1],\r\n            [-1, -1, 1, 1, 1, -1, -1]\r\n        ];\r\n\r\n        \/\/  If a Game Object is clicked on, this event is fired.\r\n        \/\/  We can use it to emit the 'clicked' event on the game object itself.\r\n        this.input.on('gameobjectup', function (pointer, gameObject) {\r\n            gameObject.emit('clicked', gameObject);\r\n        }, this);\r\n\r\n        \/\/ add our sprites\r\n        this.board = [];\r\n        this.selectedPeg = null;\r\n        this.movesCount = 0;\r\n\r\n        for (let i = 0, len = this.boardDef.length; i &lt; len; i++) {\r\n            let r = this.boardDef[i];\r\n            let row = [];\r\n            this.board.push(row);\r\n            for (let j = 0, cnt = r.length; j &lt; cnt; j++) {\r\n                let c = r[j];\r\n                if (c &gt;= 0) {\r\n                    let cell = this.add.image(45 + i * 60, 45 + j * 60, \"pegs\");\r\n                    cell.setFrame(c &gt; 0 ? 1 : 0);\r\n                    cell.setOrigin(0);\r\n\r\n                    \/\/ enable input events\r\n                    cell.setInteractive();\r\n                    cell.on('clicked', this.clickPeg, this);\r\n                    cell.gridX = i;\r\n                    cell.gridY = j;\r\n                    row.push(cell);\r\n                } else {\r\n                    row.push(null);\r\n                }\r\n            }\r\n        }\r\n        this.movesLabel = this.add.text(0, 0, 'Moves: ' + this.movesCount, { font: '24px Courier', fill: '#000000' });\r\n\r\n        this.tempPeg = this.add.sprite(-200, -200, \"pegs\");\r\n        this.tempPeg.setFrame(1);\r\n        this.tempPeg.setOrigin(0);\r\n    }\r\n\r\n    updateMoves(movesCount) {\r\n        this.movesLabel.setText('Moves: ' + movesCount);\r\n    }\r\n\r\n    gameOver() {\r\n        this.registry.set('gamedata', { movesCount: this.movesCount, remainingPegs: this.remainingPegs() });\r\n        this.scene.remove('TheGame');\r\n        let gameOver = new GameOver('GameOver');\r\n        this.scene.add('GameOver', gameOver, true);\r\n    }\r\n\r\n    isAnyValidMove() {\r\n        let colsCount = this.board.length;\r\n        for (let i = 0; i &lt; colsCount; i++) {\r\n            let col = this.board[i];\r\n            for (let j = 0, endIndex = col.length - 3; j &lt;= endIndex; j++) {\r\n                let c1 = col[j];\r\n                let c2 = col[j + 1];\r\n                let c3 = col[j + 2];\r\n\r\n                if (c1 &amp;&amp; c2 &amp;&amp; c3) {\r\n                    if (c1.frame.name !== 0 &amp;&amp; c2.frame.name !== 0 &amp;&amp; c3.frame.name === 0) return true;\r\n                    if (c1.frame.name === 0 &amp;&amp; c2.frame.name !== 0 &amp;&amp; c3.frame.name !== 0) return true;\r\n                }\r\n            }\r\n        }\r\n\r\n        var rowsCount = this.board[0].length;\r\n        for (let i = 0, len = colsCount - 3; i &lt;= len; i++) {\r\n            let r1 = this.board[i];\r\n            let r2 = this.board[i + 1];\r\n            let r3 = this.board[i + 2];\r\n            for (let j = 0; j &lt; rowsCount; j++) {\r\n                let c1 = r1[j];\r\n                let c2 = r2[j];\r\n                let c3 = r3[j];\r\n\r\n                if (c1 &amp;&amp; c2 &amp;&amp; c3) {\r\n                    if (c1.frame.name !== 0 &amp;&amp; c2.frame.name !== 0 &amp;&amp; c3.frame.name === 0) return true;\r\n                    if (c1.frame.name === 0 &amp;&amp; c2.frame.name !== 0 &amp;&amp; c3.frame.name !== 0) return true;\r\n                }\r\n            }\r\n        }\r\n        return false;\r\n    }\r\n\r\n    remainingPegs() {\r\n        var pegs = 0;\r\n        for (let i = 0, len = this.board.length; i &lt; len; i++) {\r\n            let row = this.board[i];\r\n            for (let j = 0, cnt = row.length; j &lt; cnt; j++) {\r\n                let cell = row[j];\r\n                if (cell &amp;&amp; cell.frame.name !== 0) {\r\n                    pegs++\r\n                }\r\n            }\r\n        }\r\n        return pegs;\r\n    }\r\n\r\n    clickPeg(peg) {\r\n\r\n        if (peg.frame.name === 0) {\r\n            \/\/ if we have not selected a peg to jump then no need to move any further\r\n            if (!this.selectedPeg)\r\n                return;\r\n\r\n            let clickedX = peg.gridX;\r\n            let clickedY = peg.gridY;\r\n            let selectedX = this.selectedPeg.gridX;\r\n            let selectedY = this.selectedPeg.gridY;\r\n\r\n            if ((clickedX + 2 === selectedX || clickedX - 2 === selectedX) &amp;&amp; clickedY === selectedY) {\r\n                \/\/ move horizontal\r\n                let pegToRemove = this.board[(selectedX + clickedX) \/ 2][clickedY];\r\n                if (pegToRemove.frame.name === 0)\r\n                    return;\r\n\r\n                this.updateMoves(++this.movesCount);\r\n                this.removePeg(this.tempPeg, this.selectedPeg, peg, pegToRemove);\r\n\r\n                this.selectedPeg.setFrame(0);\r\n                this.selectedPeg = null;\r\n\r\n            } else if ((clickedY + 2 === selectedY || clickedY - 2 === selectedY) &amp;&amp; clickedX === selectedX) {\r\n                \/\/ move vertical\r\n                let pegToRemove = this.board[clickedX][(selectedY + clickedY) \/ 2];\r\n                if (pegToRemove.frame.name === 0)\r\n                    return;\r\n\r\n                this.updateMoves(++this.movesCount);\r\n                this.removePeg(this.tempPeg, this.selectedPeg, peg, pegToRemove);\r\n\r\n                this.selectedPeg.setFrame(0);\r\n                this.selectedPeg = null;\r\n            }\r\n\r\n        } else {\r\n            if (this.selectedPeg) {\r\n                if (peg === this.selectedPeg) {\r\n                    peg.setFrame(1);\r\n                    this.selectedPeg = null;\r\n                } else {\r\n                    this.selectedPeg.setFrame(1);\r\n                    this.selectedPeg = peg;\r\n                    peg.setFrame(2);\r\n                }\r\n            } else {\r\n                this.selectedPeg = peg;\r\n                peg.setFrame(2);\r\n            }\r\n        }\r\n    }\r\n\r\n    removePeg(tempPeg, selectedPeg, targetPeg, pegToRemove) {\r\n        tempPeg.setPosition(selectedPeg.x, selectedPeg.y);\r\n        tempPeg.targetPeg = targetPeg;\r\n        tempPeg.removePeg = pegToRemove;\r\n        tempPeg.visible = true;\r\n        var self = this;\r\n        this.pegTween = this.tweens.add({\r\n            targets: tempPeg,\r\n            x: targetPeg.x,\r\n            y: targetPeg.y,\r\n            duration: 200,\r\n            delay: 50,\r\n            onStart: function (tween) {\r\n                var sprite = tween.targets[0];\r\n                sprite.removePeg.setFrame(0);\r\n            },\r\n            onComplete: function (tween) {\r\n                var sprite = tween.targets[0];\r\n                sprite.targetPeg.setFrame(1);\r\n                sprite.visible = false;\r\n                if (!self.isAnyValidMove()) {\r\n                    let timedEvent = self.time.addEvent({\r\n                        delay: 3000,\r\n                        callbackScope: this,\r\n                        callback: function () {\r\n                            self.gameOver();\r\n                        }\r\n                    });\r\n                }\r\n            }\r\n        });\r\n    }\r\n}\r\n\r\nclass GameOver extends Phaser.Scene {\r\n \r\n    constructor() {\r\n        super(\"GameOver\");\r\n    }\r\n\r\n    preload() {\r\n        this.load.image(\"restart\", \"images\/restart.png\");\r\n    }\r\n \r\n    create() {\r\n        var gamedata = this.registry.get('gamedata');\r\n\r\n        this.add.text(140, 100, 'Game Over', { font: '42px Courier', fill: '#000000' });\r\n        this.add.text(155, 160, 'Moves: ' + gamedata.movesCount, { font: '42px Courier', fill: '#000000' });\r\n        if (gamedata.remainingPegs &gt; 1) {\r\n            this.remainingPegs = this.add.text(30, 220, 'Remaining Pegs: ' + gamedata.remainingPegs, { font: '42px Courier', fill: '#000000' });\r\n        }\r\n        var btn = this.add.image(175, 300, 'restart');\r\n        btn.setInteractive();\r\n        btn.setOrigin(0);\r\n        btn.on('pointerup', this.startGame, this);\r\n\r\n    }\r\n\r\n    startGame() {\r\n        this.scene.remove('GameOver');\r\n        let theGame = new TheGame('TheGame');\r\n        this.scene.add('TheGame', theGame, true);\r\n    }\r\n}<\/pre>\n<p>Since the main board is just switching the sprite frames, we needed a temporary sprite which we will move between the holes. We added &#8220;removePeg&#8221; method which starts the tween on temporary sprite. clickPeg method is changed to call &#8220;removePeg&#8221; and we also removed code to update sprite frames in clickPeg. Those frames will now be changed in the tween onStart and onComplete events. Code to check for &#8220;gameover&#8221; is also moved to the tween onComplete event since we want to wait for the pegs to move before checking\u00a0for\u00a0&#8220;gameover&#8221; and switching to another scene.<\/p>\n<p>The updated game code now runs as following<\/p>\n\n<!-- iframe plugin v.5.2 wordpress.org\/plugins\/iframe\/ -->\n<iframe loading=\"lazy\" src=\"https:\/\/www.netexl.com\/blog\/demo\/16\/index.html\" width=\"500px\" height=\"500px\" scrolling=\"yes\" class=\"iframe-class\" frameborder=\"0\"><\/iframe>\n\n","protected":false},"excerpt":{"rendered":"<p>In previous articles (part 1 and part 2) we created a game with multiple scenes. Now we are going to add some tweens to it to make pegs jump over the holes. let loadGame = function () { let config = { type: Phaser.AUTO, width: 500, height: 500, backgroundColor: 0xFF0000, scene: TheGame } let game = new Phaser.Game(config); }; if (document.readyState === &#8220;complete&#8221; || (document.readyState !== &#8220;loading&#8221; &amp;&amp; !document.documentElement.doScroll)) { loadGame(); } else { document.addEventListener(&#8220;DOMContentLoaded&#8221;, loadGame); } class TheGame extends Phaser.Scene { constructor() { super(&#8220;TheGame&#8221;); } preload() { this.load.spritesheet(&#8220;pegs&#8221;, &#8220;images\/pegs.png&#8221;, { frameWidth: 60, frameHeight: 60 }); } create() { this.boardDef = [ [-1, -1, 1, 1, 1, -1, -1], [-1, -1, 1, 1, 1, -1, -1], [1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 0, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1], [-1, -1, 1, 1, 1, -1, -1], [-1, -1, 1, 1, 1, -1,[&#8230;]<\/p>\n","protected":false},"author":5,"featured_media":293,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3,24,4],"tags":[],"class_list":["post-1082","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-html5","category-javascript","category-phaser"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/posts\/1082","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/comments?post=1082"}],"version-history":[{"count":4,"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/posts\/1082\/revisions"}],"predecessor-version":[{"id":1087,"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/posts\/1082\/revisions\/1087"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/media\/293"}],"wp:attachment":[{"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/media?parent=1082"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/categories?post=1082"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/tags?post=1082"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}