Adventures in Unity – 2.5a Palette Shift

Official_unity_logo

 

For the C64 version of Bounder, the text colour of the start and end tiles bounces between black and white.

Start-2a2

Goal-2a2

 

I wanted to replicate this in my version – But I wasn’t sure how.

I eventually worked through six potential implementations;

  1. Colour Lerp.
  2. HBSC
  3. Palette Swap
  4. Quad Cut-Out
  5. Shader
  6. Texture Sheet Animation.

 

This post covers the first three attempts;

  • Colour Lerp.
  • HBSC
  • Palette Swap

 

The next post will expand on the final three.

  • Quad Cut-Out
  • Shader
  • Texture Sheet Animation.

 


 

1. Colour Lerp

The most straight forward approach; it uses the built in method color.lerp alongside Mathf.PingPong to bounce colours between black & white – The result is applied to the start/end textures.

How this works…

The Color lerpedColor holds the colour to be displayed.

public Color lerpedColor = Color.white;

Every loop, color.lerp is used to calculate the current colour – Mathf.PingPong ‘animates’ the colour – bouncing it between (in this case) black & white.

lerpedColor = Color.Lerp(Color.white, Color.black, Mathf.PingPong(Time.time, 1));
GetComponent().material.color = lerpedColor;

The end results look great; but unsurprisingly the entire texture changes colour, not just the text.

 

ColourLerp-1b

 


 

2. HBSC

Using the HSBColor class, provided in the Unity3D wiki.

The wiki description explains that HSBColor ‘provides a Hue/Saturation/Brightness/Alpha color model in addition to Unity’s built in Red/Green/Blue/Alpha colors. It is useful for interpolating between colors in a more pleasing fashion

Rather than following their usage/setup instructions, I instead just stripped the [System.Serializable] tag & used the code like a standard class.

How this works…

Define a global HSBColor object.

public float time = 2.0f;
private HSBColor hsbc;

Which is initialized in the start method.

hsbc = HSBColor.FromColor(color);

Then, every loop the next colour to display is calculated (I think it rotates through six different colours)

hsbc.h = (hsbc.h + Time.deltaTime / time) % 1.0f;
GetComponent().material.color = HSBColor.ToColor(hsbc);

The result may give you a headache;

HSBC-1a

 


 

3. Palette Swap

This reuses the Palette Swap code I wrote for the Spectrum tiles – I really like the effect – (Though I’d need to figure out why some white pixels have turned yellow when being displayed). I’d expected a huge performance hit with this method, but when tested, the game ran at a good pace.

How this works…

Each tile prefab has a material component attached. Each material component has a texture attached. This is the texture used when drawing the tile to screen.

Since the intention is to change the colours of the materials texture, the script keeps a master copy that is unaffected by the paletteSwap code – paletteSwap code uses this master copy as a reference every loop/colour update.

(Rather than using a reference textures; the script could instead, store a copy of the last colour applied and pass that to the paletteSwap code to change every loop – Unfortunately the banner letters lerp between black and white – & the texture has some black pixels in place which may accidently get caught up in the animation).

 

First the script defines a global texture to be used to hold the master/base texture;

Texture2D paletteSwapTexture;

A copy of this texture is assigned to paletteSwapTexture – (I’m guessing it’s a copy and not a reference, since changing the colours in rend.material.maintexture has no effect on the contents of paletteSwapTexture)

rend = GetComponent();
paletteSwapTexture = (Texture2D) rend.material.mainTexture;

The ‘toColour‘ colour array is defined – This is used to identify which colour in the texture to look for (index 0) & what colour to change it to (index 1).

Color[] toColour = new Color[2] { Color.white, Color.white };

Then, every loop ‘Colour Lerp’ (& Mathf.PingPong) calculates the next colour to display – This is then passed to the PaletteSwap method.

toColour[1] = Color.Lerp(Color.white, Color.black, Mathf.PingPong(Time.time, 1));
Renderer rend = GetComponent();
rend.material.mainTexture = PaletteSwap(toColour, paletteSwapTexture);

Which swaps the colours and returns a new Start/Goal tile texture;

Texture PaletteSwap(Color[] toColours, Texture2D inTexture)
{

     Texture2D source = inTexture;
     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 = 0.98 && pixels[counter].g >= 0.99)
          pixels[counter] = toColours[1];

     }

     destination.SetPixels(0, 0, destination.width, destination.height, pixels);
     destination.Apply();

     //---

     return (Texture)destination;

     //---

}

Which ultimatly looks like this – Nice effect, pain in the arse yellow pixels;

PaletteSwap-1b

 


Play the game (WebGL)


 

Next post: 2.5a Palette Shift

Last post: 2.4 Edge Padding

Contents page.

 


 

Adventures in Unity – 2.4 Edge Padding

Official_unity_logo

The levels in this project are made up of tiles – I’d working with about four different tile types to when constructing the basic game framework;

 

TileFloorA
1. Default Floor Tile
TileBonusA
2. Bonus Tile
TileStartA
3. Start Tile
TileGoalA
4. Goal Tile

 

The floor tiles looked great – But the start, goal & bonus tiles were a little out of place & had seams showing;

Seams1

 


 

Googling around, I discovered I wasn’t the first person to have this issue – & everyone seems to have their own resolution…

Some people offered sensible first steps advice, such as ensuring the in the texture setting clamp rather than repeat was selected – Others suggesting ideas like merging the tiles into one model, drawing with a custom shader, deleting all offscreen model faces, recalculating the models normals, turning off anti aliasing, turning off mip-mapping, etc..

After trying, a failing with quite a range potential solutions; what eventually worked for me was edge padding.

Edge padding, as the Unity docs explain;
Unity renders a scene using a process known as downsampling which uses texture filtering to smoothly render the textures. If the ‘gutters’ (blank areas between UV’s) have colors/transparencies that are very different from the colors inside the UV’d areas, then those colors can ‘bleed’ together which creates seams on the model. This problem will also occur when neighbouring UV shells have different colors; as the texture is downsampled eventually those colors start to mix.

To avoid this problem, edge padding should be added to the empty spaces around each UV shell. Edge padding is the process of dilating the pixels along the inside of the UV edge to spread the colors outward, forming a border of similar colors around the UV shell.

 


 

