Today I felt like making a game, so I learnt about Phaser and set myself the task/challenge of recreating a simple game from my childhood. Phaser is incredibly fun and easy to get started, I’d recommend giving it a go!

The game I chose was Tanx.

I started with the excellent Phaser tutorial and converted it to CoffeeScript. It’s perfectly fine to use CoffeeScript with Phaser, or I guess any other language that compiles to JavaScript.

It’s very rough and I only spent a few hours on it, but it gives you an idea of what you can make with this framework, even without any prior knowledge!

Instructions:

Try to shoot the other player by adjusting the angle and power. From the left, try an angle of about -30. From the right, try an angle of about 210.

Keys:

UP/DOWN: adjust angle
A/Z: adjust power
LEFT/RIGHT: pan screen
SPACE: fire / start new game

Full source code:

GAME_WIDTH = 1200

currentAngle = currentPower = 500 maxPower = 600

cursors = null current_player = null font = null win_message = null tanks = null

firing = false

game_over = false

toRad = (angle) -> angle * Math.PI / 180

loadFonts = => @angle = game.add.retroFont ‘knightHawks’, 31, 25, Phaser.RetroFont.TEXT_SET6, 10, 1, 1 @power = game.add.retroFont ‘knightHawks’, 31, 25, Phaser.RetroFont.TEXT_SET6, 10, 1, 1

bulletHit = (body, shapeA, shapeB, equation) -> firing = false this.kill() if body.sprite.parent == tanks game_over = true win = game.add.retroFont ‘knightHawks’, 31, 25, Phaser.RetroFont.TEXT_SET6, 10, 1, 1 win.text = current_player.name + ’ wins!’ win_message = game.add.image 60, 300, win win_message.fixedToCamera = true win_message.cameraOffset.x = 60 else nextTurn()

changeAngle = (diff) -> setAngle(currentAngle + diff)

setAngle = (angle) => currentAngle = angle @angle.text = ‘angle: ‘ + currentAngle

changePower = (diff) -> setPower(currentPower + diff)

setPower = (power) => currentPower = power @power.text = ‘power: ‘ + currentPower

resetAngleAndPower = -> setAngle setPower 200

nextTurn = -> resetAngleAndPower() current_player = if current_player == @player setAngle 180 @player2 else setAngle @player resetCamera()

resetCamera = -> game.camera.target = current_player

resetGame = -> game_over = false current_player = @player resetCamera() resetAngleAndPower() win_message.destroy()

fireBullet = => unless firing # create a bullet angle = currentAngle power = currentPower # calculate velocity x_velocity = Math.cos(toRad(angle)) power y_velocity = Math.sin(toRad(angle)) power cp = current_player #offset to avoid shooting self offset_x = if angle > 90 || angle < -90 -32 else 32

<span class="c1"># create the sprite and set it going</span>
<span class="nv">bullet = </span><span class="nx">game</span><span class="p">.</span><span class="nx">add</span><span class="p">.</span><span class="nx">sprite</span> <span class="nx">cp</span><span class="p">.</span><span class="nx">x</span> <span class="o">+</span> <span class="nx">offset_x</span><span class="p">,</span> <span class="nx">cp</span><span class="p">.</span><span class="nx">y</span><span class="p">,</span> <span class="s">'bullet'</span>
<span class="nx">game</span><span class="p">.</span><span class="nx">physics</span><span class="p">.</span><span class="nx">p2</span><span class="p">.</span><span class="nx">enable</span> <span class="nx">bullet</span>
<span class="nv">bullet.body.velocity.x = </span><span class="nx">x_velocity</span>
<span class="nv">bullet.body.velocity.y = </span><span class="nx">y_velocity</span>
<span class="nx">bullet</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">onBeginContact</span><span class="p">.</span><span class="nx">add</span> <span class="nx">bulletHit</span><span class="p">,</span> <span class="nx">bullet</span>
<span class="nx">game</span><span class="p">.</span><span class="nx">camera</span><span class="p">.</span><span class="nx">follow</span> <span class="nx">bullet</span>
<span class="nv">firing = </span><span class="kc">true</span>

@preload = -> game.load.image ‘sky’, ’/games/assets/sky.png’ game.load.image ‘ground’, ’/games/assets/platform.png’ game.load.image ‘bullet’, ’/games/assets/ball.gif’ game.load.spritesheet ‘dude’, ’/games/assets/dude.png’, 32, 48 game.load.image ‘knightHawks’, ’/games/assets/fonts/KNIGHT3.png’

@create = => game.world.setBounds , , GAME_WIDTH, 600

game.physics.startSystem Phaser.Physics.P2JS game.physics.p2.gravity.y = 100 game.physics.p2.defaultRestitution = 0.9 game.physics.p2.setImpactEvents true

sky = game.add.tileSprite , , 2000, 600, ‘sky’

# create the ground sprite and set to static @ground = game.add.sprite , game.world.height - 48, ‘ground’ @ground.scale.setTo 6,3

game.physics.p2.enable @ground @ground.body.static = true

# create the ’tanks’ tanks = game.add.group() @player = tanks.create 32, game.world.height - 150, ‘dude’ @player.frame = 5 @player.name = ‘Player 1’ @player2 = tanks.create GAME_WIDTH - 120, game.world.height - 150, ‘dude’ @player2.name = ‘Player 2’

# angle / power text loadFonts()

@ang = game.add.image 30, 30, @angle ang.fixedToCamera = true ang.cameraOffset.x = 30

pow = game.add.image 30, 60, @power pow.fixedToCamera = true pow.cameraOffset.x = 30

setPower currentPower setAngle currentAngle

# enable physics! tanks.forEach (thing) -> game.physics.p2.enable(thing) thing.body.gravity.y = 100

# set first player current_player = @player

game.physics.p2.setImpactEvents true

cursors = game.input.keyboard.createCursorKeys() game.input.keyboard.addKeyCapture Phaser.Keyboard.SPACEBAR

@update = -> if game_over if game.input.keyboard.isDown(Phaser.Keyboard.SPACEBAR) resetGame() else if cursors.right.isDown game.camera.target = null game.camera.x += 8 if cursors.left.isDown game.camera.target = null game.camera.x -= 8 if cursors.up.isDown changeAngle -1 if cursors.down.isDown changeAngle 1 if game.input.keyboard.isDown(Phaser.Keyboard.SPACEBAR) fireBullet() if game.input.keyboard.isDown(Phaser.Keyboard.A) changePower 1 if game.input.keyboard.isDown(Phaser.Keyboard.Z) changePower -1 @game = game = new Phaser.Game(800, 600, Phaser.CANVAS, ‘tanks’, { preload: preload, create: create, update: update })