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 interested in playing with the Canvas element in JS. Back in the dark ages I used Visual Basic 6 (!) to make a few simple 2D & 3D particle and game engines, and so those were the first things translated to JS. A simple point sphere demo and this basic particle engine were the result. Hilarity and/or horror will ensue if you look at the physics code btw.
I took that particle engine and started building objects to manage images and animation, drawing, sound, keybindings, input sequences, actors controlled by a programmable state machine, basic (read: incorrect) physics and boundary collision. The end result is the incomplete Streets of Rage demo.
What’s missing
- There’s no collision between characters, so you can’t attack anything yet.
- Loading information of any sort. This is easy to do, and I just haven’t gotten to it.
- Additional plumbing to allow multiple players.
I haven’t really worked on this in a month or two (as usual, I got distracted by real life and work) but today I fixed some of the outstanding glitches and reviewed the todo comments throughout the code. There’s plenty more to do yet.
Technical overview of features
For programmers out there interested in the internals, here’s a quick breakdown on what each system does:
- imageStore:
-
- handles the dynamic loading and storing of image files, with the option of providing or generating coordinates for each independent image in sprite sheets and a callback for load complete;
- dynamic creation and storage of flipped-axis versions (to avoid using horribly slow transforms during Canvas drawing);
- a draw function that knows about individual sprites in an image and automatically samples the correct coordinates from the source image;
- a parser for compressed info about graphic files and sprite sheet coordinates that can also generate the coordinates when given the width and height of each sprite in an image.
- animStore:
-
- handles animation sets based on the sprite frames from imageStore;
- has basic animStart, animStop and getCurrentFrame functionality to make it easy for your game to know which frame to draw;
- a parser for compressed animation info.
- inputStore:
-
- handles the collection of keyboard events such as keyup and keydown (real keydown, not affected by the keyboard repeat rate);
- translates raw keyboard events into customisable game key-mapping;
- matches key-map sequences such as “down, down-forward, forward, jab punch” to game actions like “jab fireball”. The sequences are customisable and very similar to Mugen’s ‘D,DF,F,x’ style, including charged attacks such as ‘~20D,U,ab’ (an EX Flash Kick). This is the system I’m most proud of;
- a parser for compressed input sequence data (again, quite similar to Mugen’s).
- actorStore:
-
- handles the creation of game actors (characters, agents, whatever you call them);
- creates instances of actors with physics properties, animations, audio, state machine info etc unique to each instance;
- a programmable state machine to affect a character’s state based on input, physics, current state and state timing, animation etc;
- state machine can switch to new character states, animations, sounds, affect physics etc
- a convenience function for building a draw queue list (see below) for every actor instance.
- physicsStore:
-
- stores basic physics information for each actor or particle such as position, velocity, mass, elastic restitution etc;
- restricts actors to a customisable 3D boundary box
- contains the most broken gravity and drag calculations you’ll ever see;
- No actor/particle collision yet.
- audioStore:
-
- handles the loading and storing of audio files (very similar to imageStore);
- detects browser support for the <audio> element and provides the browser with the appropriate file;
- contains workarounds for popular browser bugs.
- drawQueue:
-
- handles the drawing queue for the game, so that other functions can just pass a list of sprites to draw;
- drawQueue will sort everything by z order before running an optimised draw loop;
- the result from actorStore’s getInstanceDrawList() can be passed directly to drawQueue.
In the Streets of Rage demo, all the above systems are stored in xlib4.js, and the game-specific parts (such as sprite and animation lists, audio and input settings, etc) are stored in xsor4.js. There’s still a huge amount of work to do, but there’s a pretty clean divide between system and game logic.
The source code is licensed pretty liberally, and feedback, comments and patches (!) are welcome. Check the license at the top of the files before using or sampling from the code though, just to be safe.
One thought on “A semi-useful, half-done JavaScript 2D game engine (featuring Streets of Rage 2 characters)”