As you can see in the above GIF, I’ve made some major progress since yesterday.
Instead of dwelling on trying to make everything exactly right and then dealing with scaling up all the images on a canvas level, I decided I would follow the spirit of the 32×32 grid rule — as Daniel suggested — and just make everything match a higher resolution. So, instead of 32×32, I drew everything to match the original 32×32 space to start, but then scaled everything in a paint program to match 480 by 480.
After fretting over it some this morning, I just decided I would bend the rule in order to get more enjoyment out of the process. And, honestly, I’m not taking part to win anything, anyway. It’s a fun challenge and, after deciding not to worry too much about scaling the images, I was able to finally get some major work done toward creating a level and starting to populate it with obstacles.
With that in mind, actually, I also decided I would share some of my code too. While it’s not the whole solution, here is my current code for Mega. It’s everything that governs the motion and shooting of the character moving around in the GIF.
Oh, and yes, I did comment nearly all of it too. It’s fairly straightforward code for creating a Phaser.Sprite subclass in JavaScript, but I’ve written out what it going on in the different parts.
function Mega(game, x, y, cacheKey) { | |
// Call the Phaser.Sprite constructor | |
Phaser.Sprite.call(this, game, x, y, cacheKey); | |
// Enable the default (set to ARCADE) physics system | |
this.game.physics.enable(this); | |
// Set the anchor (body.x and body.y position) | |
// to the center (0.5 * width, 0.5 * height) | |
// of the sprite | |
this.anchor.setTo(0.5, 0.5); | |
// Check to see if this sprite is colliding | |
// with the world bounds | |
this.body.collideWorldBounds = true; | |
// Set the gravity | |
this.body.gravity.y = 190; | |
// Set the health | |
this.health = 7; | |
// The default 'facing' is left | |
this.facing = "left"; | |
// The horizontal (body.velocity.x) is 190 | |
this.hozMove = 190; | |
// The vertical (body.velocity.y) is -230 | |
this.vertMove = -230; | |
// The time between when jumping is allowed | |
this.jumpTimer = 0; | |
// Create an internal 'bulletGroup' | |
this.bulletGroup = this.game.add.group(); | |
// Enable 'body' on all entries | |
this.bulletGroup.enableBody = true; | |
// Set the physics bodies to be ARCADE | |
this.bulletGroup.physicsBodyType = Phaser.Physics.ARCADE; | |
// Create 130 entries using the 'bullet' cache key | |
this.bulletGroup.createMultiple(130, 'bullet'); | |
// Set that the world bounds are checked | |
this.bulletGroup.setAll('checkWorldBounds', true); | |
// kill() any entries that go out of (world) bounds | |
this.bulletGroup.setAll('outOfBoundsKill', true); | |
// The firing rate (in milliseconds) | |
this.fireRate = 410; | |
// The time between bullets | |
this.nextFire = 0; | |
// The body.velocity.x of the bullets | |
this.bulletSpeed = 280; | |
} | |
Mega.prototype = Object.create(Phaser.Sprite.prototype); | |
Mega.prototype.constructor = Mega; | |
Mega.prototype.moveLeft = function() { | |
// Check if the sprite is on the ground or touching | |
// another sprite | |
if ((this.body.onFloor() || this.body.touching.down)) { | |
if (this.facing === 'left') { | |
this.frame = 5; | |
} else { | |
this.frame = 0; | |
} | |
} | |
// Since we aren't touching another sprite | |
// or on the "floor" (colliding with a tilemap) | |
// use the jumping frames | |
else { | |
if (this.facing === 'left') { | |
this.frame = 9; | |
} else { | |
this.frame = 8; | |
} | |
} | |
// Set the negative velocity | |
this.body.velocity.x = -this.hozMove; | |
// Check and set the 'facing' | |
if (this.facing !== 'left') { | |
this.facing = 'left'; | |
} | |
}; | |
Mega.prototype.moveRight = function() { | |
// Check if the sprite is on the ground or touching | |
// another sprite | |
if ((this.body.onFloor() || this.body.touching.down)) { | |
if (this.facing === 'left') { | |
this.frame = 5; | |
} else { | |
this.frame = 0; | |
} | |
} | |
// Since we aren't touching another sprite | |
// or on the "floor" (colliding with a tilemap) | |
// use the jumping frames | |
else { | |
if (this.facing === 'left') { | |
this.frame = 9; | |
} else { | |
this.frame = 8; | |
} | |
} | |
// Set the positive velocity | |
this.body.velocity.x = this.hozMove; | |
// Check and set the 'facing' | |
if (this.facing !== 'right') { | |
this.facing = 'right'; | |
} | |
}; | |
Mega.prototype.shoot = function() { | |
// Check if the sprite is on the ground or touching | |
// another sprite | |
if ((this.body.onFloor() || this.body.touching.down)) { | |
if (this.facing === 'left') { | |
this.frame = 7; | |
} else { | |
this.frame = 6; | |
} | |
} | |
// Since we aren't touching another sprite | |
// or on the "floor" (colliding with a tilemap) | |
// use the jumping frames | |
else { | |
if (this.facing === 'left') { | |
this.frame = 11; | |
} else { | |
this.frame = 10; | |
} | |
} | |
// Check that enough time has past since the last shot | |
// AND make sure there are at least three 'dead' bullets to use | |
if (this.game.time.now > this.nextFire && this.bulletGroup.countDead() > 3) | |
{ | |
// Update the timer | |
this.nextFire = this.game.time.now + this.fireRate; | |
// Get the first 'dead' bullet for recycling | |
var bullet = this.bulletGroup.getFirstDead(); | |
// Check and adjust bullet starting position | |
// from which way Mega is 'facing' | |
if (this.facing === 'left') { | |
bullet.reset(this.body.x - (this.width / 4), this.body.y + (this.width / 2) - 8); | |
bullet.body.velocity.x = -this.bulletSpeed; | |
} else { | |
bullet.reset(this.body.x + this.width, this.body.y + (this.width / 2) - 8); | |
bullet.body.velocity.x = this.bulletSpeed; | |
} | |
} | |
}; | |
Mega.prototype.jump = function() { | |
// Check that enough time has past since the last jump | |
// AND make sure Mega is on the floor or another sprite | |
if (this.game.time.now > this.jumpTimer && | |
(this.body.onFloor() || this.body.touching.down)) { | |
// Set the (negative) velocity.y | |
this.body.velocity.y = this.vertMove; | |
// Update the jump timer | |
this.jumpTimer = this.game.time.now + 650; | |
// Adjust Mega to | |
// use the jumping frames | |
if (this.facing === 'left') { | |
this.frame = 9; | |
} else { | |
this.frame = 8; | |
} | |
} | |
}; | |
Mega.prototype.idle = function() { | |
// Set the 'idle' frames | |
if (this.facing === 'left') { | |
this.frame = 5; | |
} else { | |
this.frame = 0; | |
} | |
}; | |
Mega.prototype.moveDown = function() { | |
// If we are in the air, apply the inverse jumping force | |
if (!(this.body.onFloor() || this.body.touching.down)) { | |
this.body.velocity.y = -this.vertMove; | |
} | |
}; | |
Mega.prototype.update = function() { | |
this.body.velocity.x = 0; | |
// Moving left | |
if (this.game.input.keyboard.isDown(Phaser.Keyboard.LEFT) || | |
this.game.input.keyboard.justPressed(Phaser.Keyboard.A)) { | |
this.moveLeft(); | |
} | |
// Moving right | |
else if (this.game.input.keyboard.isDown(Phaser.Keyboard.RIGHT) || | |
this.game.input.keyboard.justPressed(Phaser.Keyboard.D)) { | |
this.moveRight(); | |
} | |
// Moving down | |
else if (this.game.input.keyboard.justPressed(Phaser.Keyboard.DOWN) || | |
this.game.input.keyboard.justPressed(Phaser.Keyboard.S)) { | |
this.moveDown(); | |
} | |
// Idling | |
else { | |
this.idle(); | |
} | |
// Shooting needs to be seperate so that a directional | |
// button can be pressed AND shooting can occur while | |
// that happens. It's so Mega can run-and-gun at the | |
// same time. | |
if (this.game.input.keyboard.justPressed(Phaser.Keyboard.SPACEBAR)) { | |
this.shoot(); | |
} | |
// Moving up (jumping) needs to be seperate for the same | |
// reason shooting does: Mega should be able to | |
// to run and jump without letting up on the | |
// directional input. | |
if (this.game.input.keyboard.justPressed(Phaser.Keyboard.UP) || | |
this.game.input.keyboard.justPressed(Phaser.Keyboard.W)) { | |
this.jump(); | |
} | |
}; |