HTML5 prototype of a planet gravity platform using Phaser 3 and Arcade physics
Talking about Game development, HTML5, Javascript and Phaser.
This is one of the times I create something so much weird I don’t even know how to name or describe it, but if you have already seen this somewhere, just let me know and I’ll give it a name.
Planet gravity is not a new concept, there is the Angry Birds Space tutorial series ready for you to have a look, but this time the concept is applied to a platformer, where the “planet” is a rectangle and the physics engine is Arcade Physics.
Have a look at the game:
Play with arrow keys, LEFT to move counterclockwise, RIGHT to move clockwise, UP to jump.
Try to reach the edge of the platform and see what happens.
The source code is uncommented and a bit redundant, but it’s quite easy to understand thanks to the massive use of switch
statement.
Each side of the platform has its own physics settings, and the transition between sides is made with a tween:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 | let game; let gameOptions = { gameGravity: 900, heroSpeed: 200, jumpForce: 250 } const SIDE_UP = 0; const SIDE_RIGHT = 1; const SIDE_DOWN = 2; const SIDE_LEFT = 3; window.onload = function () { let gameConfig = { type: Phaser.AUTO, backgroundColor: 0x444444, scale: { mode: Phaser.Scale.FIT, autoCenter: Phaser.Scale.CENTER_BOTH, parent: "thegame" , width: 800, height: 400 }, physics: { default : "arcade" }, scene: playGame } game = new Phaser.Game(gameConfig); } class playGame extends Phaser.Scene{ constructor(){ super ( "PlayGame" ); } preload(){ this .load.image( "tile" , "tile.png" ); this .load.image( "hero" , "hero.png" ); } create(){ this .wall = this .physics.add.sprite(game.config.width / 2, game.config.height / 2, "tile" ); this .wall.displayWidth = game.config.width / 2; this .wall.displayHeight = game.config.height / 2; this .wall.setImmovable( true ); this .hero = this .physics.add.sprite(game.config.width / 2, this .wall.getBounds().top - 100, "hero" ); this .hero.body.gravity.y = gameOptions.gameGravity; this .controls = this .input.keyboard.createCursorKeys(); this .rotating = false ; this .direction = SIDE_UP; } update(){ this .physics.world.collide( this .hero, this .wall, null , null , this ); if (! this .rotating){ if ( this .controls.left.isDown && ! this .controls.right.isDown){ this .moveCounterClockwise(); } else { if ( this .controls.right.isDown && ! this .controls.left.isDown){ this .moveClockwise(); } else { this .stopMoving(); } } if ( this .controls.up.isDown){ this .jump(); } this .checkRotation(); } } moveCounterClockwise(){ this .hero.setFlipX( true ); switch ( this .direction){ case SIDE_UP: this .hero.setVelocity(-gameOptions.heroSpeed, this .hero.body.velocity.y); break ; case SIDE_DOWN: this .hero.setVelocity(gameOptions.heroSpeed, this .hero.body.velocity.y); break ; case SIDE_LEFT: this .hero.setVelocity( this .hero.body.velocity.x, gameOptions.heroSpeed); break ; case SIDE_RIGHT: this .hero.setVelocity( this .hero.body.velocity.x, -gameOptions.heroSpeed); break ; } } moveClockwise(){ this .hero.setFlipX( false ); switch ( this .direction){ case SIDE_UP: this .hero.setVelocity(gameOptions.heroSpeed, this .hero.body.velocity.y); break ; case SIDE_DOWN: this .hero.setVelocity(-gameOptions.heroSpeed, this .hero.body.velocity.y); break ; case SIDE_LEFT: this .hero.setVelocity( this .hero.body.velocity.x, -gameOptions.heroSpeed); break ; case SIDE_RIGHT: this .hero.setVelocity( this .hero.body.velocity.x, gameOptions.heroSpeed); break ; } } stopMoving(){ switch ( this .direction){ case SIDE_UP: this .hero.setVelocity(0, this .hero.body.velocity.y); break ; case SIDE_DOWN: this .hero.setVelocity(0, this .hero.body.velocity.y); break ; case SIDE_LEFT: this .hero.setVelocity( this .hero.body.velocity.x, 0); break ; case SIDE_RIGHT: this .hero.setVelocity( this .hero.body.velocity.x, 0); break ; } } checkRotation(){ switch ( this .direction){ case SIDE_UP: if ( this .hero.getBounds().left > this .wall.getBounds().right && ! this .rotating){ this .handleRotation(1, this .wall.getBounds().right + this .hero.displayWidth / 2 + this .getHeight(), this .wall.getBounds().top + this .hero.displayHeight / 2); } if ( this .hero.getBounds().right < this .wall.getBounds().left && ! this .rotating){ this .handleRotation(-1, this .wall.getBounds().left - this .hero.displayWidth / 2 - this .getHeight(), this .wall.getBounds().top + this .hero.displayHeight / 2); } break ; case SIDE_RIGHT: if ( this .hero.getBounds().top > this .wall.getBounds().bottom && ! this .rotating){ this .handleRotation(1, this .wall.getBounds().right - this .hero.displayWidth / 2, this .wall.getBounds().bottom + this .hero.displayHeight / 2 + this .getHeight()); } if ( this .hero.getBounds().bottom < this .wall.getBounds().top && ! this .rotating){ this .handleRotation(-1, this .wall.getBounds().right - this .hero.displayWidth / 2, this .wall.getBounds().top - this .hero.displayHeight / 2 - this .getHeight()); } break ; case SIDE_DOWN: if ( this .hero.getBounds().right < this .wall.getBounds().left && ! this .rotating){ this .handleRotation(1, this .wall.getBounds().left - this .hero.displayWidth / 2 - this .getHeight(), this .wall.getBounds().bottom - this .hero.displayHeight / 2); } if ( this .hero.getBounds().left > this .wall.getBounds().right && ! this .rotating){ this .handleRotation(-1, this .wall.getBounds().right + this .hero.displayWidth / 2 + this .getHeight(), this .wall.getBounds().bottom - this .hero.displayHeight / 2); } break ; case SIDE_LEFT: if ( this .hero.getBounds().bottom < this .wall.getBounds().top && ! this .rotating){ this .handleRotation(1, this .wall.getBounds().left + this .hero.displayWidth / 2, this .wall.getBounds().top - this .hero.displayHeight / 2 - this .getHeight()); } if ( this .hero.getBounds().top > this .wall.getBounds().bottom && ! this .rotating){ this .handleRotation(-1, this .wall.getBounds().left + this .hero.displayWidth / 2, this .wall.getBounds().bottom + this .hero.displayHeight / 2 + this .getHeight()); } break ; } } handleRotation(delta, targetX, targetY){ this .hero.body.setAllowGravity( false ); this .hero.setVelocity(0, 0) this .rotating = true ; this .tweens.add({ targets: [ this .hero], angle: this .hero.angle + 90 * delta, x: targetX, y: targetY, duration: 200, callbackScope: this , onComplete: function (){ this .rotating = false ; this .hero.body.setAllowGravity( true ); this .direction = Phaser.Math.Wrap( this .direction + delta, 0, 4); this .setGravity(); } }); } setGravity(){ switch ( this .direction){ case SIDE_UP: this .hero.setGravity(0, gameOptions.gameGravity); break ; case SIDE_DOWN: this .hero.setGravity(0, -gameOptions.gameGravity); break ; case SIDE_LEFT: this .hero.setGravity(gameOptions.gameGravity, 0); break ; case SIDE_RIGHT: this .hero.setGravity(-gameOptions.gameGravity, 0); break ; } } jump(){ switch ( this .direction){ case SIDE_UP: if ( this .hero.body.touching.down){ this .hero.setVelocityY(-gameOptions.jumpForce); } break ; case SIDE_DOWN: if ( this .hero.body.touching.up){ this .hero.setVelocityY(gameOptions.jumpForce); } break ; case SIDE_LEFT: if ( this .hero.body.touching.right){ this .hero.setVelocityX(-gameOptions.jumpForce); } break ; case SIDE_RIGHT: if ( this .hero.body.touching.left){ this .hero.setVelocityX(gameOptions.jumpForce); } break ; } } getHeight(){ switch ( this .direction){ case SIDE_UP: return this .wall.getBounds().top - this .hero.getBounds().bottom; case SIDE_DOWN: return this .hero.getBounds().top - this .wall.getBounds().bottom; case SIDE_LEFT: return this .wall.getBounds().left - this .hero.getBounds().right; case SIDE_RIGHT: return this .hero.getBounds().left - this .wall.getBounds().right; } } } |
How would improve the game? Spikes to avoid and coins to collect? Or do you have some other ideas? Give me feedback, and meanwhile download the source code.
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.