Looking at BrowserQuest’s Animation Handling

I’ve written a small, quick experiment for me to see if it makes sense to write an MMO using WebSockets. It was a lot of fun but mostly what I learnt was that I was coding myself into a bit of a corner. There’s lots of techniques I’ve learnt from looking at other source code, especially BrowserQuest (code) (which is a huge inspiration).

What BrowserQuest looks like whilst playing.
What BrowserQuest looks like whilst playing.

I’ve decided to draw a line under that work, do a bit of a review, and start again on sturdier feet. The first thing I want to talk about is drawing graphics and animation.

I ignored this to start with, just drawing a round circle which the player could move around. That’s mostly because my first aim was to get a number of players moving around and seeing each other move around, using web sockets.

Upgrading from just a circle wasn’t much of a hassle. Each player object has their own draw() method which handled all the drawing it had to do. Switching from a circle to a graphic mostly just meant changing one method. Additionally I now had to give the player a state. They couldn’t just be a ball, sitting there perfectly still anymore. If they were walking right, they had to be in a ‘walking right’ state, so the correct animation could be played.

I ended up with the player object having a private definitions variable (which is actually a terrible method name, should have gone with animationFrameDefinitions) with 8 objects, each describing where the frame was.

// add sprite data
var definitions = {
    'eastStandingStill': {'frames': [[0, 2], [1, 2]]},
    'eastWalking': {'frames': [[0, 1], [1, 1], [2, 1], [3, 1]]},
    'westStandingStill': {'frames': [[3, 11], [4, 11]]},
    'westWalking': {'frames': [[1, 10], [2, 10], [3, 10], [4, 10]]},
    'northStandingStill': {'frames': [[0, 5], [1, 5]]},
    'northWalking': {'frames': [[0, 4], [1, 4], [2, 4], [3, 4]]},
    'southStandingStill': {'frames': [[0, 8], [1, 8]]},
    'southWalking': {'frames': [[0, 7], [1, 7], [2, 7], [3, 7]]},
var playerSprite = new sprite('/img/clotharmor.png', {'height': 32, 'width': 32}, definitions);

Each element of the frame are coordinates for where the next frame is. I think this might be a limitation that will quickly creep up on me. What if the frame isn’t the same size as all the others? A low hanging fruit of optimisation when loading images over HTTP is to merge many images into one, so you make only one request to the server instead of one per image. In my design, I can’t do that.

I should have paid more attention to BrowserQuest’s way of doing it. They don’t have this problem because their animation class doesn’t actually know anything about the image (it’s not given a reference to the image), and it doesn’t handle any of the drawing to the canvas. Its sole job is to decide which pixels should be drawn (and when it should switch to the next frame). Whereas I have a sprite object which assigns a height and a width for each frame within the sprite, BrowserQuest has “animations”, which start at an x,y on an image, each frame as a height and width, and there’s a certain number of frames on that line. So now, there can be many mixed frame sizes within a single image.

There is a sprite class which does know about the image. It is what points to the image we want to select our sprites from. It’s in here that we can see that all the sprite definitions are kept in individual files (I guess that makes it tidier), and are brought in using require.js.

Also, something I’d not thought, each animation can run at different times. I might want to slow down the how long a frame stays on the screen for before moving to the next. That’s easily set in this animation object.

Even in sprite.js though, very little drawing is actually done to the canvas.

That job lies with the render class.

In my version I definitely want to follow in those footsteps. Whilst I have one sprite.js doing all the drawing, BrowserQuest uses three different scripts to split responsibility, and is more flexible because of it.

The actual triggering of the drawing lies in the gameloop’s tick(), like mine does, but they’ve gone with using using requestAnimationFrame, which means the frame rate is left up to the browser (who knows much better than me how quickly a redraw is possible).

I think that is about it for animation so far. I’ve learnt a lot by reviewing BrowserQuest’s code and seeing why some of the ideas they’ve used are better than my own first try. I’m planning on continuing reviewing my code, next looking at event handling.

One thought on “Looking at BrowserQuest’s Animation Handling”

Leave a Reply

Your email address will not be published.