UV Mapping is the process of projecting a texture onto a 3D object – It identifies what area in a texture/image a model faces uses as it’s texture.

So, for example, in the image below the right side shows a model with one face selected (outlined in orange & shaded slightly) .

The left hand side shows the texture being used – The orange square (on the top left quarter) shows what part of the texture will be used to ‘paint’ the select face

UV Mapping 2

 


 

In my case, my original textures looked like this;

TileBasicX1B

My uv texture mapping looked like this;

TileBasicX2B

The problem was the UV areas I’d setup were too tight – so when Unity rendered the textures onto the models, it would sometime collect colour information outside my defined UV area – causing seams to be displayed.

The fix was to reposition the UV mapping & add a border to accommodate the potential bleeding effect.

TileBasicYB
My initial attempt at edge padding resolved 90% of the problem – but I found the enter/exit tiles didn’t quite match up.

Seams2

 


 

For ‘complex’ textures, instead of continuing that edge colour, I instead just continued the pattern.

Instead of using this;

TileBasicYB

I setup the texture to look like this;

TileBasicZB

Which worked perfectly;

Seams3

 


 

Note: My textures & UV mapping are pretty simple- I used that to my advantage when edge padding – padding far more of the texture than I really needed to.

The amount of padding actually required depends on the size of the texture used – (I think the smaller the texture, the higher the chance of seams)- For this game, my textures are 64×64 – I found I needed a 3-4 pixel buffer to completely remove seams.

 


 

Ultimately, edge padding provides a satisfying fix – going forward, as long as I keep padding in mind when creating textures, it should have almost no further impact on this project.

 


Play the game (WebGL)


 

Next post: 2.5b Palette Shift

Last post: 2.3 Palette Swap

Contents page.

 


 

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.

 


Play the game (WebGL)


 

Next post: 2.3 Edge Padding

Last post: 2.2 Framework

Contents page.

 


 

Adventures in Unity – 2.2 Framework

Official_unity_logo

 

I’m repurposing project 1 for this game – rather than starting from scratch and reusing elements I’m editing the existing code – which is never a good idea; but one I find hard to resist.

I wasn’t completely happy with the game structure with project 1 & wanted to take a stab at reviewing and updating – Hopefully improve things a little. The new version isn’t perfect, but I think it’s an improvement.

It’s based almost entirely on using enums to define ‘states’, managers to switch between states (e.g. GameStateManager & GamePlayManager) & elements which implement different code/functionality depending on the current state.

I’ve outlined each class/element in a bit more details below;

 


 

GameStateManager: As with Project 1 (Block Runner) – The game state manager looks after the top level game state – That is, are we on the title screen, highscore screen, playing the game, etc… Top level GSM stuff. It’s still overloaded; but I’ve begun simplified things a little – Game Over is now managed by GamePlayManager (though at the end of a game the GameStareManager still checks to see if the player has a new highscore).

GamePlayManager: Looks after the game state ‘in-game‘ – It’s concerned with things like; initializing the level, displaying the level intro message, playing the game, displaying end of level messages & handling gameover, etc.

Player: Modified from Project 1‘s player class – This is looks after the current state of the player – Are they entering the level, or exiting, playing the game or have they fallen off a tile? It also managers the player input and updates player input accordingly.

A notable aspect is the way player deaths are handled – Once a player is killed, the Player class looks through all the current tiles on screen and identifies all safe/standard tile types (only checking against the top half of the screen) – It then randomly chooses one to be used as the spawn point for the player re-entering the level. If there are no safe tiles currently active, the level will continue to scroll until a safe tile becomes available.

TileManager: Based on the ‘PlatformManager‘ class from Project 1 – Unlike the classes above, this isn’t managed by state/enum.

Each level is made up of tiles – Tiles create the path the player must travel through in order to reach the end of the level. It’s the environment the player exists in while playing the game – The meat of the game.

There are lots of different types of tiles used in the game – e.g. normal/standard tiles act like platforms in a standard platform game – players can safely bounce onto/off-from them as often as they like – Arrow tiles allow the player to bounce twice as far (avoiding obstacles or large gaps in the level), walls block the players path, lava tiles kill the player on collision etc…

The TileManager class manages these tiles – Instantiating, positioning, scrolling and ultimatly destroying tiles.

For the tiles themselves, I’ve followed the example given in the Space Shooter demo code – Each tile type is defined as a prefab; these are plugged into the TileManager class as generic GameObjects – This allows me to easily manage the tile types with the TileManager class, whilst allowing each tile to maintain it’s own personality.

At the start of each level, the TileManager class loads a (text) map of that level. It uses this map to populates a two-dimensional array of GameObjects (Tiles) – This builds the level.

Since levels can be pretty large, so I don’t want to have ALL the tiles instantiated & active at one time – It’ll soak up far too much memory. So the array only holds the tiles which are onscreen at that time (with a little offscreen buffering to smooth things out).

Each loop TileManager updates the position of each tile in the array – scrolling them down the screen – Once a row has moved off the bottom of the screen; TileManager reads the next line from the level map; populates the offscreen row with the new tiles & repositions them at the top of the screen, ready to scroll down and confound the player.

Scoreboard: Currently still fairly undefined – The relevant fields are in place (score, hiscore, lives, jumps) – & I’ve started hooking them up to the game code – But I’m not particularly happy with the way it currently looks in game – So I’m intending to finish connecting to the game data (hiscores are proving a pain) – But then leaving all the heavy lifting (animations, transitions, etc) – Until I decide how I want it to actually work.

 


 

There’s a couple of important areas still undefined; the background & enemies.

Background: On the C64 version of the game the background seems to be created out of tiles (as with the top layer) with generic grass, mud, water type images – Using parallax scrolling to give the impression of depth.

Enemies: From what I can gather enemies in the original bounder are made up of two classes/types;

Type 1. Enemies which operate in the expected manner – moving around the screen, needing to be avoided by the player.

Type 2. Enemies which sit on tiles, acting either as one-touch kill obstacles – or in a more esoteric manner e.g. warp enemies, which, upon collision, transport the player to another (safe) part of the screen.

With my current setup, I’m more inclined to class type 2 enemies as tile types.

Though I’m intending to wait until I’ve setup a wider range of tiles before I consider this further.

 


 

