Talking about Castle Ramble game, Game development, HTML5, Javascript and Phaser.
Castle Ramble by Chorrus Games, available for iOS and Android, is an infinite roguelite platformer where you can shoot at tiles and break them.
What a nice idea! Why not building a HTML5 prototype to let the hero break tiles?
We’ll start from the Yeah Bunny prototype, split the screen in two, then you will be able to jump by tapping on the left side of the screen and fire by tapping on the right side of the screen.
Once a bullet hits a tile, BOOM, the tile no longer exists. Have a look:
Click or tap on the left half of the screen to jump, double jump or wall jump. You can also slide on walls. By clicking or tapping on the right side of the screen, you will fire a bullet which can break walls.
This adds a great twist to tile based games, and here you can find the completely commented source code of the prototype:
let game;
let gameOptions = {
// player gravity
playerGravity: 900,
// player friction when on wall
playerGrip: 100,
// player horizontal speed, in pixels per second
playerSpeed: 200,
// player jump force
playerJump: 400,
// player double jump force
playerDoubleJump: 300,
// bullet speed, in pixels per second
bulletSpeed: 400
}
// constant to make "destructible" more readable
const DESTRUCTIBLE = 1;
window.onload = function() {
let gameConfig = {
type: Phaser.AUTO,
backgroundColor: 0x444444,
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH,
parent: "thegame",
width: 640,
height: 480
},
physics: {
default: "arcade",
arcade: {
gravity: {
y: 0
}
}
},
scene: [preloadGame, playGame]
}
game = new Phaser.Game(gameConfig);
window.focus();
}
class preloadGame extends Phaser.Scene{
constructor(){
super("PreloadGame");
}
preload(){
this.load.tilemapTiledJSON("level", "level.json");
this.load.image("tile", "tile.png");
this.load.image("hero", "hero.png");
this.load.image("bullet", "bullet.png");
}
create(){
this.scene.start("PlayGame");
}
}
class playGame extends Phaser.Scene{
constructor(){
super("PlayGame");
}
create(){
// creation of "level" tilemap
this.map = this.make.tilemap({
key: "level"
});
// adding tiles to tilemap
let tile = this.map.addTilesetImage("tileset01", "tile");
// tiles 1 and 2 have collision enabled
this.map.setCollision([1, 2]);
// which layer should we render? "layer01"
this.layer = this.map.createDynamicLayer("layer01", tile);
// add the hero sprite and enable ARCADE physics
this.hero = this.physics.add.sprite(300, 376, "hero");
// set hero horizontal speed
this.hero.body.velocity.x = gameOptions.playerSpeed;
// the hero can jump
this.canJump = true;
// the hero cannot double jump
this.canDoubleJump = false;
// the hero is not on the wall
this.onWall = false;
// the hero can fire a bullet
this.canFire = true;
// add the bullet and enable arcade physics
this.bullet = this.physics.add.sprite(-20, -20, "bullet");
// wait for player input
this.input.on("pointerdown", this.handleInput, this);
// set workd bounds to allow camera to follow the player
this.cameras.main.setBounds(0, 0, 1920, 1440);
// make the camera follow the player
this.cameras.main.startFollow(this.hero);
}
handleInput(e){
// left half of the screen = jump
if(e.x < game.config.width / 2){
// the hero can jump when:
// canJump is true AND the hero is on the ground (blocked.down)
// OR
// the hero is on the wall
if((this.canJump && this.hero.body.blocked.down) || this.onWall){
// apply jump force
this.hero.body.velocity.y = -gameOptions.playerJump;
// is the hero on a wall?
if(this.onWall){
// change the horizontal velocity too. This way the hero will jump off the wall
this.setPlayerXVelocity(true);
}
// hero can't jump anymore
this.canJump = false;
// hero is not on the wall anymore
this.onWall = false;
// hero can now double jump
this.canDoubleJump = true;
}
else{
// can the hero make the doubple jump?
if(this.canDoubleJump){
// the hero can't double jump anymore
this.canDoubleJump = false;
// apply double jump force
this.hero.body.velocity.y = -gameOptions.playerDoubleJump;
}
}
}
// right half of the sceen = fire
else{
// can the player fire?
if(this.canFire){
// player can't fire anymore
this.canFire = false;
// place the bullet at player position
this.bullet.x = this.hero.x;
this.bullet.y = this.hero.y;
// set bullet speed
this.bullet.body.velocity.x = gameOptions.bulletSpeed * ((this.hero.body.velocity.x > 0) ? 1 : -1);
}
}
}
update(){
// set some default gravity values. Look at setDefaultValues method for more information
this.setDefaultValues();
// handle collision between bullet and tiles
this.physics.world.collide(this.bullet, this.layer, function(bullet, layer){
// stop the bullet
bullet.body.velocity.x = 0;
// place the bullet out of the stage
bullet.x = -20;
// now the player can fire again
this.canFire = true;
// is the tile destructible?
if(layer.index == DESTRUCTIBLE){
// remove the tile from the map
this.map.removeTileAt(layer.x, layer.y);
}
}, null, this);
// handle collision between hero and tiles
this.physics.world.collide(this.hero, this.layer, function(hero, layer){
// some temporary variables to determine if the player is blocked only once
let blockedDown = hero.body.blocked.down;
let blockedLeft = hero.body.blocked.left
let blockedRight = hero.body.blocked.right;
// if the hero hits something, no double jump is allowed
this.canDoubleJump = false;
// hero on the ground
if(blockedDown){
// hero can jump
this.canJump = true;
}
// hero on the ground and touching a wall on the right
if(blockedRight){
// horizontal flipping hero sprite
hero.flipX = true;
}
// hero on the ground and touching a wall on the right
if(blockedLeft){
// default orientation of hero sprite
hero.flipX = false;
}
// hero NOT on the ground and touching a wall
if((blockedRight || blockedLeft) && !blockedDown){
// hero on a wall
hero.scene.onWall = true;
// remove gravity
hero.body.gravity.y = 0;
// setting new y velocity
hero.body.velocity.y = gameOptions.playerGrip;
}
// adjust hero speed according to the direction it's moving
this.setPlayerXVelocity(!this.onWall || blockedDown);
}, null, this)
}
// default values to be set at the beginning of each update cycle,
// which may be changed according to what happens into "collide" callback function
// (if called)
setDefaultValues(){
this.hero.body.gravity.y = gameOptions.playerGravity;
this.onWall = false;
this.setPlayerXVelocity(true);
}
// set player velocity according to the direction it's facing, unless "defaultDirection"
// is false, in this case multiplies the velocity by -1
setPlayerXVelocity(defaultDirection){
this.hero.body.velocity.x = gameOptions.playerSpeed * (this.hero.flipX ? -1 : 1) * (defaultDirection ? 1 : -1);
}
}
Another great game concept built in a few lines thanks to Phaser power. Download the source code and play with it.
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.