A few years ago Impact.JS was one of the best JavaScript game libraries around. Development ceased in 2014, meaning it lost ground to other engines still being actively developed. In 2018, the author, Dominic Szablewski, made Impact.JS free and open source.
That makes it ideal if you want to dig around in the code to see what makes a game engine tick. You can find the source code on GitHub.
A few years ago, I wanted to develop a simple browser Christmas game, so I was looking around for a suitable JavaScript game framework. Certainly it’s possible to write a game from the ground up, but why reinvent the wheel? Most programmers approaching the development of a new website would rather start with an established framework like John Resig’s excellent jQuery library than expend hours of effort developing their own cross-browser approach. Why should games development be any different?
Having surveyed the numerous libraries out there, I opted for Impact.JS. At the time, this was not free, but it did seem well supported, with a large, active user base. Impact sets up a canvas and a set of entities and establishes a game loop, which consists of an update phase, in which entities are updated based on player input or the physics model, and a draw phase in which they are positioned on the canvas in the correct z-order. This model will be familiar to anyone who has worked with other game libraries or frameworks. Having this core functionality allows you to focus your time and energy on building the game mechanics.
This game was only really intended as a means of developing my HTML5 games development skills and as a bit of silly fun for family and friends. The concept was simple one – to stage a snowball fight with Santa’s elves. Here’s a screenshot of the game in action:
Impact also comes with an excellent tool called Weltmeister, for building levels. As this game is not level based but based around a single background, I didn’t have need for it on this occasion, but I recommend you explore it if you are thinking of using Impact.
Impact comes with several example games, so getting a feel for how the library is used did not take too long. The first real technical issue I had to solve was the snowballs. The snowballs that are thrown by the player need to look like they are flying into the screen. Correspondingly the snowballs thrown by the elves need to look like they are flying out towards the player. My first thoughts on how to do this were by scaling the snowball sprite as it flies. Impact allows you to set a scale for the entire game, but there didn’t seem to be a built in way of scaling individual entities. Fortunately, because Impact uses a pseudo class structure, you can override the inherited draw method for each entity. Furthermore, in the draw method you can safely manipulate the canvas drawing context directly, as long as it is saved before you make changes and then restored before the draw function returns. I was able to use this to directly change the canvas scale before drawing the snowball entity:
draw: function() { var ctx = ig.system.context; ctx.save(); // save the drawing context // set the origin to the position of the entity ctx.translate( ig.system.getDrawPos( this.pos.x - this.offset.x - ig.game.screen.x ), ig.system.getDrawPos( this.pos.y - this.offset.y - ig.game.screen.y ) ); // scale the drawing context appropriately ctx.scale( this._scale, this._scale ); // now call the built in function to draw to the translated canvas this.currentAnim.draw( 0, 0 ); ctx.restore(); // restore the context before returning },
This worked well. The next surprise was that Impact has no button class, but having seen that Impact can co-exist quite happily with code that writes directly to the canvas, I used a similar approach to draw outlines for buttons.
Because the snowballs appear to move in and out of the screen, one thing I had to be careful of with this game was z-ordering. Once an elf had thrown a snowball, I needed this to remain above other entities, even those that are subsequently added to the scene. Impact keeps track of live entities in an array. Any new entities that are spawned are simply pushed to the end of the array. During the draw routine the array is iterated in order, so the last entities added are the last to be drawn and appear above the other entities. However, it is possible to give each entity a z-order and then re-sort the array by z-order during the update phase. This is exactly the approach I took initially – I would give the elf snowball an arbitrarily high z-order and then call the function to re-sort the array. This seemed to work fine, until I tested it on Google Chrome. For some reason, Chrome does not like that sort function at all – sprites would flicker, disappear and appear for no apparent reason.
The solution was, once again, to implement some custom behaviour. So I implemented my own mini-sort that simply looks for the elf snowball in the array, snips it out of it’s current position (if it’s not already the last element) and pushes it back to the top of the array. It’s a testament to how robust Impact is that it tolerated this “interference” without any problems.
When the game was finished, I posted the link on Facebook. Everybody seemed to enjoy it, but the only way they could share their high scores was by commenting in Facebook. They wanted to have some sort of official leaderboard instead. Once again, I didn’t have the desire nor the time to go down the road of reinventing the wheel, so I looked around for a strong JavaScript social gaming API.
After surveying the field I decided to use clay.io. I picked it for the following reasons:
- It was free to use if your game was free
- It had a marketplace, but you were not obliged to feature your game there
- The game could be hosted on your own server or the hosting/cloud service you use – it didn’t have to be uploaded to Clay.io
- You could set it up so that players only have to log in if they want to use the social gaming functions you enable
- The out of the box functionality and look and feel was attractive and professional, but could be customised
- It was quick and easy to set up and implement
Even if you were hosting the game yourself, you still created a game record on Clay.io. This gave you a unique key for your game, so that you could associate the API functions you include with the correct game in your Clay account. You then just included a brief script (which was customised based on the functions you want to include) on the initial html page and then included simple snippets of code to activate the functions you needed at the appropriate points in your game.
Within 15 minutes I had a working solution using the default look and feel of the Clay UI:
Unfortunately in my haste to get this new functionality out to everybody, I neglected to pay enough attention to the game settings on Clay.io. The default behaviour for Clay is for the login/sign up screen to appear as soon as it can once the API has downloaded. Because I hadn’t changed this, my players were getting the login screen appearing even if they had no intention of using the leaderboard function. To make it even more annoying, if it was a bit slow in loading it would sometimes appear after they had started playing and interrupt the action. If you wanted the login/sign up screen to appear only when a social gaming function is used, you had to explicitly set this on the game settings page on Clay.io. Once this was done it worked fine – players who weren’t interested in the leaderboard didn’t see it at all, while players who wanted to post their scores would be prompted to sign up or log in the first time they tried to post a score.
From the information on the clay.io website, it looks like they are currently in the process of rebuilding the whole system, but if you want to explore their SDK you can find it here on GitHub.
If you want to try SnowThrow for yourself, you’ll find a working version of the game here: Play Snowthrow game. Note that in this version, the leaderboard functionality has been disabled.
If you want to explore the source code you’ll find it on GitHub: Snowthrow source code.
Be First to Comment