A short video demonstrating the current framework…

 

 


 

For now that’s it.

For my next step I’m intending to start defining & adding tile types to the game – getting their functionality defined & making them look a little more interesting/unique.

So, first I need to figure out what different tile types there are & how they operate.

Below is a quick video demonstrating how things are at the moment

 


Play the game (WebGL)


 

Next post: 2.3 Palette Swap

Last post: 2.1 Introduction

Contents page.

 


 

Adventures in Unity – 2.1 Introduction

Official_unity_logo

My second project with unity.

For this, I decided, rather than expanding project 1 (something I’d like to do in the future) – I’d write a completely new game.

I wanted something a little larger – But still keeping the scale ‘reasonable’ – So I can learn & play while I develop.

Eventually setting on a remake of ‘Bounder‘ – a Commodore 64 game released by Gremlin Graphics in 1985.


Bounder_c64_cover


The cassette inlay explains;

Move Bounder ( tennis ball ) around screen, bouncing on hexagonal
slabs only.  If you miss, you fall to your death. Be sure to
identify mountains and walls as you can't go over them, you must
go around. Any collision means you lose  a life.

As a rule: IF IT ISN'T HEXAGONAL, THEN AVOID IT!

(Note – slabs are in fact rectangular with a hexagonal design)


This long play video demonstrates C64 gameplay;


It’s worth noting that Urbanscan a company owned by Ian Stewart (one of Gremlin Graphics founders) – Has released a new version of Bounder on the app store.


Putting this together, I’m intending to try and re-use projects 1’s structure\GSM – maybe tidied & tightened a little – Which will hopefully allow me to focus more on the game itself.

Ideally I’d like to build a more conservative version first – which I can release as a WebGL game. Which I can then expand upon to create a PC version – Allowing me to add 3D models/animations, more involved shaders, etc… Stuff I havn’t really implemented so far… Also allowing me to setup & try out the type & range of configuration options PC gamers expect to see.


Rather than updating every week – This time I’ll try to update every milestone (working on this part time this will more time between blog entries; but hopefully they will be more meaningful when I do publish them).

Lets see how this goes 🙂


Play the game (WebGL)


Next post: 2.2 Framework

Contents page.


Adventures in Unity – 1.9 Tighten & Tidy

Official_unity_logo

With the inclusion of Obstacles; there isn’t anything else I really wanted to add to this project.

It still needed a lot of work; pulling all the elements together and polishing the presentation – One final Tighten & Tidy.

But I felt I’d done what I set out to achieve at the start.

I’ve included notes for the larger updates and amendments below – & while there’s a lot more that could be done (especially with the code structure) – I think this will be it for project 1.

 


 

Particles – This is a very simple game, and could sometimes look a little flat. I added a small number of collision particles (platform collision, pickup collision, obstacle collision) to hopefully make the game more visually appealing.

particles

 


 

Standardised Colours – For better or worse I’ve used the same colour scheme for all my games since EOL back in 2010.

To standardise the same colours across the game  I created a script called ColourManager. This contains a number of static methods to manage element colours;

GetColour returns a specific colour, used by HighScoreAdd to manage the keyboard key colours.

SetColour used to set the colour of a components renderer. Used to set the colours of platforms, obstacles and pickups

CurrentColour & NextColour used by platforms and titlescreen text to loop through each colour in rotation.

 

colour_range

 


 

Ensure player isn’t killed at game start – Until now, at the game start; a player would sometimes immediately hit the edge of a platform and fall out of the game – Ending the game before it had begun. To fix this I made a very minor edit to the PlatformManager code so that the very first platform is much longer – Giving the player a safe entry to the game before different platform heights are introduced.

 


 

Ensure player managed when not in-game –  On the title screen (and game over screen) the player components ridgedbody element was still being updated – If a player left the title screen running for any length of time, they would be subjected to mysterious platform colour changes & collision sound effects being played seemingly randomly. To resolve this, I now only update the player if in-game (if statement in the players Update method) – Otherwise I reset them to an offscreen position and reset the velocity

     void ResetPlayer()
     {
          rb.transform.position = new Vector3(0, 7.5f, 0); //Set game init position
          rb.velocity = Vector3.zero;
     }

 



 

Set Alternate Game Name – Changed the title screen to say 20 goto 10 presents Bock Run. It’s not a great name, but seemed a little better than Welcome to Test Game (Colours are randomly determined on screen init).

 

GameTitle

 


 

In-Game Scoreboard – I simplified the presentation of the scoreboard by only displaying the players score. Though I wanted the score to be shown behind the player (but in front of thr skybox), so I complicated the back end. Adding a new canvas (called CanvasScore), plugging in the Main Camera changing the render mode to ScreenSpace – Camera

CanvasScore

 


 

SkyBox – I liked the default skybox, but I thought I should at least try something a little different. Ultimately I settled on the Fantasy Skybox FREE by G.E.TeamDev  – Stripping out all the content except the Sunny 01A material and Sunny_01A_Front texture.

Sunny_01A_front

 


 

Player Death Pause – Colliding with an obstacle, results in the player exploding in a cloud of particles – game over. Previously, after creating an instance of explosion particles the game went straight to either the ‘new highscore‘ or ‘game over‘ screen letting the particles play out in the background. While this didn’t look too bad, I wanted to add a slight pause – Long enough to let the player appreciate what had happened – But not so long as to be distracting – I guessed about a second would do the job.

My quick and dirty way to add this, was to create a new game states ‘GameOverPause‘ which would be set only if the game over state was a result of obstacle collision. When setting  ‘GameOverPause‘, a timer is run as a coroutine;

IEnumerator GameOverPauseTimer()
{
     yield return new WaitForSeconds(1);
     HighScoreCheck();
}

Once the timer completes HighScoreCheck() is called and the games next state is detemined – ‘New Highscore’ or ‘Game Over’

 


 

Background scrolls smoothly when transitioning from game over to game start – At the moment, when a new game is started, I reload the scene – start everything from fresh. I wasn’t sure if this was a little jumpy & liked the idea of the background blocks continually scrolling, whatever the game state (game/game over/new highscore, etc…). No reset or reloads – when the game ends, the background keeps scrolling – when the player presses play they fall straight into the scene whatever state it’s in. I ultimately decided not to go this way (Because of ‘Ensure player isn’t killed at game start‘), but the code is still in place & can be activated, either in-code by setting the ‘bool reloadLevel‘ (in the GameStateManager class) to false – Or in-game by pressing ‘R‘.

 


 

