├── README.md ├── assets ├── images │ ├── spike.png │ ├── background.png │ ├── kenney_player.png │ └── kenney_player_atlas.json ├── tilesets │ ├── platformPack_tilesheet.png │ └── kenney_simple_platformer.tsx └── tilemaps │ ├── level1.tmx │ └── level1.json ├── index.html ├── LICENSE └── game.js /README.md: -------------------------------------------------------------------------------- 1 | # creating-platformers-in-phaser-with-tiled 2 | Learn how to use Tiled to and Platformer games with Phaser 3 | -------------------------------------------------------------------------------- /assets/images/spike.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StackAbuse/creating-a-platformer-with-phaser-3/HEAD/assets/images/spike.png -------------------------------------------------------------------------------- /assets/images/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StackAbuse/creating-a-platformer-with-phaser-3/HEAD/assets/images/background.png -------------------------------------------------------------------------------- /assets/images/kenney_player.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StackAbuse/creating-a-platformer-with-phaser-3/HEAD/assets/images/kenney_player.png -------------------------------------------------------------------------------- /assets/tilesets/platformPack_tilesheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StackAbuse/creating-a-platformer-with-phaser-3/HEAD/assets/tilesets/platformPack_tilesheet.png -------------------------------------------------------------------------------- /assets/tilesets/kenney_simple_platformer.tsx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Platformer 8 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /assets/tilemaps/level1.tmx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0, 9 | 0,0,0,0,0,0,47,47,47,0,0,0,0,0, 10 | 0,0,0,0,0,47,0,0,0,0,0,0,0,0, 11 | 0,0,0,0,47,0,0,0,0,0,0,0,0,0, 12 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1, 13 | 4,4,4,4,4,4,4,4,4,4,4,4,4,4, 14 | 4,4,4,4,4,4,4,4,4,4,4,4,4,4 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Marcus Sanatan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /assets/tilemaps/level1.json: -------------------------------------------------------------------------------- 1 | { "height":7, 2 | "infinite":false, 3 | "layers":[ 4 | { 5 | "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 47, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4], 6 | "height":7, 7 | "id":6, 8 | "name":"Platforms", 9 | "opacity":1, 10 | "type":"tilelayer", 11 | "visible":true, 12 | "width":14, 13 | "x":0, 14 | "y":0 15 | }, 16 | { 17 | "draworder":"topdown", 18 | "id":3, 19 | "name":"Spikes", 20 | "objects":[ 21 | { 22 | "gid":71, 23 | "height":64, 24 | "id":1, 25 | "name":"", 26 | "rotation":0, 27 | "type":"", 28 | "visible":true, 29 | "width":64, 30 | "x":192, 31 | "y":256 32 | }, 33 | { 34 | "gid":71, 35 | "height":64, 36 | "id":2, 37 | "name":"", 38 | "rotation":0, 39 | "type":"", 40 | "visible":true, 41 | "width":64, 42 | "x":448, 43 | "y":64 44 | }, 45 | { 46 | "gid":71, 47 | "height":64, 48 | "id":3, 49 | "name":"", 50 | "rotation":0, 51 | "type":"", 52 | "visible":true, 53 | "width":64, 54 | "x":672, 55 | "y":256 56 | }], 57 | "opacity":1, 58 | "type":"objectgroup", 59 | "visible":true, 60 | "x":0, 61 | "y":0 62 | }], 63 | "nextlayerid":7, 64 | "nextobjectid":5, 65 | "orientation":"orthogonal", 66 | "renderorder":"right-down", 67 | "tiledversion":"1.2.4", 68 | "tileheight":64, 69 | "tilesets":[ 70 | { 71 | "columns":14, 72 | "firstgid":1, 73 | "image":"..\/tilesets\/platformPack_tilesheet.png", 74 | "imageheight":448, 75 | "imagewidth":896, 76 | "margin":0, 77 | "name":"kenney_simple_platformer", 78 | "spacing":0, 79 | "tilecount":98, 80 | "tileheight":64, 81 | "tilewidth":64 82 | }], 83 | "tilewidth":64, 84 | "type":"map", 85 | "version":1.2, 86 | "width":14 87 | } -------------------------------------------------------------------------------- /assets/images/kenney_player_atlas.json: -------------------------------------------------------------------------------- 1 | { 2 | "frames": [ 3 | { 4 | "filename": "robo_player_0", 5 | "frame": { 6 | "x": 0, 7 | "y": 0, 8 | "w": 96, 9 | "h": 96 10 | }, 11 | "anchor": { 12 | "x": 0.5, 13 | "y": 0.5 14 | } 15 | }, 16 | { 17 | "filename": "robo_player_1", 18 | "frame": { 19 | "x": 96, 20 | "y": 0, 21 | "w": 96, 22 | "h": 96 23 | }, 24 | "anchor": { 25 | "x": 0.5, 26 | "y": 0.5 27 | } 28 | }, 29 | { 30 | "filename": "robo_player_2", 31 | "frame": { 32 | "x": 192, 33 | "y": 0, 34 | "w": 96, 35 | "h": 96 36 | }, 37 | "anchor": { 38 | "x": 0.5, 39 | "y": 0.5 40 | } 41 | }, 42 | { 43 | "filename": "robo_player_3", 44 | "frame": { 45 | "x": 288, 46 | "y": 0, 47 | "w": 96, 48 | "h": 96 49 | }, 50 | "anchor": { 51 | "x": 0.5, 52 | "y": 0.5 53 | } 54 | }, 55 | { 56 | "filename": "robo_player_4", 57 | "frame": { 58 | "x": 0, 59 | "y": 96, 60 | "w": 96, 61 | "h": 96 62 | }, 63 | "anchor": { 64 | "x": 0.5, 65 | "y": 0.5 66 | } 67 | }, 68 | { 69 | "filename": "robo_player_5", 70 | "frame": { 71 | "x": 96, 72 | "y": 96, 73 | "w": 96, 74 | "h": 96 75 | }, 76 | "anchor": { 77 | "x": 0.5, 78 | "y": 0.5 79 | } 80 | }, 81 | { 82 | "filename": "robo_player_6", 83 | "frame": { 84 | "x": 192, 85 | "y": 96, 86 | "w": 96, 87 | "h": 96 88 | }, 89 | "anchor": { 90 | "x": 0.5, 91 | "y": 0.5 92 | } 93 | }, 94 | { 95 | "filename": "robo_player_7", 96 | "frame": { 97 | "x": 288, 98 | "y": 96, 99 | "w": 96, 100 | "h": 96 101 | }, 102 | "anchor": { 103 | "x": 0.5, 104 | "y": 0.5 105 | } 106 | } 107 | ], 108 | "meta": { 109 | "description": "Atlas generado con Atlas Packer Phaser3", 110 | "web": "https://gammafp.github.io/atlas-packer-phaser/" 111 | } 112 | } -------------------------------------------------------------------------------- /game.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | type: Phaser.AUTO, 3 | parent: 'game', 4 | width: 800, 5 | heigth: 640, 6 | scale: { 7 | mode: Phaser.Scale.RESIZE, 8 | autoCenter: Phaser.Scale.CENTER_BOTH 9 | }, 10 | scene: { 11 | preload, 12 | create, 13 | update, 14 | }, 15 | physics: { 16 | default: 'arcade', 17 | arcade: { 18 | gravity: { y: 500 }, 19 | }, 20 | } 21 | }; 22 | 23 | const game = new Phaser.Game(config); 24 | 25 | function preload() { 26 | // Image layers from Tiled can't be exported to Phaser 3 (as yet) 27 | // So we add the background image separately 28 | this.load.image('background', 'assets/images/background.png'); 29 | // Load the tileset image file, needed for the map to know what 30 | // tiles to draw on the screen 31 | this.load.image('tiles', 'assets/tilesets/platformPack_tilesheet.png'); 32 | // Even though we load the tilesheet with the spike image, we need to 33 | // load the Spike image separately for Phaser 3 to render it 34 | this.load.image('spike', 'assets/images/spike.png'); 35 | // Load the export Tiled JSON 36 | this.load.tilemapTiledJSON('map', 'assets/tilemaps/level1.json'); 37 | // Load player animations from the player spritesheet and atlas JSON 38 | this.load.atlas('player', 'assets/images/kenney_player.png', 39 | 'assets/images/kenney_player_atlas.json'); 40 | } 41 | 42 | function create() { 43 | // Create a tile map, which is used to bring our level in Tiled 44 | // to our game world in Phaser 45 | const map = this.make.tilemap({ key: 'map' }); 46 | // Add the tileset to the map so the images would load correctly in Phaser 47 | const tileset = map.addTilesetImage('kenney_simple_platformer', 'tiles'); 48 | // Place the background image in our game world 49 | const backgroundImage = this.add.image(0, 0, 'background').setOrigin(0, 0); 50 | // Scale the image to better match our game's resolution 51 | backgroundImage.setScale(2, 0.8); 52 | // Add the platform layer as a static group, the player would be able 53 | // to jump on platforms like world collisions but they shouldn't move 54 | const platforms = map.createStaticLayer('Platforms', tileset, 0, 200); 55 | // There are many ways to set collision between tiles and players 56 | // As we want players to collide with all of the platforms, we tell Phaser to 57 | // set collisions for every tile in our platform layer whose index isn't -1. 58 | // Tiled indices can only be >= 0, therefore we are colliding with all of 59 | // the platform layer 60 | platforms.setCollisionByExclusion(-1, true); 61 | 62 | // Add the player to the game world 63 | this.player = this.physics.add.sprite(50, 300, 'player'); 64 | this.player.setBounce(0.1); // our player will bounce from items 65 | this.player.setCollideWorldBounds(true); // don't go out of the map 66 | this.physics.add.collider(this.player, platforms); 67 | 68 | // Create the walking animation using the last 2 frames of 69 | // the atlas' first row 70 | this.anims.create({ 71 | key: 'walk', 72 | frames: this.anims.generateFrameNames('player', { 73 | prefix: 'robo_player_', 74 | start: 2, 75 | end: 3, 76 | }), 77 | frameRate: 10, 78 | repeat: -1 79 | }); 80 | 81 | // Create an idle animation i.e the first frame 82 | this.anims.create({ 83 | key: 'idle', 84 | frames: [{ key: 'player', frame: 'robo_player_0' }], 85 | frameRate: 10, 86 | }); 87 | 88 | // Use the second frame of the atlas for jumping 89 | this.anims.create({ 90 | key: 'jump', 91 | frames: [{ key: 'player', frame: 'robo_player_1' }], 92 | frameRate: 10, 93 | }); 94 | 95 | // Enable user input via cursor keys 96 | this.cursors = this.input.keyboard.createCursorKeys(); 97 | 98 | // Create a sprite group for all spikes, set common properties to ensure that 99 | // sprites in the group don't move via gravity or by player collisions 100 | this.spikes = this.physics.add.group({ 101 | allowGravity: false, 102 | immovable: true 103 | }); 104 | 105 | // Get the spikes from the object layer of our Tiled map. Phaser has a 106 | // createFromObjects function to do so, but it creates sprites automatically 107 | // for us. We want to manipulate the sprites a bit before we use them 108 | map.getObjectLayer('Spikes').objects.forEach((spike) => { 109 | // Add new spikes to our sprite group 110 | const spikeSprite = this.spikes.create(spike.x, spike.y + 200 - spike.height, 'spike').setOrigin(0); 111 | // By default the sprite has loads of whitespace from the base image, we 112 | // resize the sprite to reduce the amount of whitespace used by the sprite 113 | // so collisions can be more precise 114 | spikeSprite.body.setSize(spike.width, spike.height - 20).setOffset(0, 20); 115 | }); 116 | 117 | // Add collision between the player and the spikes 118 | this.physics.add.collider(this.player, this.spikes, playerHit, null, this); 119 | } 120 | 121 | function update() { 122 | // Control the player with left or right keys 123 | if (this.cursors.left.isDown) { 124 | this.player.setVelocityX(-200); 125 | if (this.player.body.onFloor()) { 126 | this.player.play('walk', true); 127 | } 128 | } else if (this.cursors.right.isDown) { 129 | this.player.setVelocityX(200); 130 | if (this.player.body.onFloor()) { 131 | this.player.play('walk', true); 132 | } 133 | } else { 134 | // If no keys are pressed, the player keeps still 135 | this.player.setVelocityX(0); 136 | // Only show the idle animation if the player is footed 137 | // If this is not included, the player would look idle while jumping 138 | if (this.player.body.onFloor()) { 139 | this.player.play('idle', true); 140 | } 141 | } 142 | 143 | // Player can jump while walking any direction by pressing the space bar 144 | // or the 'UP' arrow 145 | if ((this.cursors.space.isDown || this.cursors.up.isDown) && this.player.body.onFloor()) { 146 | this.player.setVelocityY(-350); 147 | this.player.play('jump', true); 148 | } 149 | 150 | // If the player is moving to the right, keep them facing forward 151 | if (this.player.body.velocity.x > 0) { 152 | this.player.setFlipX(false); 153 | } else if (this.player.body.velocity.x < 0) { 154 | // otherwise, make them face the other side 155 | this.player.setFlipX(true); 156 | } 157 | } 158 | 159 | /** 160 | * playerHit resets the player's state when it dies from colliding with a spike 161 | * @param {*} player - player sprite 162 | * @param {*} spike - spike player collided with 163 | */ 164 | function playerHit(player, spike) { 165 | // Set velocity back to 0 166 | player.setVelocity(0, 0); 167 | // Put the player back in its original position 168 | player.setX(50); 169 | player.setY(300); 170 | // Use the default `idle` animation 171 | player.play('idle', true); 172 | // Set the visibility to 0 i.e. hide the player 173 | player.setAlpha(0); 174 | // Add a tween that 'blinks' until the player is gradually visible 175 | let tw = this.tweens.add({ 176 | targets: player, 177 | alpha: 1, 178 | duration: 100, 179 | ease: 'Linear', 180 | repeat: 5, 181 | }); 182 | } 183 | --------------------------------------------------------------------------------