├── 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 |
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 |
--------------------------------------------------------------------------------