{"id":127,"date":"2017-04-07T11:16:45","date_gmt":"2017-04-07T11:16:45","guid":{"rendered":"http:\/\/www.netexl.com\/blog\/?p=127"},"modified":"2017-04-08T08:16:11","modified_gmt":"2017-04-08T08:16:11","slug":"display-a-hint-using-tween-in-phaser","status":"publish","type":"post","link":"https:\/\/www.netexl.com\/blog\/display-a-hint-using-tween-in-phaser\/","title":{"rendered":"Display a hint using tween in Phaser"},"content":{"rendered":"<p>This is next task I planned to implement in <a href=\"http:\/\/www.netexl.com\/games\/numberspread\/\" target=\"_blank\">Number Spread<\/a> game I discussed in the previous\u00a0<a href=\"https:\/\/www.netexl.com\/blog\/making-of-a-responsive-game-in-phaser-part-1\/\">article series<\/a>. In a game we have many scenarios when we want to display a hint and I considered many options as to how this can be implemented in Phaser. The\u00a0options could be either using a tween or animation. For Number Spread it was a simple scenario when I just needed to display a hand which moved in certain\u00a0direction to give a hint to player what is to be done. A tween for this purpose seemed fine. Look at the complete code for it below.<\/p>\n<pre class=\"theme:xcode lang:default mark:251-281 decode:true \">var TheGame = {\r\n};\r\n\r\nTheGame.Params = {\r\n\tbaseWidth: 1920,\r\n\tbaseHeight: 1080,\r\n\tminPadding: 50,\r\n\ticonSize: 364,\r\n\t    fieldSize: {\r\n        rows: 6,\r\n        cols: 6\r\n    }\r\n};\r\n\r\nTheGame.Boot = function (game) { };\r\n\r\nTheGame.Boot.prototype =  {\r\n    init: function () {\r\n        this.scale.scaleMode = Phaser.ScaleManager.RESIZE;\r\n    },\r\n    preload: function () {\r\n        this.load.image(\"loading\", \"loadingback.png\");\r\n    },\r\n    create: function () {\r\n        this.state.start(\"Loading\");\r\n    }\t\r\n};\r\n\r\nTheGame.Loading = function (game) {\r\n};\r\n\r\nTheGame.Loading.prototype = {\r\n    init: function () {\r\n    },\r\n    preload: function () {\r\n        this.stage.backgroundColor = 0x222222;\r\n        var loadingBar = this.add.sprite(this.world.centerX, this.world.centerY, \"loading\");\r\n        loadingBar.anchor.setTo(0.5);\r\n        this.load.setPreloadSprite(loadingBar);\r\n\r\n        this.load.spritesheet(\"settings\", \"settings.png\", 364, 364);\r\n\t\tthis.load.image(\"background\", \"background.png\");\r\n\t    this.load.image(\"current\", \"current.png\");\r\n        this.load.image(\"best\", \"best.png\");\r\n        this.load.image(\"hand\", \"hand.png\");\r\n        this.load.spritesheet(\"tiles\", \"tiles.png\", 120, 120);\r\n\t\r\n    },\r\n    create: function () {\r\n       this.state.start(\"TheGame\");\r\n    }\r\n};\r\n\r\nTheGame.MyGame = function (game) {\r\n};\r\n\r\nTheGame.MyGame.prototype = {\r\n    preload: function () {\r\n    },\r\n    create: function () {\r\n\t\tthis.background = this.add.image(0, 0, \"background\");\r\n\t\tthis.background.height = this.game.height;\r\n\t\tthis.background.width = this.game.width;\r\n\r\n        \/\/ First add all sprites to the game and then position them after scaling\r\n\t\tthis.scoreTile = this.game.add.image(-200, -200, \"current\");\r\n\t\tthis.scoreTile.anchor.setTo(0.5);\r\n\t\tthis.bestScoreTile = this.game.add.image(-200, -200 , \"best\");\r\n\t\tthis.bestScoreTile.anchor.setTo(0.5);\r\n\t\tthis.soundButton = this.game.add.button(-200, -200, \"settings\", this.toggleSound, this);\r\n\t\tthis.soundButton.anchor.setTo(0.5);\r\n\t\tthis.soundButton.frame = 2;\r\n\t\tthis.soundButton.clicked = false;\r\n\t\tthis.newButton = this.game.add.button(-200 , -200, \"settings\", this.toggleSound, this);\r\n\t\tthis.newButton.anchor.setTo(0.5);\r\n\t\tthis.newButton.frame = 5;\r\n\t\tthis.newButton.clicked = false;\r\n\t\tthis.helpButton = this.game.add.button(-200 , -200, \"settings\", this.toggleSound, this);\r\n\t\tthis.helpButton.anchor.setTo(0.5);\r\n\t\tthis.helpButton.frame = 4;\r\n\t\tthis.helpButton.clicked = false;\r\n\t\t\r\n        \/\/ Add all grid tiles to the game\r\n        this.initTiles();\r\n                \r\n        \/\/ Position the controls using available width and height in the game\r\n\t\tthis.positionControls(this.game.width, this.game.height);\r\n\t\t\r\n\t\tthis.showHint();\r\n    },\r\n\tscaleSprite: function (sprite, availableSpaceWidth, availableSpaceHeight, padding, scaleMultiplier, isFullScale) {\r\n\t\tvar scale = this.getSpriteScale(sprite._frame.width, sprite._frame.height, availableSpaceWidth, availableSpaceHeight, padding, isFullScale);\r\n\t\tsprite.scale.x = scale * scaleMultiplier;\r\n\t\tsprite.scale.y = scale * scaleMultiplier;\r\n\t},\r\n\tgetSpriteScale: function (spriteWidth, spriteHeight, availableSpaceWidth, availableSpaceHeight, minPadding, isFullScale) {\r\n\t\tvar ratio = 1;\r\n\t\tvar currentDevicePixelRatio = window.devicePixelRatio;\r\n\t\t\/\/ Sprite needs to fit in either width or height\r\n\t\tvar widthRatio = (spriteWidth * currentDevicePixelRatio + 2 * minPadding) \/ availableSpaceWidth;\r\n\t\tvar heightRatio = (spriteHeight * currentDevicePixelRatio + 2 * minPadding) \/ availableSpaceHeight;\r\n\t\tif(widthRatio &gt; 1 || heightRatio &gt; 1){\r\n\t\t\tratio = 1 \/ Math.max(widthRatio, heightRatio);\r\n\t\t} else {\r\n\t\t\tif(isFullScale)\r\n\t\t\t\tratio = 1 \/ Math.min(widthRatio, heightRatio);\r\n\t\t}\r\n\t\treturn ratio * currentDevicePixelRatio;\t\r\n\t},\r\n\tresize: function (width, height) {\r\n\t\tthis.background.height = height;\r\n\t\tthis.background.width = width;\r\n\t\tthis.positionControls(width, height);\r\n\t},\r\n\tpositionControls: function (width, height) {\r\n\t\t\/\/ We would consider landscape orientation if height to width ratio is less than 1.3.\r\n\t\t\/\/ Pick any value you like or a preference for landscape or portrait orientation\r\n        var isLandscape = height \/ width  &lt; 1.3 ? true: false;\r\n\t\tif(isLandscape){\r\n\t\t\tvar availableGridSpace = Math.min(width * 2 \/ 3, height);\r\n\t\t\tthis.calculatedTileSize = (availableGridSpace * 0.9) \/ 6;\r\n\t\t\tthis.verticalMargin = (height - 6 * this.calculatedTileSize) \/ 2;\r\n\t\t\tthis.horizontalMargin = (width * 2 \/ 3 - 6 * this.calculatedTileSize) \/ 2;\r\n\t\t\t\r\n\t\t\tthis.tileGroup.x = this.horizontalMargin;\r\n\t\t\tthis.tileGroup.y = this.verticalMargin;\r\n\r\n\t\t\tthis.scaleSprite(this.scoreTile, width \/ 3, height \/ 3, 50, 1);\r\n\t\t\tthis.scoreTile.x = width * 2 \/ 3 + width \/ 6;\r\n\t\t\tthis.scoreTile.y = this.verticalMargin + this.scoreTile.height \/ 2;\r\n\r\n\t\t\tthis.scaleSprite(this.bestScoreTile, width \/ 3, height \/ 3, 50, 1);\t\t\t\r\n\t\t\tthis.bestScoreTile.x = width * 2 \/ 3 + width \/ 6;\r\n\t\t\tthis.bestScoreTile.y = this.verticalMargin + this.scoreTile.height + 50;\r\n\r\n\t\t\tvar calculatedSettingsVerticalSpace = height - 2 * this.verticalMargin - 2 * 50 - this.scoreTile.height - this.bestScoreTile.height;\r\n\r\n\t\t\tthis.scaleSprite(this.soundButton, width \/ 3, calculatedSettingsVerticalSpace \/ 3, 20, 1);\r\n\t\t\tthis.soundButton.x = width * 2 \/ 3 + width \/ 6;\r\n\t\t\tthis.soundButton.y = height - this.verticalMargin - this.soundButton.height \/ 2;\r\n\r\n\t\t\tthis.scaleSprite(this.newButton, width \/ 3, calculatedSettingsVerticalSpace \/ 3, 20, 1);\r\n\t\t\tthis.newButton.x = width * 2 \/ 3 + width \/ 6;\r\n\t\t\tthis.newButton.y = height - this.verticalMargin - this.soundButton.height - 50;\r\n\t\t\r\n\t\t\tthis.scaleSprite(this.helpButton, width \/ 3, calculatedSettingsVerticalSpace \/ 3, 20, 1);\r\n\t\t\tthis.helpButton.x = width * 2 \/ 3 + width \/ 6;\r\n\t\t\tthis.helpButton.y = height - this.verticalMargin - this.soundButton.height - this.newButton.height\/ 2 - 100;\r\n\r\n\t\t\tfor (var i = 0; i &lt; TheGame.Params.fieldSize.rows; i++) {\r\n\t\t\t\tfor (var j = 0; j &lt; TheGame.Params.fieldSize.cols; j++) {\r\n\t\t\t\t\tthis.scaleSprite(this.tilesArray[i][j], this.calculatedTileSize, this.calculatedTileSize, 0, 1, true);\r\n\t\t\t\t\tvar tileX = j * this.calculatedTileSize + this.calculatedTileSize \/ 2;\r\n\t\t\t\t\tvar tileY = i * this.calculatedTileSize + this.calculatedTileSize \/ 2;\r\n\t\t\t\t\tthis.tilesArray[i][j].x = tileX;\r\n\t\t\t\t\tthis.tilesArray[i][j].y = tileY;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t} else {\r\n\t\t\t\r\n\t\t\tvar availableGridSpace = this.game.width;\r\n\t\t\tthis.calculatedTileSize = (availableGridSpace * 0.9) \/ 6;\r\n\t\t\tthis.verticalMargin = (this.game.height - 6 * this.calculatedTileSize) \/ 2;\r\n\t\t\tthis.horizontalMargin = (this.game.width - 6 * this.calculatedTileSize) \/ 2;\r\n\t\t\t\r\n\t\t\tthis.tileGroup.x = this.horizontalMargin;\r\n\t\t\tthis.tileGroup.y = this.verticalMargin;\r\n\t\r\n\t\t\tthis.scaleSprite(this.scoreTile, width \/ 2, this.verticalMargin, 20, 1);\r\n\t\t\tthis.scoreTile.x = width \/ 4;\r\n\t\t\tthis.scoreTile.y = this.verticalMargin \/ 2;\r\n\r\n\t\t\tthis.scaleSprite(this.bestScoreTile, width \/ 2, this.verticalMargin, 20, 1);\t\t\t\r\n\t\t\tthis.bestScoreTile.x = width * 3 \/ 4;\r\n\t\t\tthis.bestScoreTile.y = this.verticalMargin \/ 2;\r\n\r\n\t\t\tthis.scaleSprite(this.soundButton, width \/ 3, this.verticalMargin, 50, 0.75);\r\n\t\t\tthis.soundButton.x = this.world.centerX;\r\n\t\t\tthis.soundButton.y = height - this.verticalMargin \/ 2;\r\n\r\n\t\t\tthis.scaleSprite(this.newButton, width \/ 3, this.verticalMargin, 50, 0.75);\r\n\t\t\tthis.newButton.x = this.world.centerX - this.soundButton.width;\r\n\t\t\tthis.newButton.y = height - this.verticalMargin \/ 2;\r\n\t\t\t\r\n\t\t\tthis.scaleSprite(this.helpButton, width \/ 3, this.verticalMargin, 50, 0.75);\r\n\t\t\tthis.helpButton.x = this.world.centerX + this.soundButton.width;\r\n\t\t\tthis.helpButton.y = height - this.verticalMargin \/ 2;\r\n\r\n\t\t\tfor (var i = 0; i &lt; TheGame.Params.fieldSize.rows; i++) {\r\n\t\t\t\tfor (var j = 0; j &lt; TheGame.Params.fieldSize.cols; j++) {\r\n\t\t\t\t\tthis.scaleSprite(this.tilesArray[i][j], this.calculatedTileSize, this.calculatedTileSize, 0, 1, true);\r\n\t\t\t\t\tvar tileX = j * this.calculatedTileSize + this.calculatedTileSize \/ 2;\r\n\t\t\t\t\tvar tileY = i * this.calculatedTileSize + this.calculatedTileSize \/ 2;\r\n\t\t\t\t\tthis.tilesArray[i][j].x = tileX;\r\n\t\t\t\t\tthis.tilesArray[i][j].y = tileY;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\t\r\n\t\tif(this.hintTile &amp;&amp; this.hand){\r\n\t\t\tthis.hand.destroy();\r\n\t\t\tthis.showHint();\r\n\t\t}\t\t\r\n\t},\r\n    initTiles: function () {\r\n\r\n\t\tthis.tilesArray = [];\r\n        this.tileGroup = this.game.add.group();\r\n\t\tthis.tileGroup.x = this.horizontalMargin;\r\n\t\tthis.tileGroup.y = this.verticalMargin;\r\n\r\n        for (var i = 0; i &lt; TheGame.Params.fieldSize.rows; i++) {\r\n            this.tilesArray[i] = [];\r\n            for (var j = 0; j &lt; TheGame.Params.fieldSize.cols; j++) {\r\n\r\n\t\t\t\tvar tileX = j * this.calculatedTileSize + this.calculatedTileSize \/ 2;\r\n\t\t\t\tvar tileY = i * this.calculatedTileSize + this.calculatedTileSize \/ 2;\r\n\t\t\t\tvar tile = this.game.add.sprite(tileX, tileY, \"tiles\");\r\n\t\t\t\ttile.anchor.set(0.5);\r\n\t\t\t\ttile.value = 0;\r\n\t\t\t\ttile.point = new Phaser.Point(j, i);\r\n\t\t\t\tthis.scaleSprite(tile, this.calculatedTileSize, this.calculatedTileSize, 0, 1);\r\n\t\t\t\tthis.tilesArray[i][j] = tile;\r\n\t\t\t\tthis.tileGroup.add(tile);\r\n\t\t\t\r\n            }\r\n        }\r\n\t\tthis.addNumericTiles();\r\n    },\r\n\taddNumericTiles: function () {\r\n\t\tvar blankTiles = [];\r\n        for (var i = 0; i &lt; TheGame.Params.fieldSize.rows; i++) {\r\n            for (var j = 0; j &lt; TheGame.Params.fieldSize.cols; j++) {\r\n                if (this.tilesArray[i][j].value === 0) {\r\n                    blankTiles.push(this.tilesArray[i][j]);\r\n                }\r\n            }\r\n        }\r\n\r\n\t    for (i = 0; i &lt; 2; i++) {\r\n            var tile = Phaser.ArrayUtils.removeRandomItem(blankTiles);\r\n            if (tile) {\r\n                    var val = this.game.rnd.integerInRange(2, 9);\r\n                    this.tilesArray[tile.point.y][tile.point.x].value = val;\r\n                    this.tilesArray[tile.point.y][tile.point.x].frame = val;\r\n\t\t\t\t\tthis.hintTile = this.tilesArray[tile.point.y][tile.point.x]; \/\/ Last generated tile to be used for hint\r\n                    var tile = this.tilesArray[tile.point.y][tile.point.x];\r\n            }\r\n        }\t\r\n\t},\r\n\tshowHint: function () {\r\n\t\tif(this.hintTile) {\r\n\t\t\tvar hintTileX = this.hintTile.point.x * this.calculatedTileSize + this.calculatedTileSize \/ 2;\r\n\t\t\tvar hintTileY = this.hintTile.point.y * this.calculatedTileSize + this.calculatedTileSize \/ 2;\r\n\r\n\t\t\tvar nextTile = null;\r\n\t\t\t\r\n\t\t\tif(this.tilesArray[this.hintTile.point.y][this.hintTile.point.x + 1] &amp;&amp; this.tilesArray[this.hintTile.point.y][this.hintTile.point.x + 1].value === 0)\r\n\t\t\t\tnextTile = this.tilesArray[this.hintTile.point.y][this.hintTile.point.x + 1];\r\n\t\t\telse if(this.tilesArray[this.hintTile.point.y][this.hintTile.point.x - 1] &amp;&amp; this.tilesArray[this.hintTile.point.y][this.hintTile.point.x - 1].value === 0)\r\n\t\t\t\tnextTile = this.tilesArray[this.hintTile.point.y][this.hintTile.point.x - 1];\r\n\t\t\telse if(this.tilesArray[this.hintTile.point.y + 1][this.hintTile.point.x] &amp;&amp; this.tilesArray[this.hintTile.point.y + 1][this.hintTile.point.x].value === 0)\r\n\t\t\t\tnextTile = this.tilesArray[this.hintTile.point.y + 1][this.hintTile.point.x];\r\n\t\t\telse if(this.tilesArray[this.hintTile.point.y - 1][this.hintTile.point.x] &amp;&amp; this.tilesArray[this.hintTile.point.y - 1][this.hintTile.point.x].value === 0)\r\n\t\t\t\tnextTile = this.tilesArray[this.hintTile.point.y - 1][this.hintTile.point.x];\r\n\t\t\t \r\n\t\t\t\r\n\t\t\tif(nextTile){\r\n\t\t\t\tthis.hand = this.game.add.image(hintTileX, hintTileY , \"hand\");\r\n\t\t\t\tthis.tileGroup.add(this.hand);\r\n\t\t\t\tthis.hand.anchor.y = 0;\r\n\t\t\t\tthis.hand.anchor.x = 0.5\r\n\t\t\t\tthis.scaleSprite(this.hand, this.calculatedTileSize, this.calculatedTileSize, 0, 1, true);\r\n\r\n\t\t\t\tvar nextTileX = nextTile.point.x * this.calculatedTileSize + this.calculatedTileSize \/ 2;\r\n\t\t\t\tvar nextTileY = nextTile.point.y * this.calculatedTileSize + this.calculatedTileSize \/ 2;\r\n\t\t\t\tthis.game.add.tween(this.hand).to({ x:nextTileX, y: nextTileY }, 2000, Phaser.Easing.Quadratic.InOut, true, 0, 1000, true);\r\n\t\t\t}\r\n\t\t\t\t\r\n\t\t}\r\n\t},\t\r\n    toggleSound: function (button) {\r\n    },\r\n    restart: function (button) {\r\n    },\r\n    help: function (button) {\r\n    }\r\n};\r\n\r\nvar mygame;\r\nwindow.onload = function () {\r\n\tmygame = new Phaser.Game(TheGame.Params.baseWidth, TheGame.Params.baseHeight, Phaser.AUTO);\t\r\n\tmygame.state.add(\"Boot\", TheGame.Boot);\r\n\tmygame.state.add(\"Loading\", TheGame.Loading);\r\n\tmygame.state.add(\"TheGame\", TheGame.MyGame);\r\n\tmygame.state.start(\"Boot\");\r\n}\r\n<\/pre>\n<p>We added two random numbers in the grid and player is supposed to drag the number in the grid. The code for displaying hint is highlighted in <em>showHint<\/em> method.\u00a0In this method we find the start position and end position for the hint and then use a tween to move our hint icon between the two positions.<\/p>\n<pre class=\"lang:default decode:true \">this.game.add.tween(this.hand).to({ x:nextTileX, y: nextTileY }, 2000, Phaser.Easing.Quadratic.InOut, true, 0, 1000, true);\r\n<\/pre>\n<p>Please note another\u00a0important change in this code from the previous article to accommodate responsive behavior for the hint icon. Every time resize method is called and we are re-aligning elements in the game, we remove the hint icon from the game and re-add it by calling <em>showHint<\/em> method which calculates the correct position and scale for the hint icon and adds a new tween for displaying the hint.<\/p>\n<pre class=\"lang:default decode:true\">if(this.hintTile &amp;&amp; this.hand){\r\n\tthis.hand.destroy();\r\n\tthis.showHint();\r\n}\t\t\r\n<\/pre>\n<p>See below for the hint element at\u00a0work<\/p>\n\n<!-- iframe plugin v.5.2 wordpress.org\/plugins\/iframe\/ -->\n<iframe loading=\"lazy\" src=\"https:\/\/www.netexl.com\/blog\/demo\/4\/index.html\" width=\"100%\" height=\"500\" scrolling=\"yes\" class=\"iframe-class\" frameborder=\"0\"><\/iframe>\n\n<p>Click <a href=\"https:\/\/www.netexl.com\/blog\/demo\/4\/index.html\" target=\"_blank\">here<\/a> if you want\u00a0to check this on your device.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is next task I planned to implement in Number Spread game I discussed in the previous\u00a0article series. In a game we have many scenarios when we want to display a hint and I considered many options as to how this can be implemented in Phaser. The\u00a0options could be either using a tween or animation. For Number Spread it was a simple scenario when I just needed to display a hand which moved in certain\u00a0direction to give a hint to player what is to be done. A tween for this purpose seemed fine. Look at the complete code for it below. var TheGame = { }; TheGame.Params = { baseWidth: 1920, baseHeight: 1080, minPadding: 50, iconSize: 364, fieldSize: { rows: 6, cols: 6 } }; TheGame.Boot = function (game) { }; TheGame.Boot.prototype = { init: function () { this.scale.scaleMode = Phaser.ScaleManager.RESIZE; }, preload: function () { this.load.image(&#8220;loading&#8221;, &#8220;loadingback.png&#8221;); }, create:[&#8230;]<\/p>\n","protected":false},"author":5,"featured_media":85,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,3,4],"tags":[],"class_list":["post-127","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-framework","category-html5","category-phaser"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/posts\/127","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=127"}],"version-history":[{"count":4,"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/posts\/127\/revisions"}],"predecessor-version":[{"id":148,"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/posts\/127\/revisions\/148"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/media\/85"}],"wp:attachment":[{"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/media?parent=127"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/categories?post=127"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/tags?post=127"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}