August 8, 2010

A semi-useful, half-done JavaScript 2D game engine (featuring Streets of Rage 2 characters)

In mid-April this year I got inter­est­ed in play­ing with the Can­vas ele­ment in JS. Back in the dark ages I used Visu­al Basic 6 (!) to make a few sim­ple 2D & 3D par­ti­cle and game engines, and so those were the first things trans­lat­ed to JS. A sim­ple point sphere demo and this basic par­ti­cle engine were the result. Hilar­i­ty and/or hor­ror will ensue if you look at the physics code btw.

I took that par­ti­cle engine and start­ed build­ing objects to man­age images and ani­ma­tion, draw­ing, sound, key­bind­ings, input sequences, actors con­trolled by a pro­gram­ma­ble state machine, basic (read: incor­rect) physics and bound­ary col­li­sion. The end result is the incom­plete Streets of Rage demo.

What’s missing

I haven’t real­ly worked on this in a month or two (as usu­al, I got dis­tract­ed by real life and work) but today I fixed some of the out­stand­ing glitch­es and reviewed the todo com­ments through­out the code. There’s plen­ty more to do yet.

Technical overview of features

For pro­gram­mers out there inter­est­ed in the inter­nals, here’s a quick break­down on what each sys­tem does:

  • han­dles the dynam­ic load­ing and stor­ing of image files, with the option of pro­vid­ing or gen­er­at­ing coor­di­nates for each inde­pen­dent image in sprite sheets and a call­back for load complete;
  • dynam­ic cre­ation and stor­age of flipped-axis ver­sions (to avoid using hor­ri­bly slow trans­forms dur­ing Can­vas drawing);
  • a draw func­tion that knows about indi­vid­ual sprites in an image and auto­mat­i­cal­ly sam­ples the cor­rect coor­di­nates from the source image;
  • a pars­er for com­pressed info about graph­ic files and sprite sheet coor­di­nates that can also gen­er­ate the coor­di­nates when giv­en the width and height of each sprite in an image.
  • han­dles ani­ma­tion sets based on the sprite frames from imageStore;
  • has basic ani­m­Start, ani­m­Stop and getCur­rent­Frame func­tion­al­i­ty to make it easy for your game to know which frame to draw;
  • a pars­er for com­pressed ani­ma­tion info.
  • han­dles the col­lec­tion of key­board events such as keyup and key­down (real key­down, not affect­ed by the key­board repeat rate);
  • trans­lates raw key­board events into cus­tomis­able game key-mapping;
  • match­es key-map sequences such as “down, down-for­ward, for­ward, jab punch” to game actions like “jab fire­ball”. The sequences are cus­tomis­able and very sim­i­lar to Mugen’s ‘D,DF,F,x’ style, includ­ing charged attacks such as ‘~20D,U,ab’ (an EX Flash Kick). This is the sys­tem I’m most proud of;
  • a pars­er for com­pressed input sequence data (again, quite sim­i­lar to Mugen’s).
  • han­dles the cre­ation of game actors (char­ac­ters, agents, what­ev­er you call them);
  • cre­ates instances of actors with physics prop­er­ties, ani­ma­tions, audio, state machine info etc unique to each instance;
  • a pro­gram­ma­ble state machine to affect a char­ac­ter’s state based on input, physics, cur­rent state and state tim­ing, ani­ma­tion etc;
  • state machine can switch to new char­ac­ter states, ani­ma­tions, sounds, affect physics etc
  • a con­ve­nience func­tion for build­ing a draw queue list (see below) for every actor instance.
  • stores basic physics infor­ma­tion for each actor or par­ti­cle such as posi­tion, veloc­i­ty, mass, elas­tic resti­tu­tion etc;
  • restricts actors to a cus­tomis­able 3D bound­ary box
  • con­tains the most bro­ken grav­i­ty and drag cal­cu­la­tions you’ll ever see;
  • No actor/particle col­li­sion yet.
  • han­dles the load­ing and stor­ing of audio files (very sim­i­lar to imageStore);
  • detects brows­er sup­port for the <audio> ele­ment and pro­vides the brows­er with the appro­pri­ate file;
  • con­tains workarounds for pop­u­lar brows­er bugs.
  • han­dles the draw­ing queue for the game, so that oth­er func­tions can just pass a list of sprites to draw;
  • drawQueue will sort every­thing by z order before run­ning an opti­mised draw loop;
  • the result from actor­Store’s getInstance­DrawList() can be passed direct­ly to drawQueue.

In the Streets of Rage demo, all the above sys­tems are stored in xlib4.js, and the game-spe­cif­ic parts (such as sprite and ani­ma­tion lists, audio and input set­tings, etc) are stored in xsor4.js. There’s still a huge amount of work to do, but there’s a pret­ty clean divide between sys­tem and game logic.

The source code is licensed pret­ty lib­er­al­ly, and feed­back, com­ments and patch­es (!) are wel­come. Check the license at the top of the files before using or sam­pling from the code though, just to be safe.

posted by Andrew

One thought on “A semi-useful, half-done JavaScript 2D game engine (featuring Streets of Rage 2 characters)

Leave a Reply

Your email address will not be published. Required fields are marked *