Jet Boy (or Super Jet Boy to give it its full title) is a game about a boy with a rocket pack who embarks on an adventure to recover some mystical golden statuettes. High above the kingdoms of his homeworld a series of enchanted sky temples play host to an array of weird monsters charged with one simple task – protect their master’s golden treasure.
Jet Boy has no special powers. For once I resisted the urge to load my game character up with bombs & lasers and throw explosions around the screen. All I wanted with Jet Boy was a simple case of learning how to control a character with a rocket pack.
Much of the work in creating the game was already written thanks to my work on Crossfire.
I pretty much lifted most of that logic when constructing Jet Boy and adapted it to respond to gravity and bi-directional movement.
The real challenge in developing the game was in the control of the main character.
I knew that I wanted movement in all directions and also the concept of thrust to lift the character higher up the level. When no thrust was applied (either through no screen press or lack of fuel) the character would simply fall until they reached the base of the level. At this point the player would be looking for bonus ballons carrying fuel to re-enable the rocket pack.
So how do you acheive all of this without using on-screen buttons in a touch screen environment?
The obvious conflict was in the distinction between simply touching the screen to move the character and touching the screen to apply thrust to the rocket pack. As soon as I tap the screen both would apply. I may only want to shift the character from left to right in a horizontal fashion but by virtue of the fact I’m touching the screen the rocket pack would fire and the character would raise.
I initially figured this would be acceptable but the moment I fired it up it was anything but. In fact it was maddening.
So I set a new attribute on the player object that essentially ticked down to zero. At zero the character would start to rise.
It’s a kind of pre-thrust thrust if you like.
The player touches the screen, the ticker is set, the rocket pack engages, the ticker begins the count down, a second or so later the ticker hits zero and there is enough thrust to enable the character to rise. If the player releases the touch before the ticker hits zero the ticker is forced to zero.
This worked quite well as it allowed for a reasonable amount of horizontal movement before the character started to rise up the screen.
To aid this I deliberately set horizontal motion to be very loose. In other words when the player slides his finger quickly across the screen the character zips left to right (or vice versa) at speed.
As usual when you implement this kind of thing to solve a problem you are often treated to a few surprises. One such surprise was in how nice it felt to have the character free-falling down the level and gaining speed through gravity, only to have that descent cut short in a smooth fashion thanks to the application of thrust. There is no fancy physics engine beneath all of this it is simply a case of fine tuning the numbers until I’m happy with the results.
Finally I would like to mention here that to achieve the smooth motion I deal a lot in floating point values. i.e. 1.2345 rather than 1. This affords a decent amount of granularity. But when it comes to drawing on the screen using context.drawImage() I cannot apply those floating point values to the x/y co-ordinates of the sprite. The result is a blurry mess of a sprite.
To combat this I simply round the figures down to the nearest integer with Math.round() / Math.floor() — round up and round down respectively.
All of my sprites contain an ‘angle’ attribute so rotation occurs on each sprite regardless of whether the angle is always zero.
/* g.ctx stores the 2D canvas context, o is the sprite object, the spritesheet property stores all the sprite image data */ g.ctx.save(); g.ctx.translate(Math.floor(o.x) + (Math.floor(o.w/2)),Math.floor(o.y) + (Math.floor(o.h/2))); g.ctx.rotate(o.angle * (Math.PI / 180)); g.ctx.drawImage(o.spritesheet.image, o.frame * o.spritesheet.framewidth, o.row * o.spritesheet.frameheight, o.spritesheet.framewidth, o.spritesheet.frameheight, Math.round(-o.w/2), Math.round(-o.h/2), Math.round(o.w), Math.round(o.h)); g.ctx.restore();
(I will document my approach to spritesheeting at some point in the near future)
Sprite without rounding:
So that’s it. The game is still very much in development and I’m spending a healthy amount of time enjoying the process of designing levels. When the game is complete it will be available through the usual portals and of course Mozilla’s MarketPlace.