There are a number of techniques or creating interesting and varied sprites. Today we’re going to explore using SVG to programmaticlly create several variations of the one image.
Creating Sprite Variations
SVG is a vector image format that uses lines and shapes to create images. It’s XML based format, which means we can edit the properties of the image using an XML parser, or even using basic string manipulation to change the image on the fly.
What this means is that we can easily customise the colours or gradients in a game sprite by using as little as a basic search and replace operation. This might be used in a number of circumstances, be it customising your character or just injecting some variation into your game scenery.
Today’s workflow will look like this:
- Load SVG file as a string.
- Replace all mention of a particular colour with a new colour.
- Render the SVG image to a Canvas context.
- Use the rendered context as you would any other image.
Here’s a SVG version of one of our game characters, but let’s say we wanted to allow the player to customise their colour.
If you open your SVG image in your text editor, you’ll quickly notice that the format is very similar in concept to normal HTML and CSS. A normal SVG is generally constructed with paths, shapes, and regular text elements (see the MDN site for a full list of SVG elements). You should be able to right click on the image of the ninja to view the source of it an get a feel for what’s going on.
Here’s an example path from our ninja pic. The d attribute specifies the coordinates the path follows, and the style attribute allows css-like styles to be applied to the element. Here we have set a red background colour (#ff000) in hexadecimal notation:
<path d='m 204.146,150.31198 c 0,41.73 0.5,88.463 -8.5,127.998 -66,-28.553 -63,-28.553 -63,-94.447' id='path51' style='fill:#ff0000' />
You might realise straight away that we can do all kinds of things with this image format, notably here we could replace all the instances of the colour red with any colour of our choosing.
The following code will search for any reference to the colour red, and replace it with the colour blue throughout the entire document.
var svgNinjaRed = '<svg […]>'; // This variable contains our SVG file. var svgNinjaBlue = svgNinjaRed.replace(/#ff0000/g, '#0000ff');
Once we’ve run a quick search and replace through the SVG file, all instances of the colour red have been replaced with blue.
It really is as simple as that for basic operations, although we could create much more advanced templates to add accessories, change features of the character or update textual values.
Once we’re done, we can render the final SVG to a canvas context and use it in our game.
SVG to Canvas Rendering
Most modern browsers have native support for loading SVG images in the DOM, but instead we’re going to use a third party library called canvg to render the SVG image to a Canvas buffer instead.
Fortunately the syntax for canvg is quite simple, and it can take our SVG string as a parameter:
// Create our Canvas element var canvasNinja = new document.createElement('canvas'); canvasNinja.width = 240; canvasNinja.height = 320; // Get drawing context var canvasNinjaContext = canvasNinja.getContext('2d'); // Render Ninja to canvas canvasNinjaContext.drawSvg(svgNinjaBlue,0,0);
The SVG image has now been rasterised for use in our canvas game.
There are a number of interfaces to canvg, but the drawSvg method is the most familiar because it uses the same syntax as the native canvas drawImage. It’s also worth noting that you should render your SVG to a separate Canvas instance before displaying it on the screen as the canvg library isn’t very fast. As complex images can take several seconds to render, keeping a pre-rendered copy to use with drawImage is a necessity.
Putting it all together
Once we’ve put all this together, we end up with a pretty sweet script that can change colours of SVG sprites on the fly.
I’ve created a demo showcasing this technique, which lets you see the technique in action. You are free to use it for your own purposes.