Multiple obstacle types – spheres bounce, blocks burst – Expanding obstacles a little – As well as the instant death obstacles, I added new type – using the ObstacleSphere prefab – bounce. Both obstacle types use the same script, the only change is the addition of a enum used to determine obstacle type;

public enum ObstacleType { Cube = 0, Sphere, Pyramid }
public ObstacleType type;

& a little code to handle the different collision responses – Cubes & pyramids use the old/existing – instant death code – While spheres ‘bounce’ the player uncontrollably

void OnCollisionEnter(Collision collision)
{

     if (GameState.Is(GameState.State.InGame) && collision.gameObject.CompareTag("Player")) //Ensure we are checking aginst the player.
     {

          Player player = collision.gameObject.GetComponent(); //Grab the player

          if (type == ObstacleType.Cube || type == ObstacleType.Pyramid)
               player.PlayDie();

          else if (type == ObstacleType.Sphere)
               player.Bounce();

     }

}

Bouncing just increases the players Y velocity;

public void Bounce()
{

     float bounceAmount = 6f;
     rb.velocity = new Vector3(rb.velocity.x, Mathf.Abs(rb.velocity.y) + bounceAmount, rb.velocity.z);
     audioBounce.Play();

}

 


 

Controller support – Support for different input methods was pretty spotty – some screens recognised mouse and keyboard input; others only mice. – I wanted to ensure all screens could be controlled by either keyboard or mouse, as well as gamepad (No touch input yet though).

The setup as it currently stands is a little rough, clearly a first draft which could  benefit from a review/rewrite or two. It’s a static class based around four methods – Two methods Horizontal & Vertical check for player movement requests  & two methods ButtonsDown & Buttons to check for player action requests (jump, select key, button, etc).

Companion axis methods Left/Right – Up/Down – HorizontalOff/VerticalOff use the results of Horizontal  & Vertical to provide reference to the current state (e.g. is the player trying to move left) without needing to rerun Horizontal & Vertical more than once a frame. Used to navigate the the virtual keyboard and main menu buttons.

Companion button methods e.g ButtonADownButtonBDownButtonShoulderLDown are used to reference specific button selections.

Horizontal()  & Vertical() effectivly both work in the same way, just along different axis. They are run every loop; checking for user input using a valid method (left thumbstick, right thumbstick, arrow key, etc) and return a result value; -1 for left/down, 0 for no input  & 1 for right/up.

public static float Horizontal() //Is the player using horizontal movement
{

     lastHorizontal = currentHorizontal;

     if (Input.GetKey(KeyCode.LeftArrow) || ZoneCheck(ZoneRange.Left, Input.GetAxis("Horizontal")) || ZoneCheck(ZoneRange.Left, Input.GetAxis("HorizontalThumbstickR")) || Input.GetAxis("HorizontalDPad")  0)
          currentHorizontal = 1;

     else if (Input.GetKeyUp(KeyCode.LeftArrow) || Input.GetKeyUp(KeyCode.RightArrow) ||
                    (currentHorizontal != 0 && ZoneCheck(ZoneRange.Centre, Input.GetAxis("Horizontal")) && ZoneCheck(ZoneRange.Centre, Input.GetAxis("HorizontalThumbstickR")) && Input.GetAxis("HorizontalDPad") == 0))
          currentHorizontal = 0;

     return currentHorizontal;

}

Buttons() checks all valid input methods (gamepad buttons, space bar, return key, etc) using the unity function GetButton – To check if the button is currently being pressed -Used for in game jumping.

public static bool Buttons() //Any button pressed
{

     if (Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.Space) || 
            Input.GetButton("JoystickButton0") || Input.GetButton("JoystickButton1") || Input.GetButton("JoystickButton2") || Input.GetButton("JoystickButton3"))
          return true;

     return false;

}

ButtonsDown() checks all valid input methods (gamepad buttons, space bar, return key, etc) using the unity function GetButtonDown – To check if the button was pressed in the last frame. Used when the user selects a button from the Main Menu.

public static bool ButtonsDown() //Any button pressed
{

     if (Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.Space) || 
            Input.GetButtonDown("JoystickButton0") || Input.GetButtonDown("JoystickButton1") || Input.GetButtonDown("JoystickButton2") || Input.GetButtonDown("JoystickButton3"))
          return true;

     return false;

}

 


 

Split GameController into multiple classes – The GameController script was heavily overloaded – It managed the games states, title screen buttons, gameplay loop, player score, game timer, scroll speed, in-game music. While I suspect it really needs to be completely re-worked, for the moment I spread the functionality over three classes;

GameState – Really just holds & manages an enum used to determine the games current state (title screen, game over screen, in-game, etc) and the games last state.

GameStateManager – Is still rather overloaded since it does a lot more than just manage the GSM – Looks after the title screen buttons, manages highscore add, game over pause – Pretty much everything that isn’t ‘in-game’

InGameManager – Does handle everything in game (platforms, obstacles, player, etc) – It also manages the players score, background scroll speeds, etc.

It’s far from idea & both GameStateManager and InGameManager are still overloaded, but I think it is an improvement.

 


 

Instructions – I added some very simple instructions -To let players know what they can do (e.g. double jump) and how they can do it (keyboard mouse, gamepad). I try to keep instructions as simple as possible – & as free of words as possible – In case the player doesn’t speak English.

GamePad icon made by EpicCoders from http://www.flaticon.com
Mouse icon made by Freepik from http://www.flaticon.com

InstructionsALT

 


 

So, for version 1 at least, I think that’s pretty much everything. It’s far from perfect, but for my first attempt at a Unity game – not a complete failure – I’ve some very basic gameplay, a messy GSM, a working online highscore table and a much greater understanding of Unity.

As usual, a video showing the game, a link to the source code and a link to play the game online (WebGL) can be found below.

Thankyou!

 


Play the game (WebGL)

Grab a copy of the project here


 

 


 

Last post: 1.8 Obstacles

Contents page.

 


 

