{"id":1283,"date":"2022-02-22T15:51:34","date_gmt":"2022-02-22T15:51:34","guid":{"rendered":"http:\/\/www.netexl.com\/blog\/?p=1283"},"modified":"2026-04-02T09:20:35","modified_gmt":"2026-04-02T09:20:35","slug":"add-borders-in-puzzle-games-in-phaser-3","status":"publish","type":"post","link":"https:\/\/www.netexl.com\/blog\/add-borders-in-puzzle-games-in-phaser-3\/","title":{"rendered":"Add borders in Puzzle games in Phaser 3"},"content":{"rendered":"\n<p>In puzzle games where we need to add borders to our puzzles and mark various zones, we can use Phase Path to draw those lines. For this example we are going to draw something like the image below<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"520\" height=\"513\" src=\"https:\/\/www.netexl.com\/blog\/wp-content\/uploads\/2022\/02\/image-12.png\" alt=\"\" class=\"wp-image-1284\" srcset=\"https:\/\/www.netexl.com\/blog\/wp-content\/uploads\/2022\/02\/image-12.png 520w, https:\/\/www.netexl.com\/blog\/wp-content\/uploads\/2022\/02\/image-12-300x296.png 300w, https:\/\/www.netexl.com\/blog\/wp-content\/uploads\/2022\/02\/image-12-280x276.png 280w\" sizes=\"auto, (max-width: 520px) 100vw, 520px\" \/><\/figure>\n\n\n\n<p>Let us start by creating our html file and including Phaser 3 js and our game js in it.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;!DOCTYPE html>\r\n&lt;html>\r\n&lt;head>\r\n    &lt;meta charset=\"utf-8\" \/>\r\n    &lt;title>&lt;\/title>\r\n    &lt;meta http-equiv=\"Content-type\" content=\"text\/html; charset=utf-8\">\r\n    &lt;meta name=\"viewport\" content=\"width=device-width, minimum-scale=1, initial-scale=1, user-scalable=no\">\r\n    &lt;script src=\"js\/phaser.min.js\">&lt;\/script>\r\n    &lt;script src=\"js\/game.js\">&lt;\/script>\r\n&lt;\/head>\r\n&lt;body>\r\n\t&lt;div id=\"mygame\">&lt;\/div>\r\n&lt;\/body>\r\n&lt;\/html>\r<\/code><\/pre>\n\n\n\n<p>Now our JS code for would be <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const BLANK= 0;\r\n\r\nlet launchGame = function () {\r\n    let config = {\r\n        type: Phaser.AUTO,\r\n        scale: {\r\n            parent: 'mygame',\r\n            mode: Phaser.Scale.FIT,\r\n            autoCenter: Phaser.Scale.CENTER_BOTH,\r\n            width: window.innerWidth * window.devicePixelRatio,\r\n            height: window.innerHeight * window.devicePixelRatio\r\n        },\r\n        autoResize: true,\r\n        scene: &#91;TheGame]\r\n    }\r\n\r\n    let game = new Phaser.Game(config);\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.image(\"blank\", \"images\/blank.png\");\r\n    }\r\n\r\n    init(config) {\r\n    }\r\n\r\n    create() {\r\n        this.cameras.main.setBackgroundColor('#004050');\r\n\r\n\t\tthis.size = 9;\r\n\t\tthis.board = &#91;\r\n\t\t\t&#91;1, 1, 1, 1, 1, 1, 2, 2, 2],\r\n\t\t\t&#91;1, 3, 1, 3, 3, 3, 4, 2, 2],\r\n\t\t\t&#91;1, 3, 3, 3, 3, 5, 4, 4, 2],\r\n\t\t\t&#91;6, 6, 6, 3, 5, 5, 4, 2, 2],\r\n\t\t\t&#91;7, 6, 6, 5, 5, 5, 4, 4, 2],\r\n\t\t\t&#91;7, 7, 6, 5, 5, 8, 4, 4, 4],\r\n\t\t\t&#91;7, 6, 6, 5, 8, 8, 8, 8, 9],\r\n\t\t\t&#91;7, 7, 6, 8, 8, 8, 9, 8, 9],\r\n\t\t\t&#91;7, 7, 7, 9, 9, 9, 9, 9, 9]\r\n\t\t];\r\n\t\t\r\n        this.gameArray = &#91;];\r\n        for (let i = 0; i &lt; this.size; i++) {\r\n            this.gameArray&#91;i] = &#91;];\r\n            for (let j = 0; j &lt; this.size; j++) {\r\n                let v = this.board&#91;i]&#91;j] ? this.board&#91;i]&#91;j] : BLANK;\r\n                let tile = this.add.image(-900, -900, \"blank\").setOrigin(0);\r\n                this.gameArray&#91;i]&#91;j] = {\r\n                    sprite: tile\r\n                };\r\n            }\r\n        }\r\n\r\n        this.lines = this.add.graphics();\r\n        this.graphics = this.add.graphics();\r\n\r\n        this.scale.on('resize', this.resize, this);\r\n        this.positionControls(this.cameras.main.width, this.cameras.main.height);\r\n    }\r\n\r\n    getPosition(i, j) {\r\n        return { x: this.boardLeft + j * this.tileSize, y: this.boardTop + i * this.tileSize };\r\n    }\r\n\r\n    positionControls(width, height) {\r\n\r\n        let availableWidth = width, tileSize, scale, availableButtonSize;\r\n        tileSize = Math.min(availableWidth \/ (this.size + 1), height \/ (this.size + 3));\r\n        let tile = this.gameArray&#91;0]&#91;0].sprite;\r\n        scale = scaleManager.scaleSprite(tile, tileSize, tileSize, 0, 1, true);\r\n\r\n        this.boardLeft = (width - tileSize * this.size) \/ 2;\r\n        this.boardTop = height * 0.5 - this.size * tileSize * 0.5;\r\n        this.tileSize = tileSize;\r\n\r\n        for (let x = 0; x &lt; this.size; x++) {\r\n            for (let y = 0; y &lt; this.size; y++) {\r\n                let pos = this.getPosition(x, y);\r\n                scaleManager.scaleSpriteTo(this.gameArray&#91;x]&#91;y].sprite, scale);\r\n                this.gameArray&#91;x]&#91;y].sprite.setPosition(pos.x, pos.y);\r\n            }\r\n        }\r\n\r\n        this.lines.clear();\r\n        this.lines.lineStyle(2, 0xC5C6C6, 1);\r\n\r\n        this.graphics.clear();\r\n        this.graphics.lineStyle(this.size > 10 ? 4 : 5, 0xE10C30, 1);\r\n\r\n        let path, lines;\r\n        for (let x = 0; x &lt; this.size; x++) {\r\n            for (let y = 0; y &lt; this.size; y++) {\r\n\r\n                let directions = { 'up': &#91;-1, 0], 'down': &#91;1, 0], 'left': &#91;0, -1], 'right': &#91;0, 1] }; \/\/ left, right, bottom, top\r\n\r\n                for (let key in directions) {\r\n                    if (directions.hasOwnProperty(key)) {\r\n                        let d = directions&#91;key];\r\n                        let dx = d&#91;0];\r\n                        let dy = d&#91;1];\r\n                        if (x + dx >= 0 &amp;&amp; x + dx &lt; this.size) {\r\n                            if (y + dy >= 0 &amp;&amp; y + dy &lt; this.size) {\r\n                                if (this.board&#91;x + dx]&#91;y + dy] !== this.board&#91;x]&#91;y]) {\r\n                                    switch (key) {\r\n                                        case 'left':\r\n                                            path = new Phaser.Curves.Path(this.gameArray&#91;x]&#91;y].sprite.x, this.gameArray&#91;x]&#91;y].sprite.y);\r\n                                            path.lineTo(this.gameArray&#91;x]&#91;y].sprite.x, this.gameArray&#91;x]&#91;y].sprite.y + tileSize);\r\n                                            break;\r\n                                        case 'right':\r\n                                            path = new Phaser.Curves.Path(this.gameArray&#91;x + dx]&#91;y + dy].sprite.x, this.gameArray&#91;x + dx]&#91;y + dy].sprite.y);\r\n                                            path.lineTo(this.gameArray&#91;x + dx]&#91;y + dy].sprite.x, this.gameArray&#91;x + dx]&#91;y + dy].sprite.y + tileSize);\r\n                                            break;\r\n                                        case 'down':\r\n                                            path = new Phaser.Curves.Path(this.gameArray&#91;x + dx]&#91;y + dy].sprite.x, this.gameArray&#91;x + dx]&#91;y + dy].sprite.y);\r\n                                            path.lineTo(this.gameArray&#91;x + dx]&#91;y + dy].sprite.x + tileSize, this.gameArray&#91;x + dx]&#91;y + dy].sprite.y);\r\n                                            break;\r\n                                        case 'up':\r\n                                            path = new Phaser.Curves.Path(this.gameArray&#91;x]&#91;y].sprite.x, this.gameArray&#91;x]&#91;y].sprite.y);\r\n                                            path.lineTo(this.gameArray&#91;x]&#91;y].sprite.x + tileSize, this.gameArray&#91;x]&#91;y].sprite.y);\r\n                                            break;\r\n                                    }\r\n                                    path.draw(this.graphics);\r\n                                } else {\r\n                                    switch (key) {\r\n                                        case 'left':\r\n                                            lines = new Phaser.Curves.Path(this.gameArray&#91;x]&#91;y].sprite.x, this.gameArray&#91;x]&#91;y].sprite.y);\r\n                                            lines.lineTo(this.gameArray&#91;x]&#91;y].sprite.x, this.gameArray&#91;x]&#91;y].sprite.y + tileSize);\r\n                                            break;\r\n                                        case 'right':\r\n                                            lines = new Phaser.Curves.Path(this.gameArray&#91;x + dx]&#91;y + dy].sprite.x, this.gameArray&#91;x + dx]&#91;y + dy].sprite.y);\r\n                                            lines.lineTo(this.gameArray&#91;x + dx]&#91;y + dy].sprite.x, this.gameArray&#91;x + dx]&#91;y + dy].sprite.y + tileSize);\r\n                                            break;\r\n                                        case 'down':\r\n                                            lines = new Phaser.Curves.Path(this.gameArray&#91;x + dx]&#91;y + dy].sprite.x, this.gameArray&#91;x + dx]&#91;y + dy].sprite.y);\r\n                                            lines.lineTo(this.gameArray&#91;x + dx]&#91;y + dy].sprite.x + tileSize, this.gameArray&#91;x + dx]&#91;y + dy].sprite.y);\r\n                                            break;\r\n                                        case 'up':\r\n                                            lines = new Phaser.Curves.Path(this.gameArray&#91;x]&#91;y].sprite.x, this.gameArray&#91;x]&#91;y].sprite.y);\r\n                                            lines.lineTo(this.gameArray&#91;x]&#91;y].sprite.x + tileSize, this.gameArray&#91;x]&#91;y].sprite.y);\r\n                                            break;\r\n                                    }\r\n                                    lines.draw(this.lines);\r\n                                }\r\n                            }\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n        }\r\n\r\n        \/\/ top\r\n        path = new Phaser.Curves.Path(this.gameArray&#91;0]&#91;0].sprite.x, this.gameArray&#91;0]&#91;0].sprite.y);\r\n        path.lineTo(this.gameArray&#91;0]&#91;0].sprite.x + this.size * tileSize, this.gameArray&#91;0]&#91;0].sprite.y);\r\n        path.draw(this.graphics);\r\n\r\n        \/\/ bottom\r\n        path = new Phaser.Curves.Path(this.gameArray&#91;0]&#91;0].sprite.x, this.gameArray&#91;0]&#91;0].sprite.y + this.size * tileSize);\r\n        path.lineTo(this.gameArray&#91;0]&#91;0].sprite.x + this.size * tileSize, this.gameArray&#91;this.size - 1]&#91;0].sprite.y + tileSize);\r\n        path.draw(this.graphics);\r\n\r\n        \/\/ left\r\n        path = new Phaser.Curves.Path(this.gameArray&#91;0]&#91;0].sprite.x, this.gameArray&#91;0]&#91;0].sprite.y);\r\n        path.lineTo(this.gameArray&#91;0]&#91;0].sprite.x, this.gameArray&#91;this.size - 1]&#91;0].sprite.y + tileSize);\r\n        path.draw(this.graphics);\r\n\r\n        \/\/ right\r\n        path = new Phaser.Curves.Path(this.gameArray&#91;0]&#91;0].sprite.x + this.size * tileSize, this.gameArray&#91;0]&#91;0].sprite.y);\r\n        path.lineTo(this.gameArray&#91;0]&#91;0].sprite.x + this.size * tileSize, this.gameArray&#91;this.size - 1]&#91;0].sprite.y + tileSize);\r\n        path.draw(this.graphics);\r\n    }\r\n\r\n    resize(gameSize, baseSize, displaySize, resolution) {\r\n        let width = gameSize.width;\r\n        let height = gameSize.height;\r\n\r\n        this.cameras.resize(width, height);\r\n        this.positionControls(width, height);\r\n    }\r\n}\r\n\r\n\r\nfunction LocalScaleManager() {\r\n}\r\n\r\nLocalScaleManager.prototype = {\r\n    scaleSprite: function (sprite, availableSpaceWidth, availableSpaceHeight, padding, scaleMultiplier, isFullScale) {\r\n        let scale = this.getSpriteScale(sprite.frame.width, sprite.frame.height, availableSpaceWidth, availableSpaceHeight, padding, isFullScale);\r\n        sprite.setScale(scale * scaleMultiplier);\r\n        return scale;\r\n    },\r\n    scaleSpriteTo: function (sprite, scale) {\r\n        sprite.setScale(scale);\r\n    },\r\n    getSpriteScale: function (spriteWidth, spriteHeight, availableSpaceWidth, availableSpaceHeight, minPadding, isFullScale) {\r\n        let ratio = 1;\r\n        let currentDevicePixelRatio = window.devicePixelRatio;\r\n        \/\/ Sprite needs to fit in either width or height\r\n        let widthRatio = (spriteWidth * currentDevicePixelRatio + 2 * minPadding) \/ availableSpaceWidth;\r\n        let heightRatio = (spriteHeight * currentDevicePixelRatio + 2 * minPadding) \/ availableSpaceHeight;\r\n        if (widthRatio > 1 || heightRatio > 1) {\r\n            ratio = 1 \/ Math.max(widthRatio, heightRatio);\r\n        } else {\r\n            if (isFullScale)\r\n                ratio = 1 \/ Math.max(widthRatio, heightRatio);\r\n        }\r\n        return ratio * currentDevicePixelRatio;\r\n    }\r\n};\r\n\r\nlet scaleManager = new LocalScaleManager;\r\n\r\nlaunchGame();<\/code><\/pre>\n\n\n\n<p>Try running this locally in your web server and you will see the result as shown in the screenshot.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In puzzle games where we need to add borders to our puzzles and mark various zones, we can use Phase Path to draw those lines. For this example we are going to draw something like the image below Let us start by creating our html file and including Phaser 3 js and our game js in it. Now our JS code for would be Try running this locally in your web server and you will see the result as shown in the screenshot.<\/p>\n","protected":false},"author":5,"featured_media":1539,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[33,3,24,4,22],"tags":[],"class_list":["post-1283","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-code","category-html5","category-javascript","category-phaser","category-quick-tip"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/posts\/1283","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=1283"}],"version-history":[{"count":2,"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/posts\/1283\/revisions"}],"predecessor-version":[{"id":1286,"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/posts\/1283\/revisions\/1286"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/media\/1539"}],"wp:attachment":[{"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/media?parent=1283"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/categories?post=1283"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/tags?post=1283"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}