Adventures in Unity – 2.3 Palette Swap

Official_unity_logo

 

I wanted to try using textures based on the original games graphics.

Mainly for two reasons;

  1. 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.
  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.

 

PaletteSwappedA1

 


 

After a quick google I figured I had three options for replicating the spectrum level  based palette swap;

  1. 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.
  2. 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.
  3. 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.

TileGoalLSpectrum

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.

 

PaletteSwappedStartA1

 


 

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;

TextureAdvancedA

 

2. In the ‘inspector‘ tab, change change the texture’s ‘Type‘ to ‘Advanced‘.

TextureAdvancedB

 

3 Next, put a tick in the read/write enabled check box.

TextureAdvancedC

 


 

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 };
     }
}

 


 

PaletteSwappedGoalA1

 

& 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…

  1. Once the level data is loaded, identify which tile types will be used for the level,.
  2. Load the the textures for the identified tile types – change their colours and store in an object.
  3. Now, when instantiating a new tile instance, I attach the relevant pre-cooked texture.

 


 

Next post: 2.3 Edge Padding

Last post: 2.2 Framework

Contents page.

 


 

Advertisements

2 thoughts on “Adventures in Unity – 2.3 Palette Swap”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s