Adventures in Unity – 1.8 Obstacles

Official_unity_logo

Obstacles are static/non-moving  objects which appear on platforms with increasing frequency as the game progresses.

They are there to provide an extra challenge for the player and give a little more variety to the game.

Should a player collide with an obstacle, it’s instant death – game over – & so should be avoided at all costs.

The code itself it pretty straight forward – (Obstacles are created, they scroll left then are destroyed) – it’s a modified/simplified version of the code written to handle pickups.

 


 

For in-game obstacles I initially wanted to use a pyramid (Tetrahedron) shape – Since these are pointy and seem like the sort of thing a player should avoid. Unfortunately Unity doesn’t provide tetrahedrons as part of the basic 3D objects.

My first attempt at a workaround was to use cubes – Rotating them, and placing them halfway into the platform top – They looked okay-ish; but didn’t quite work out.

My second attempt was to use spheres – These looked much better – I nearly stuck with them – I just wasn’t convinced they looked dangerous enough.

Looking for an alternative – I found an article in Morten Nobel’s Blog detailing how to create a tetrahedron in code (rather than importing a model) – I used the code pretty much as-is – only making two very minor tweaks; 1. To make them self-construct when initialised & 2. To auto build a collision mesh.

Since I’m a big fan of procrastination, the code currently has all three obstacles types in place, setup as prefabs.

ObstacleCube – A 3D object Cube with default settings – implemented as as a prefab. A obstacle c# script is attached (used to manage collision response).

ObstacleCube

ObstacleSphere – A 3D object Sphere with default settings – implemented as a prefab. A obstacle c# script is attached (used to manage collision response).

ObstacleSphere

ObstacleTetrahedron – A custom object. Defined as a prefab, it contains the same components as ObstacleCube and ObstacleSphere – Except that the MeshFilter is empty. In addition it includes two extra C# scripts; obstacle – used to manage collision response & TetraHedron – Used to construct the object at runtime.

ObstacleTetra

 


 

ObstacleManager;

ObstacleManager is a C# script which manages all obstacles in game, covering their entire lifespan (initalisation, update & removal).

The public method AddObstacles is used to instantiate new obstacles (called externally by the PlatformManager script).

Once created they are then add to a list (List obstacles). Each frame the positions of all obstacles are updated.

Once an obstacle moves offscreen it is destroyed by ObstacleManager  to free up space.

 


 

AddObstacles;

public void AddObstacles(Vector3 inPosition, float inWidth)
{

     Vector3 obstaclePosition = new Vector3(inPosition.x, inPosition.y, inPosition.z-4.5f);


     float obstacleHalfWidth = obstacle.transform.localScale.x * 0.6f;
     float halfWidth = (inWidth / 2f)-obstacleHalfWidth;
     obstaclePosition.x = Random.Range(obstaclePosition.x - halfWidth, obstaclePosition.x + halfWidth);

     for (int counter=0; counter<9; counter++)
     {
          obstacles.Add(Instantiate(obstacle, obstaclePosition, Quaternion.identity) as GameObject);
          obstacles[obstacles.Count - 1].transform.position = obstaclePosition;
          obstaclePosition.z += 1;
     }

}

AddObstacles is called by the platformManager script. When called it creates a column of obstacles – Even though, only the centre obstacle is need (since the player doesn’t move on the Y axis) – a row is more visually appealing.

It receives two parameter values inPosition & inWidth. inPosition is the source/base position for obstacles it is about to instantiate. This can be used to add new obstacles to the middle of the platform. This is fine, looks good and works as an extra challenge in-game. However to keep things interesting I wanted to position the obstacles randomly along the platforms X axis. For this I use the inWidth variable.

 


 

Update;

Every loop the position of instantiated obstacles is updated, moving them from right to left; destroying any which move out of the game area;

// Update is called once per frame
void Update()
{

     //---

     if (GameController.gameState == GameController.GameState.InGame)
     {

          //---

          ScrollSpeed(); //set movement speed
          UpdatePositions(); //update platforms positions

          //---

     }

//---

}

//---
//---

//set movement speed
void ScrollSpeed()
{

     speed = 0.1f + (GameController.scrollSpeed * 0.004f);

     if (speed > maxSpeed)
          speed = maxSpeed;

}

 


 

Obstacle;

All obstacles have an attached script -all called Obstacle.

It’s is a simple script containing two methods;

Start – Which sets the obstacles colour after initialisation.

OnCollisionEnter – Checks to see if the player has collided with the obstacles – If a collision has occurred – PlayerDie() method is called.

 


 

Start;

Start defines the colour of the obstacle (currently they are all set to black);

void Start()
{

     //---

     Renderer rend = GetComponent(); //Ensure the obstacle's colour is set to black
     Color whichColour = Color.black;
     rend.material.SetColor("_Color", whichColour);
     rend.material.SetColor("_SpecColor", whichColour);
     rend.material.SetColor("_EmissionColor", whichColour);
     rend.material.SetColor("_ReflectColor", whichColour);

     //---

}

 


 

OnCollisionEnter;

OnCollisionEnter checks for collision with the player – Since obstacles are deadly, if a collision is found, I call the player.PlayDie method;

void OnCollisionEnter(Collision collision)
{

     if (collision.gameObject.CompareTag("Player")) //Ensure we are checking aginst the player.
     {

          Player player = collision.gameObject.GetComponent(); //Grab the player
          player.PlayDie();

     }

}

 


 

That’s pretty much it. I may expand obstacles a little in the future – But for now it gives me a framework to work from.

 


Play the game (WebGL)

Grab a copy of the project here


 

 


 

Next post: 1.9 Tighten & Tidy

Last post: 1.7e Highscores (Add)

Contents page.

 


 

Adventures in Unity – 1.7e Highscores (Add)

Official_unity_logo

Last part for highscores – A method to allow players who have achieved a new high score to enter their names. This probably should have been a simple screen containing two elements an input field and a enter button. However, for whatever reason I decided to make things a little more complex – Adding a virtual keyboard.

The front end for the virtual keyboard is based on the example provided in the Unity Samples: UI package – The back end I put together myself – With the bulk of the code for this section setup for managing user input. It can currently be controlled either by mouse or keyboard – Expanding to allow controller input ‘should’ be fairly straight forward.

