I wanted to try using textures based on the original games graphics.
Mainly for two reasons;
- Because I wanted to see how it would look – If it works out, I have some low impact textures for the WebGL version of the game – If not, then, at least I’ll still have reason #2.
- I thought they would work well as placeholders – until this point, I’d just coloured different tile types different colours – & while that’s fine when I only have 2-3 types, once the numbers start stacking up it’s going to get confusing remembering which tile colour has what effect – Using the original graphics as placeholder textures ensures they’ll look unique & easily distinguishable – It’ll make it faster & easier when trying to see how the original game works and feels against my implementation – & makes creating the textures much easier & far less time consuming.
However, since I never seem to like making anything too easy for myself, I decided to create textures based on both the C64 & Spectrum versions of the game.
The Sinclair ZX Spectrum 48k handled graphics in a rather ‘unique’ way- The 256×19 screen was broken down into 8×8 pixel blocks – Each block can display two colours at any one time – background & foreground (paper & pen)
While this architectural design caused lots of headaches when developing video games – With bounder, it allowed the developers to base each level on a different colour pair – so they could use the same base graphic for each tile type – But by changing the colours used to draw the tiles, it gives each level a distinct feel.
Effectively palette swapping. As Wiki explains;
A palette swap is a practice used in video games, whereby a graphic that is already used for one element is given a different palette, so it can be reused as other elements. The different palette gives the new graphic another set of colors, which makes it recognizably distinct from the original.
After a quick google I figured I had three options for replicating the spectrum level based palette swap;
- Use a different texture for each level. This, given my requirements, would be the quick to implement, be low on systems resources and could be implemented cleanly.
- Write a shader to swap the colours at runtime – Another sensible, relatively easy option – & since I’ve yet to play around with shaders in Unity it would have been a useful intro.
- Edit the textures and change their colours at runtime – When I’ve tried this sort of thing in the past, it’s usually come with a huge processing overhead – impacting game performance needlessly – probably the worst option of the three.
I went for approach #3.
With this game, palette swapping is pretty straight forward;
The projects textures contains a maximum or four colours; black, white, green and blue (most only contain black & white) – e.g.
Since there are so few colours, all of which are known beforehand, identifying and swapping is pretty simple.
The code provided below is specific to this,simple colour swap – but should be easily modifiable to accommodate more complicated requirements.
First we need to make the original (source) texture read/write enabled. This is so we can run though the texture and read the colour of each pixel. This can be done within the Unity IDE in three steps.
1 In the ‘project‘ tab, select the texture you want to use;
2. In the ‘inspector‘ tab, change change the texture’s ‘Type‘ to ‘Advanced‘.
3 Next, put a tick in the read/write enabled check box.
Now the ‘source’ texture is ready, we need some code to perform the swap.
I’ve written two methods for swapping the colours -Both are displayed below – There’s not a lot between them – cut & paste whichever one you prefer.
Method 1:
Based on the code provided by Scribe in answer to a palette swap question on the unity community answers…
This method requires a source texture (the read/write enabled texture created earlier) and an array of colours – These are the level specific colours which will replace the source colours.
It copies the textures pixels into a Color array – Then loops through the color array, checking the colour of each pixel and replacing as required.
Once all the colours have been checks & changed, it sets the image into the destination texture & importantly calls Apply() to save the updated texture.
Texture PaletteSwap(Texture2D source, Color[] toColours) {
//Renderer rend = GetComponent();
//Texture2D source = rend.material.mainTexture
Texture2D destination = new Texture2D(source.width, source.height);
//---
Color[] pixels = source.GetPixels(0, 0, source.width, source.height);
int pixelsLength = pixels.Length;
for (int counter=0; counter< pixelsLength; counter++)
{
if (pixels[counter] == Color.white)
pixels[counter] = toColours[0];
else if (pixels[counter] == Color.black)
pixels[counter] = toColours[1];
else if (pixels[counter] == Color.green)
pixels[counter] = Color.black;
else if (pixels[counter] == Color.blue)
pixels[counter] = toColours[2];
}
destination.SetPixels(0, 0, destination.width, destination.height, pixels);
destination.Apply();
//---
//rend.material.mainTexture = (Texture)CopyTexture2D((Texture2D)rend.material.mainTexture);
return (Texture)destination;
//---
}
Method 2:
Based on code provided by CarterG81 in answer to a palette swap question on the unity community forums…
This method uses two while loops (y & x) to get the coordinates of each pixel in the source texture in turn – It then checks the retrieved pixels colour against the swap list – writing the replacement colour directly onto the destination texture.
Once the loops complete, it calls the all important Apply() method to create the new destination texture.
Texture PaletteSwap(Texture2D source, Color[] toColours)
{
//Renderer rend = GetComponent();
//Texture2D source = rend.material.mainTexture
Texture2D destination = new Texture2D(source.width, source.height);
Color pixelColor;
int y = 0;
while (y < destination.height)
{
int x = 0;
while (x < destination.width)
{
pixelColor = source.GetPixel(x, y);
if (pixelColor == Color.white)
destination.SetPixel(x, y, toColours[0]);
else if (pixelColor == Color.black)
destination.SetPixel(x, y, toColours[1]);
else if (pixelColor == Color.green)
destination.SetPixel(x, y, Color.black);
else if (pixelColor == Color.blue)
destination.SetPixel(x, y, toColours[2]);
else
Debug.Log("Color.r: " + source.GetPixel(x, y).r + " Color.g: " + source.GetPixel(x, y).g + " Color.b: " + source.GetPixel(x, y).b);
++x;
}
++y;
}
destination.Apply();
//Apply the texture to the material
//rend.material.mainTexture = (Texture)CopyTexture2D((Texture2D)rend.material.mainTexture);
return destination;
}
The colour swap list is defined in a cheesy little method which, when passed the current level number, returns a array colors for that level;
Color[] LevelColours(int inLevel)
{
switch (inLevel)
{
case 1: return new Color[] { Color.white, Color.black, Color.magenta };
case 2: return new Color[] { Color.yellow, Color.blue, Color.magenta };
case 3: return new Color[] { Color.cyan, Color.red, Color.cyan };
case 4: return new Color[] { Color.cyan, Color.black, Color.red };
case 5: return new Color[] { Color.green, Color.black,Color.white };
case 6: return new Color[] { Color.white, Color.red, Color.blue };
case 7: return new Color[] { Color.yellow, Color.red, Color.white };
case 8: return new Color[] { Color.white, Color.black, Color.yellow };
case 9: return new Color[] { Color.yellow, Color.blue, Color.white };
case 0: return new Color[] { Color.cyan, Color.red, Color.blue };
default: return new Color[] { Color.red, Color.yellow, Color.white };
}
}
& that’s it.
I’m currently swapping the texture palette every time I instantiate a new tile – & while It seems to work with no noticeably slowdown/jerkiness, it’s impractical as a long term solution.
My next step will be to instantiate everything at the beginning of the level…
- Once the level data is loaded, identify which tile types will be used for the level,.
- Load the the textures for the identified tile types – change their colours and store in an object.
- Now, when instantiating a new tile instance, I attach the relevant pre-cooked texture.
Play the game (WebGL)
Next post: 2.3 Edge Padding
Last post: 2.2 Framework
Contents page.