The player doesn’t need to use the virtual keyboard to enter their name – They can always click on the input field and type their names directly – So there’s also a little code to ensure that only valid characters are accepted and that the maximum length is not exceeded should this approach be used.

To try and explain how I’ve put this together, I’ve split the description into two sections;

  1. Front End: GUI.
  2. Back End: Code

1. Front End: GUI

HighscoreAdd

HighscoreAdd – an empty component which groups together all the new highscore UI elements.

HighScoreTextTop

HighscoreAddTitle

A text component, used as the title of the screen

Window – Contains the input field and keyboard keys – The bulk of new highscore components.

Inputfield

HighscoreAddInputField

Displays the players name. Contents can be filled either by using the keyboard keys or by direct input. I set the character limit to 15 so that highscore names fit nicely into the highscore table. Although I only want to allow the player to enter alphanumeric I have the input type set as standard – Managing the input in code- the method InputFieldUpdate is called ‘on value changed‘ which checks the character types, only accepting valid characters.’One Edit End‘ calls a second script method ClickDone – This allows the player to press enter after tying in their name to add the new high score – rather than forcing them to click the done button.

Vertical Group – Is the start of the on-screen keyboard. It’s a component that itself just contains a Vertical Layout Group (script) and has as a child the Grid Component.

Grid – Another component setup primarily to manage the shape of the keyboard, is has two layout components attached Grid Layout Group (script) and LayoutElement (script). As children it has 40 buttons – one for each key.

GridCell-X – Forty of these, one for each key. They are setup in pretty much exactly the same way as all the other buttons in the game. An Animator shrinks the button slightly when clicked, and audioSource & event trigger produce a click sound when the key is selected and a relevant method is called On Click  – ClickAlphanumeric method is called for the alphanumeric keys, with individual methods for the space (clickSpace), delete (ClickDelete), caps (ClickCaps) and enter (ClickDone) keys.


2. Back End: Code

The backbone of the keyboard is a script called HighscoreAdd. This is primarily a functional script, primarily containing methods for handling key presses and ensuring only acceptable (Alphanumeric) values are used for the highscore name.

If the player clicks on an alphanumeric keyboard key, it calls the ClickAlphanumeric method.

public void ClickAlphaNumeric(string inValue)
{

     if (inputField.text.Length < inputField.characterLimit)
          inputField.text += inValue;

     UpdateIndex(inValue);

}

This method first checks to see if the input has reached maximum allowed characters as set in the input field, if there’s space, it adds the new character. Before calling UpdateIndex – a method which changes the colour of the button selected – To make the keyboard feel a little more interactive.

public void ClickSpace(Text inValue)
{

     if (inputField.text.Length < inputField.characterLimit)
          inputField.text += " ";

     UpdateIndex(inValue.text);

}

ClickDelete removes the last character entered by assigning the players name text with a substring of itself, starting from position 0 and ending at length-1;

public void ClickDelete(Text inValue)
{

     if (inputField.text.Length > 0)
          inputField.text = inputField.text.Substring(0, inputField.text.Length - 1);

     UpdateIndex(inValue.text);

}

ClickCaps switches characters between upper and lower case. When the player adds a letter by clicking a button, it adds the value of the buttons text/label to the players name. So when switching between upper and lower case, this method loops through each alphabetic button in turn and sets its label to an upper or lower case version of itself. This has the added effect of visually displaying the current case. I also change the colour of the caps button to indicate it’s state – currently red for uppercase and blue for lower case.

public void ClickCaps(Text inValue)
{

     useCaps = !useCaps;

     UpdateIndex(inValue.text);

     if (useCaps)
     {
          lastColor = Color.red;

          for (int counter = 0; counter < buttons.Length; counter++)
               buttons[counter].GetComponentInChildren().text = buttons[counter].GetComponentInChildren().text.ToUpper();
     }

     else
     {
          lastColor = Color.blue;

          for (int counter = 0; counter < buttons.Length; counter++)
               buttons[counter].GetComponentInChildren().text = buttons[counter].GetComponentInChildren().text.ToLower();
     }

}

ClickDone is run when either the DONE button is clicked or the player presses the return key while the input field is focused. This function first checks to see if the player has added a name – If there is no value, or only spaces have been added the generic player name ‘A Nony Moose’ is used instead. Once a player name is set, it passes it to the SetHighScore method in the gameController script to be added to the list of highscores.

public void ClickDone(Text inValue)
{

     //---

     GameObject gameControllerObject = GameObject.FindGameObjectWithTag("GameController");

     if (gameControllerObject != null)
     {
          GameController gameController = gameControllerObject.GetComponent();

          if (inputField.text == "" || OnlySpaces(inputField.text))
          inputField.text = "A Nony Moose";

          gameController.SetHighScore(inputField.text);
     }

     //---

}

UpdateIndex method is called by all Button functions to ensure the game knows which was the last button pressed, and to update the button highlight appropriately (By calling UpdateButtonIndex);

void UpdateIndex(string inValue)
{

     for (int counter = 0; counter < buttons.Length; counter++)
          if (inValue.ToUpper() == buttons[counter].GetComponentInChildren().text.ToUpper())
          {
               index = counter;
               UpdateButtonIndex();
               break;
          }

}

UpdateButtonIndex stores the index value of the last button clicked. It also stores the last colour of the button (its unpressed colour), before changing it’s current colour to show its selection (currently grey) – The reason the last colour is stored, despite the majority of keys being white is because the caps and done keys have their own colours – We need to ensure they are correctly returned to these colours once these keys are correctly deselected;

void UpdateButtonIndex()
{

     if (index = buttons.Length)
          index = buttons.Length - 1;


     //if (index != lastIndex)
     {

          buttons[lastIndex].image.color = lastColor;
          lastColor = buttons[index].image.color;
          buttons[index].image.color = Color.grey;
          lastIndex = index;
     }

}

InputCheck, called from the update method, runs every loop. It allows the keyboard to be controller via the arrow and enter keys. Arrows to move around the keyboard – Enter to select the key and add the character to the players name. This input method is only valid if the input field isn’t currently selected – This is because the input field runs the ClickDone method if the return key is pressed;

bool lastFocused = false;
public void InputCheck(bool checkReturn)
{

     if (inputField.isFocused)
     {
     }

     else if (Input.GetKeyDown(KeyCode.LeftArrow))
     {

          if (index >= 0 && index % columnLength > 0)
          {
               index--;
               UpdateButtonIndex();
          }
     }

     else if (Input.GetKeyDown(KeyCode.RightArrow))
     {

          if (index == -1 || (index + 1) % columnLength > 0)
          {
               index++;
               UpdateButtonIndex();
          }
     }

     else if (Input.GetKeyDown(KeyCode.UpArrow))
     {
          if (index >= columnLength)
          {
               index -= columnLength;
               UpdateButtonIndex();
          }
     }

     else if (Input.GetKeyDown(KeyCode.DownArrow))
     {
          if (index != -1 && index + 1 <= buttons.Length - columnLength)
          {
               index += columnLength;
               UpdateButtonIndex();
          }
     }

     else if ((Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.Space)))
     {

          if (index = buttons.Length)
               index = buttons.Length - 1;

          lastIndex = index;

          if (!checkReturn)
          {
               var pointer = new PointerEventData(EventSystem.current);
               ExecuteEvents.Execute(buttons[index].gameObject, pointer, ExecuteEvents.submitHandler);
               buttons[index].GetComponentInChildren().Play();
               }

     }

}

Finally; the InputFieldMethod, run every time a character is added by the player directly into the inputfield (by clicking on the inputfield component and typing using the computers keyboard, rather than the virtual keyboard). It checks to see if the last character added character is valid (alphanumeric or a space) – If it isn’t, it deletes the character, if it is it returns without making any changes;

    
public void InputFieldUpdate()
{

     if (inputField.text.Length > 0)
     {
     
          char chr = inputField.text[inputField.text.Length - 1];
     
          if ((chr  'z') && (chr  'Z') && (chr  '9') && chr != ' ')
          {
               inputField.text = inputField.text.Substring(0, inputField.text.Length - 1);
          }

     }
        
}

Play the game (WebGL)

Grab a copy of the project here



Next post: 1.8 Obstacles

Last post: 1.7d Highscores (Display)

Contents page.


Adventures in Unity – 1.7d Highscores (Display)

Official_unity_logo

 

The backbone of the highscore system is in place – But theres still no method for the program to display highscores.

Neither of the two sources I’d used to far were able to help me here – The unify community code was more involved with the back-end of things & the YouTube guide [Unity Tutorial] Online Highscores 01 (dreamlo) only demonstrated a very basic method for displaying the top ten scores.

I’d intended to show the top fifty scores; which was clearly going to be too many to display all at the same time on a single screen. I figured the game needed the ability to list the highscores, and let the player scroll through them.

I thought this would be pretty easy to implement – unfortunately there’s currently not a lot of elements in Unity’s UI for handling this kind of thing – At least, not without putting in a lot of effort first.

Ultimately I was able to put something together, which I think does the job pretty well, but still isn’t quite as flexible as I’d liked.


 

Highscore display is made up of three main elements;

1. HighScorePanel prefab

HighscorePanel

This is a panel with three text components attached (position, Name, Score). Used to display the details of one highscore entry.

2. ScrollView component

HighscoreDisplay

This contains a list HighScorePanel objects & is used to display the top 50 scores.

3. HighScoreDisplay script

The meat of the highscore display process – This code requests highscore data and uses it to fill the scrollview component with the current data to display.

 


 

 

The HighScoreDisplay script,  run at startup, calls the function refreshHighScores, which every 30 seconds, (by way of the HighscoreController script), requests an updated list of highscore data;

    
IEnumerator refreshHighScores()
{
     while (true)
     {
          highScoreController.DownloadHighscores();
          yield return new WaitForSeconds(30);
     }
}

 

Once the HighscoreController refreshes the highscore list, it passes control back to the HighScoreDisplay script – By calling the OnHighScoresDownloaded function along with a list of all current highscores. OnHighScoresDownloaded uses the highscore data to populate the ScrollView with HighscorePanel prefabs, each of which containing one highscore entry;

 

public void OnHighScoresDownloaded(HighScore[] highScoreList)
{

     ClearList();

     for (int counter = 0; counter < highScoreList.Length; counter++)
     {

          //---

          GameObject go = Instantiate(hsPanel) as GameObject;
          go.transform.SetParent(hsContent.transform);
          go.transform.localScale = go.transform.localScale * canvas.scaleFactor;
          Text[] children = go.GetComponentsInChildren();
          children[0].text = (counter + 1).ToString();
          children[1].text = highScoreList[counter].userName;
          children[2].text = highScoreList[counter].score.ToString();

          //---

          if (counter == HighscoreController.lastHighScore)
          {

               string currentColour = "ff0000";

               children[0].text = "" + children[0].text + "";
               children[1].text = "" + children[1].text + "";
               children[2].text = "" + children[2].text + "";

          }

     }

}

 

OnHighScoresDownloaded loops through each highscore entry, & for each it creates instance of a HighScorePanel prefab; adding the name, score and position in highscore to the prefabs attached Text components.

for (int counter = 0; counter < highScoreList.Length; counter++)
{

     //---

     GameObject go = Instantiate(hsPanel) as GameObject;
     go.transform.SetParent(hsContent.transform);
     go.transform.localScale = go.transform.localScale * canvas.scaleFactor;
     Text[] children = go.GetComponentsInChildren();
     children[0].text = (counter + 1).ToString();
     children[1].text = highScoreList[counter].userName;
     children[2].text = highScoreList[counter].score.ToString();

     //---
}

Setting the prefabs parent to the content component of the HighScorePanel component.
This attaches it to the scroll object allowing it to be displayed,

     go.transform.SetParent(hsContent.transform);

Also scaling the text, so that it will look approximately the same no matter what screen res is used;

     go.transform.localScale = go.transform.localScale * canvas.scaleFactor;

 

& Finally, If a new highscore has been achieved, after the new high score entry added the game displays the highscore table, highlighting the new entry by changing its colour;

 

if (counter == HighscoreController.lastHighScore)
{

     string currentColour = "ff0000";

     children[0].text = "" + children[0].text + "";
     children[1].text = "" + children[1].text + "";
     children[2].text = "" + children[2].text + "";
}

 


 

Now I have a way display highscores list – But no method to allow players to add new highscores to the table.

 


Play the game (WebGL)

Grab a copy of the project here


 

 


 

Next post: 1.7e Highscores (Add)

Last post: 1.7c Highscores (In-Game Backend)

Contents page.

 


 

Adventures in Unity – 1.7c Highscores (In-Game Backend)

Official_unity_logo

Once the server side code was in place, I needed methods to download the current high score list and upload new entries in-game.

The basic code for connecting to the server and retrieving scores was provided by the unify community ‘Server Side Highscores’ article.

I added the method Md5Sum (also provided by the unify community) to encode the data before sending to the server.

Expanding on this code using the  YouTube tutorial ‘[Unity Tutorial] Online Highscores 01 (dreamlo)‘ – Using IEnumerator when adding a new highscore or downloading the current highscore list provides a smoother in game experience for the player, since it allows the primary thread to manage the game, while the secondary threads manage the highscore table status.

 


 

The modified code was placed in a c# script called HighScoreController – Which I attached to an empty component also called HighScoreController.

Functionality within the HighScoreController script can be split into two fairly clear sections;

  1. Add New High Score.
  2. Download (format and store) current highscores list.

 


 

1. Add New High Score;

Adapting the method to add new highscores – Using IEnumerator to allow the code to run on a separate thread to the main game;

    
public static void AddNewHighScore(string userName, int score)
{
     instance.StartCoroutine(instance.UploadNewHighscore(userName, score));
}

// remember to use StartCoroutine when calling this function!
IEnumerator UploadNewHighscore(string name, int score)
{
     //This connects to a server side php script that will add the name and score to a MySQL DB.
     // Supply it with a string representing the players name and the players score.
     string hash = Md5Sum(name + score + secretKey);

     string post_url = addScoreURL + "name=" + WWW.EscapeURL(name) + "&score=" + score + "&hash=" + hash;

     // Post the URL to the site and create a download object to get the result.
     WWW hs_post = new WWW(post_url);
     yield return hs_post; // Wait until the download is done

     if (hs_post.error != null)
     {
          print("There was an error posting the high score: " + hs_post.error);
     }

     else
     {
          DownloadHighscores();
     }

}

With an associated method which encrypts highscores for sending (also provided by the Unify Community site);

    
public string Md5Sum(string strToEncrypt)
{

     System.Text.UTF8Encoding ue = new System.Text.UTF8Encoding();
     byte[] bytes = ue.GetBytes(strToEncrypt);

     // encrypt bytes
     System.Security.Cryptography.MD5CryptoServiceProvider md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
     byte[] hashBytes = md5.ComputeHash(bytes);

     // Convert the encrypted bytes back to a string (base 16)
     string hashString = "";

     for (int i = 0; i < hashBytes.Length; i++)
     {
          hashString += System.Convert.ToString(hashBytes[i], 16).PadLeft(2, '0');
     }

     return hashString.PadLeft(32, '0');

}

 


 

2. Download (format and store) current highscores list;

Expanding on the Unify Community method to download highscores as a string from the server. As with the AddNewHighScore method, DownloadHighScores is also modified from the Unify Community code using IEnuerator to allow threaded functionality;

public void DownloadHighscores()
{
     StartCoroutine("DownloadHighscoresFromDatabase");
}

// Get the scores from the MySQL DB to display in a Text.
// remember to use StartCoroutine when calling this function!
IEnumerator DownloadHighscoresFromDatabase()
{

     WWW hs_get = new WWW(highscoreURL);
     yield return hs_get;

     int counter = 0;
     for (; counter < hs_get.text.Length; counter++)
          if (hs_get.text[counter] == '#')
               break;

     string hs_got = hs_get.text.Substring(0, counter); //Substring(0, counter);

     if (hs_get.error != null)
     {
          print("There was an error getting the high score: " + hs_get.error);
     }
     else
     {
          FormatHighScores(hs_got);
          highScoreDisplay.OnHighScoresDownloaded(highScoresList);
     }

}

 


 

Note: The highscore data is returned by 000webhost as a text string – When returning the data, 000webhost  appends a little extra/unwanted info


http://stats.hosting24.com/count.php

This code is used by 000webhost to check site usage – Which is fair enough, since they provide the hosting for free – But is unneeded and unwanted by the game – I definitely dont want it getting mixed up with the highscore table.

My quick and dirty fix to this;

  
int counter = 0;

for (; counter < hs_get.text.Length; counter++)
     if (hs_get.text[counter] == '#')
          break;

string hs_got = hs_get.text.Substring(0, counter); //Substring(0, counter);

On the server, after creating the highscore list, I append a ‘#’ to the end of the string. A character I know isn’t used either in the highscores (it cant be entered when adding a highscore) or in 000webhost’s appended text. I loop through the highscore text, searching for the position of the hash character – Once found, the code removes it and any text which follows.

 


 

After DownloadHighscoresFromDatabase has retrieved the highscore text; it calls FormatHighScores which parses the names and scores from the text and stores them for use in game;

void FormatHighScores(string TextStream)
{
     string[] entries = TextStream.Split(new char[] { '\n' }, System.StringSplitOptions.RemoveEmptyEntries);
     highScoresList = new HighScore[entries.Length];

     for (int counter = 0; counter < entries.Length; counter++)
     {

          string[] entryInfo = entries[counter].Split(new char[] { '|' });

          if (entryInfo.Length == 2)
          {

               //---

               string userName = entryInfo[0];
               int score = int.Parse(entryInfo[1]);
               highScoresList[counter] = new HighScore(userName, score);

               //---

          }

     }

     //---

}

FormatHighScores populates a list of HighScore struct. This is used by the HighscoreDisplay script to populate the highscore table – And by the HighScoreAdd script to check if the new score should be counted as a new entry;

public struct HighScore
{
     public string userName;
     public int score;

     public HighScore(string inUserName, int inScore)
     {
          userName = inUserName;
          score = inScore;
     }
}

 


 

This provides a backbone in-game to manage highscores – But I still need methods to display current high score & add new highscores.

 


Play the game (WebGL)

Grab a copy of the project here


 

 


 

Next post: 1.7d Highscores (Display)

Last post: 1.7b Highscores (In-Game Overview)

Contents page.