Adventures in Unity – 1.7a Highscores (Server Side MySQLi)

Official_unity_logo

When it came to setting up the high-scores for Project2 (Bounder),  I discovered that the server-side/back-end PHP I’d used for Project 1 (BlockRun) was now out of date.

It seems that from PHP 5.5.x? onward the MySQL extensions had been deprecated, instead MySQLi or PDO_MySQL were new new extension options.
The Unity3D wiki provides a PDO version which can be found here Server Side Highscores
I wanted to get my hands a little dirtier this time – So I instead opted to update the code to use MySQLi.

I hope, in this post, to give a quick overview of the amended code
I’ve also updated the project 1 file (zipped up and linked at the bottom of this article and every project 1 post) it now contains both the MySQL and the new MySQLi files)


There are four files used for the server-side high-scores

  1. .htaccess
  2. addscore.php
  3. crossdomain.xml
  4. display.php

Since .htaccess and crossdomain.xml are unchanged in this update, I won’t discuss them in this post – If you’d like to know more about what I’m doing (probably wrong) with these files, what they are for, and where to put them in your own high-score setup – try here: Adventures in Unity – 1.7a Highscores (Server Side)

This post will just cover the MySQLi implementation of addscore.php and display.php.


addscore.php

This is called by the game when adding a new high-score.

The full PHP (included in BlockRun.zip – link below);

$servername = "localhost";
$username = "username";
$password = "password";
$database = "database";

// Create connection
$conn = mysqli_connect($servername, $username, $password, $database);

// Check connection
if (!$conn) {
	die("Connection failed: " . mysqli_connect_error());
}

//---

// Strings must be escaped to prevent SQL injection attack. 
$name = mysqli_real_escape_string($conn, $_GET['name']);
$score = mysqli_real_escape_string($conn, $_GET['score']);	
$hash = $_GET['hash']; 
	
//---
	
$secretKey="secretKey"; # Change this value to match the value stored in the client javascript below 
$real_hash = md5($name . $score . $secretKey);
	
if($real_hash == $hash) {
	echo "#1";
	
	// Send variables for the MySQL database class. 
	$query = "INSERT INTO BlockRun (name, score) VALUES ('$name', '$score')";
	$result=mysqli_query($conn,$query);
	mysqli_free_result($result); // Free result set
		
	//---
			
	$query = "delete from BlockRun Where id not in (select * from(select id from BlockRun order by score desc limit 50) as temp)"; 
	$result=mysqli_query($conn,$query);
	mysqli_free_result($result); // Free result set

} 
	
//---
	
echo "# END";
mysqli_close($conn);
	

Breaking this down;

First, the PHP attempts to connect to the database…

// Create connection
$conn = mysqli_connect($servername, $username, $password, $database);

 

If no connection can be made, the script exits & throws an error…

// Check connection
if (!$conn) {
	die("Connection failed: " . mysqli_connect_error());
}

 

PHP collects the new highscores name & score as well as the hash provided by the game;

// Strings must be escaped to prevent SQL injection attack. 
$name = mysqli_real_escape_string($conn, $_GET['name']);
$score = mysqli_real_escape_string($conn, $_GET['score']);	
$hash = $_GET['hash']; 

 

We have a local copy of the secret key (this should match the key in the game code) and build a server side instance of the hash (this will be used to ensure the new highscore is valid)…

$secretKey="secretKey"; # Change this value to match the value stored in the client javascript below 
$real_hash = md5($name . $score . $secretKey);

 

If everything is valid…

if($real_hash == $hash)

 

The new high-score is added into the database…

$query = "INSERT INTO BlockRun (name, score) VALUES ('$name', '$score')";
$result=mysqli_query($conn,$query);
mysqli_free_result($result); // Free result set

 

Because only the top 50 highscores are stored, after adding a new entry the database is queried, with the query results ordered by score (highest to lowest) – deleting any scores from the 51st row onward…

$query = "delete from BlockRun Where id not in (select * from(select id from BlockRun order by score desc limit 50) as temp)"; 
$result=mysqli_query($conn,$query);
mysqli_free_result($result); // Free result set

 

With that done, the connection to the database is closed and the process ends…

echo "# END";
mysqli_close($conn);

addscore.php

This is called by the game when adding a new highscore.

The full PHP (included in BlockRun.zip – link below);

$servername = "localhost";
$username = "username";
$password = "password";
$database = "database";

// Create connection
$conn = mysqli_connect($servername, $username, $password, $database);

// Check connection
if (!$conn) {
	die("Connection failed: " . mysqli_connect_error());
}

//---

$query = "SELECT * FROM `BlockRun` ORDER by `score` DESC LIMIT 50";

if ($result=mysqli_query($conn,$query))
{
	while ($row = mysqli_fetch_array($result)) {
		echo $row['name'] . "|" . $row['score'] . "\n";
	}		
}

//---

echo "#";
	
mysqli_free_result($result); // Free result set
mysqli_close($conn);	

Breaking this down;

 

First, the PHP attempts to connect to the database…

// Create connection
$conn = mysqli_connect($servername, $username, $password, $database);

 

If no connection can be made, the script exits & throws an error…

// Check connection
if (!$conn) {
	die("Connection failed: " . mysqli_connect_error());
}

 

If a successful connection is made, a query is constructed requesting the top 50 scores are returned (I only store the top 50 scores in this database) – The results are returning in descending order (highest to lowest) based on the players score…

$query = "SELECT * FROM `BlockRun` ORDER by `score` DESC LIMIT 50";

 

The database is queried and the results are stored in $result
Each row in the results is then ‘echoed’ back to the game…

if ($result=mysqli_query($conn,$query))
{
	while ($row = mysqli_fetch_array($result)) {
		echo $row['name'] . "|" . $row['score'] . "\n";
	}		
}

 

A final output to confirm the end of the file…

echo "#";

 

Once complete everything is tided up – emptying the $result set and closing the connection…

mysqli_free_result($result); // Free result set
mysqli_close($conn);

Play the game (WebGL)

Grab a copy of the project here



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

Last post: 1.7a Highscores (Server Side)

Contents page.


Adventures in Unity – 2.11 Enemies, Bonuses & Environmental Spectrum

Official_unity_logo

Below is a (hopefully) complete list of NPC’s (i.e. everything that’s not a floor tile) used in the Spectrum version of Bounder.


Enemies

HandDisc
Type: Boxing Glove
Effect: Instant Death
Movement Type: Vertical (up & down), Down
Levels (no. in level): 2(1) 6(1)


 

BearTrap2
Type: BearTrap
Effect: Instant Death
Movement Type: Static, Horizontal (left to right)
Levels (no. in level): 3(1) 4(3) 5(2) 6(3) 9(2) 10(4)


Binary
Type: Binary
Effect: Instant Death
Movement Type: Vertical (up & down)
Levels (no. in level): 5(1)


Binoculars

BinocularsR
Type: Binoculars
Effect: Instant Death
Movement Type: Horizontal (left to right), Vertical (up & down)
Levels (no. in level): 1(3) 2(8) 4(2) 5(3) 6(8) 8(2) 9(1) 10(5)


Bird
Type: Bird (Crow?)
Effect: Instant Death
Movement Type: Angled (DownDownLeft, Down, DownDownRight, Chase Player)
Levels (no. in level): 1(2) 2(1) 3(1) 4(2) 6(5) 7(2) 10(4)


Blade

BladeR

Type: Blade
Effect: Instant Death
Movement Type: Horizontal (left to right), Chase player
Levels (no. in level): 11(1) 2(1) 4(2) 5(4) 6(2) 7(2) 10(1)


Coin2
Type: Coin (Type 1)
Effect: Instant Death
Movement Type: Down, Horizontal (left to right)
Levels (no. in level): 3(4) 6(5) 9(1)


Coin3
Type: Coin (Type 2)
Effect: Instant Death
Movement Type: Horizontal (left to right), Vertical (up & down)
Levels (no. in level): 3(2) 4(1) 5(3) 6(3) 8(2)


Dart
Type: Dart
Effect: Instant Death
Movement Type: Left, Right
Levels (no. in level): 1(1) 2(1) 3(2) 4(1) 5(2) 6(1) 8(4) 10(2)


DustCloud
Type: Dust Cloud
Effect: Instant Death
Movement Type: Horizontal (left to right), Vertical (up & down)
Levels (no. in level): 2(2) 5(1) 10(1)


Electricity

ElectricityR
Type: Electricity Generator
Effect: No Damage
Movement Type: Static
Levels (no. in level): 2(1)


Fireball
Type: Fireball
Effect: Launched form Volcanoes & Plant-Pots (Exploding) – Instant Death
Movement Type: Angled (random angle set a launch)
Levels (no. in level): 2(1) 5(1) 8(1)


Alien
Type: Gremlin
Effect: Instant Death
Movement Type: Static, Horizontal (left to right), Vertical (up & down) & angled (Down-left)
Levels (no. in level): 3(3) 4(3) 6(4) 7(5) 9(2) 10(3)


Mouth
Type: Mouth
Effect: Instant Death
Movement Type: Static, Horizontal (left to right)
Levels (no. in level): 6(1) 9(1) 10(1)


PlantPot

PlantPotR

Type: Robot (Moving)
Effect: Instant Death
Movement Type: Horizontal (left to right), Vertical (up & down), Down
Levels (no. in level): 2(3) 4(1) 6(1) 9(1) 10(1)


PlantPot

PlantPotR

Type: Robot (Exploding)
Effect: Instant Death
Movement Type: static, explodes launching fireballs once it has scrolled a third of the way down the screen
Levels (no. in level): 4(1) 6(1)


RotatingWheel
RotatingWheelR
Type: Spinning Wheel
Effect: Instant Death
Movement Type: Horizontal (left to right), Vertical (up & down), Down
Levels (no. in level): 3(3) 4(3) 5(2) 6(6) 9(3) 10(3)


SeaMissile1a

SeaMissile2
Type: Sea Missile
Effect: Instant Death
Movement Type: down
Levels (no. in level): 3(4) 4(2) 5(12) 6(16) 9(1) 10(6)


Volcano
Type: Volcano
Effect: Instant Death
Movement Type: static, explodes once it has scrolled a third of the way down the screen
Levels (no. in level): 2(1) 5(1) 8(1)


Bonuses

Basketball
Type: Basketball
Effect: 20 Jumps
Movement Type: Horizontal (left to right)
Levels (no. in level): 6(1)


Copyright
Type: Copyright
Effect: 10000 points
Movement Type: Horizontal (left to right)
Levels (no. in level): 6(1)


Environmental

BoxingGlove
Type: Boxing Glove
Effect: Appears once the player reaches the end of a (non-bonus) level – Player has 6 bounces to reach the exit – otherwise they are ‘punched’ towards the exit
Movement Type: Vertical (up & down)
Levels (no. in level): 1(1) 2(1) 3(1) 4(1) 5(1) 6(1) 7(1) 8(1) 9(1) 10(1)


Fan

FanR
Type: Fan
Effect: pushes player left-right (depending on direction fan is facing)
Movement Type: static
Levels (no. in level): 4(2) 5(2) 6(2) 8(2)


JumpGateA
JumpGateB
Type: Jump Gate
Effect: player collides with entrance gate disappears from the game until re-appearing from an exit gate
Movement Type: static
Levels (no. in level): 3(2) 4(2) 5(2) 6(7)


MovingTile
Type: MovingTile
Effect: player can bounce safely on moving tile
Movement Type: Horizontal (left to right)
Levels (no. in level): 3(2) 4(3) 5(3) 6(8) 7(2) 9(2) 10(3)


Movement Key

 Static  Horizontal  Vertical  Left  Right
Static Horizontal Vertical Left Right
 Down  DownLeft  DownDownLeft  DownDownRight  DownRight
Down Down Left DownDown Left DownDown Right Down Right
 Cross  Random  Angled  ChasePlayer  Explode
Cross Random Angled Chase Player Explode
 Fan  Mine
Fan Mine

Play the game (WebGL)


Next Post: 2.12 Bounder Without Bounder

Last post: 2.10 Enemies, Bonuses & Environmental C64

Contents page.


Adventures in Unity – 2.10 Enemies, Bonuses & Environmental C64

Official_unity_logo

Below is a (hopefully) complete list of NPC’s (i.e. everything that’s not a floor tile) used in the C64 version of Bounder.


Enemies

BearTrap

Type: Bear Trap
Effect: Instant Death
Movement Type: Static
Levels (no. in level): 4(7) 6(4) 7(6) 9(3)


BinocularsH
BinocularsHB
BinocularsVB
BinocularsVB
Type: Binoculars
Effect: Instant Death
Movement Type: Horizontal (left to right), Vertical (up & down), Cross
Levels (no. in level): 2(13) 4(16) 5(12) 6(25) 7(33) 8(23) 9(14) 10(17)


Blade
Type: Blade
Effect: Instant Death
Movement Type: Horizontal (left to right), rotating (fan), roaming (random movement)
Levels (no. in level): 1(3) 2(3) 3(3) 4(4) 5(1) 6(7) 7(2) 10(4)


BottleAxe
Type: Bottle Axe(?)
Effect: Instant Death
Movement Type: Right
Levels (no. in level): 3(4) 4(2) 7(4)


CoinB1
Type: Vampire Coin
Effect: Instant Death
Movement Type: Down
Levels (no. in level): 3(6) 5(11) 6(15) 8(18) 9(8) 10(3)


Dart
Type: Dart
Effect: Instant Death
Movement Type: Right
Levels (no. in level): 3(4) 4(8) 9(8)


Electricity
Type: Electricity
Effect: Player can collide with generators without dying, electricity stops momentarily to let players through
Movement Type: static
Levels (no. in level): 2(1) 5(4) 7(1) 8(1) 10(1)


Fan

FanB
Type: Fan
Effect: Pushes player left-right (depending on direction fan is facing)
Movement Type: Static
Levels (no. in level): 3(4) 7(5) 8(6) 10(2)


MineB2
Type: Mine
Effect: Explodes once it has scrolled a third of the way down the screen – launching four mini-mines which travel in diagonal direction until off screen
Movement Type: Static
Levels (no. in level): 1(1) 9(3)
MineA


PterodactylB2

Type: Pterodactyl
Effect: Instant Death
Movement Type: Angled (DownLeft, Down, DownRight)
Levels (no. in level): 1(1) 3(1) 5(1) 6(1) 8(1)


Spaceship
Type: Missile
Effect: Instant Death
Movement Type: Down – Launches above the player – Initial X position is set to the player x position
Levels (no. in level): 1(1) 2(6) 3(4) 4(4) 5(4) 6(14) 7(10) 8(9) 9(9) 10(14)


SeaMissile1C2
Type: Sea Missile
Effect: Instant Death
Movement Type: Down
Levels (no. in level): 3(2) 5(4) 6(12)


VolcanoB
Type: Volcano
Effect: launches Fireballs
Movement Type: Static
Levels (no. in level): 4(1) 5(1) 6(2) 7(1) 8(1) 9(1)


FireBall
Type: Fireball
Effect: Instant Death
Movement Type: Angled (random angle set a launch)
Levels (no. in level): 4(1) 5(1) 6(2) 7(1) 8(1) 9(1)


Bonuses

Basketball
Type: Basketball
Effect: 20 Jumps
Movement Type: Static
Levels (no. in level): 3(1) 4(2) 5(2) 6(1) 7(2) 8(2) 9(2) 10(2)


Bug
Type: Bug
Effect: 30000 points
Movement Type: Down
Levels (no. in level): 4(1) 6(2) 8(1) 9(2)


Copyright
Type: Copyright
Effect: 10000 points
Movement Type: Down
Levels (no. in level): 3(1) 4(1) 5(1) 6(1) 7(1)


Environmental

SpringBuffer
Type: Spring Buffer
Effect: Pushes player left/right (depending on direction fan is facing)
Movement Type: Static
Levels (no. in level): 3(3) 8(2)


JumpgateA
JumpgateB
Type: Jump Gate
Effect: Player collides with entrance gate disappears from the game until re-appearing from an exit gate
Movement Type: Static
Levels (no. in level): 3(2) 4(2) 5(2) 6(4) 7(2) 9(4) 10(2)


BoxingGlove
Type: Boxing Glove
Effect: Appears once the player reaches the end of a (non-bonus) level – Player has 6 bounces to reach the exit – otherwise they are ‘punched’ towards the exit
Movement Type: Vertical (up & down)
Levels (no. in level): all(1)


Movement Key

 Static  Horizontal  Vertical  Left  Right
Static Horizontal Vertical Left Right
 Down  DownLeft  DownDownLeft  DownDownRight  DownRight
Down Down Left DownDown Left DownDown Right Down Right
 Cross  Random  Angled  ChasePlayer  Explode
Cross Random Angled Chase Player Explode
 Fan  Mine
Fan Mine

Play the game (WebGL)


Next Post: 2.11 Enemies, Bonuses & Environmental Spectrum

Last post: 2.9 Tileset Spectrum

Contents page.


Adventures in Unity – 2.9 Tileset Spectrum

Official_unity_logo

Below is a (hopefully) complete list of tiles used in the Spectrum version of Bounder.
Both foreground & background (floor) tiles are included.


I’ve presented all the tiles in Black and White – However, in game their colours are defined by the current level.

From levels 1-10 (left to right) their colours would be;

LevelColours - Spec

Note: After level 7 the colours start repeating


Default Tiles

00 - Standard Spec
Type: Standard Floor Tiles
Levels: All
Effect: Safe tile – player bounces off


Effect Tiles

02a Effects C64 - LongJump
Type: Long Jump
Levels: 1 2 3 4 5 6 8 9 10
Effect: Allows the player to jump twice as far


02a Effects Spec - Moving
Type: Moving
Levels: 3 4 5 6 7 9 10
Effect: Safe tile – player bounces off


02a Effects Spec - Mystery
Type: Mystery
Levels: 1 2 4 5 6 7 8 10
Effect: Mystery tiles are activated if the player bounces on them.
They can provide either positive or negative effects.
Each mystery tile has a specific effect assigned to it – It will have the same effect every game.

List Of Potential Effects:
2 bonus jumps
5 bonus jumps
10 bonus jumps
15 bonus jumps
20 bonus jumps
25 bonus jumps

minus 10 jumps

1 extra life
2 extra life
3 extra life
4 extra life
10 extra life

Instant death – dart
Instant death – mouth
Instant death – explodes
Instant death – player shrinks out of game
Instant death – bear trap
Instant death – boxing gloves

100 bonus points
500 bonus points
1000 bonus points
10000 bonus points
25000 bonus points


Instant Death Tiles

02a Effects Spec - Barrier
Type: Barrier
Levels: 1 2 3 4 5 6 8 9 10
Effect: Kills the player on contact


02a Effects Spec - Spike
Type: Spike
Levels: 1 2 3 4 5 6 9 10
Effect: Kills the player on contact


04 - Wall1
Type: Wall (Type 1)
Levels: 1 2 3 4 5 6 8 9 10
Effect: Kills the player on contact


04 - Wall2
Type: Wall (Type 2)
Levels: 2 3 4 5 6 8 10
Effect: Kills the player on contact


Background/Floor Tiles

08c - Floor Spec
Type: Floor
Levels: All
Effect: Kills the player on contact


Play the game (WebGL)

 

 


Next Post: 2.10 Enemies, Bonuses & Environmental C64

Last post: 2.8 Tileset C64

Contents page.


Adventures in Unity – 2.8 Tileset C64

Official_unity_logo

Below is a (hopefully) complete list of tiles used in the C64 version of Bounder.
Both foreground & background (floor) tiles are included.


Default Tiles

00 - Standard
Type: Standard Floor Tiles
Levels: All
Effect: Safe tile – player bounces off


01 - Corners
Type: Standard Floor Tiles (Corners)
Levels: All
Effect: Safe tile – player bounces off


Effect Tiles

02a Effects C64 - Mystery
Type: Mystery
Levels: All
Effect: Mystery tiles are activated if the player bounces on them.

They can provide either positive or negative effects.

However, the way mystery tiles are handled on the C64 version of Bounder adds a slight random/strategic element to the game…

Each mystery tile has a specific effect assigned to it – It will have the same effect every game.

When a mystery tile is created, its effect, rather than being attached to the tile,  is instead added to a global list.

So – If the current game screen contains three active mystery tiles, the list will contain three effects.

No matter which tile the player bonuses on, the first effect in the list will be applied to the player.

Once the effect has been applied to the player, it is removed from the list – Otherwise, it is removed from the list once the mystery tile scrolls offscreen.

List Of Potential Effects:
1 bonus level jump
2 bonus level jumps
5 bonus level jumps
20 bonus level jumps

Minus 10 bonus level jumps

1 extra life
2 extra lives

Instant death – dart
Instant death – mouth
Instant death – player explodes

200 bonus points
500 bonus points
1000 bonus points
2000 bonus points
5000 bonus points
7000 bonus points
10000 bonus points
30000 bonus points
50000 bonus points


02a Effects C64 - LongJump
Type: Long Jump
Levels: All
Effect: Allows the player to jump twice as far


02a Effects C64 - Vanishing
Type: Vanishing
Levels: 5 6
Effect: Disappears just before player collision


Instant Death Tiles

07b - Grass
Type: Grass
Levels: 1 7 (Green), 10 (Yellow), 6 (Blue)
Effect: Player can safely bounce on basic grass (single colour tile, far left column) – All others kill the player on contact


09b - Lava1
Type: Lava (Type 1)
Levels: 2 4 9 (Orange), 5 (Blue), 8 (Purple), 10 (Yellow)
Effect: Kills the player on contact


09c - Lava2
Type: Lava (Type 2)
Levels: 2 4 9 (Orange), 1 (Green), 5 (Blue), 8 (Purple), 10 (Yellow)
Effect: Kills the player on contact


09d - Lava3
Type: Lava (Type 3)
Levels: 4 9 (Orange), 5 (Blue), 8 (Purple), 10 (Yellow)
Effect: Kills the player on contact


06b - Mud
Type: Mud (Type 1)
Levels: All
Effect: Kills the player on contact


05b - Mud2
Type: Mud (Type 2)
Levels: 3 5 6 7 8 9 10
Effect: Kills the player on contact


03b - Water
Type: Water
Levels: 3 5 6
Effect: Kills the player on contact


03b - Quicksand
Type: QuickSand (Water Yellow)
Levels: 10
Effect: Kills the player on contact


Background/Floor Tiles

08b - Floor
Type: Floor
Levels: All – 1 7 (Green), 2 4 9 (Orange), 3 5 6 (Blue), 8 (Purple), 10 (Yellow)
Effect: Kills the player on contact


Play the game (WebGL)


Next Post: 2.9 Tileset Spectrum

Last post: 2.7 Mesh Fade

Contents page.


 

Adventures in Unity – 2.7 Mesh Fade

Official_unity_logo

Fading a model out of the game can be a pretty useful technique.

When using the built-in shaders, fading a single mesh model in/out is a pretty straight forward process.

If you have model made up of multiple meshes – there is a couple of extra steps you may need to get the desired effect.

I’m going to try and cover both situations below;


For this example, I’m using Unity’s built-in ‘standard’ shader.

The standard shader provides two rendering modes that can be used to fade a mesh – Transparent & Fade.

The unity guide explains their intended usage;

TransparentSuitable for rendering realistic transparent materials such as clear plastic or glass. In this mode, the material itself will take on transparency values (based on the texture’s alpha channel and the alpha of the tint colour), however reflections and lighting highlights will remain visible at full clarity as is the case with real transparent materials.

FadeAllows the transparency values to entirely fade an object out, including any specular highlights or reflections it may have. This mode is useful if you want to animate an object fading in or out. It is not suitable for rendering realistic transparent materials such as clear plastic or glass because the reflections and highlights will also be faded out.

Though it’s generally best to try out both options and see which works best for your project.


To fade in/out a single mesh model;

Models use a material to manage their texture, each material has an assoiated colour.
To fade the mesh we change of the alpha value of this colour.

Alpha values are between 0 & 1;
0.0 = completely transparent
0.x = faded to some degree.
1.0 = fully opaque

To fade a mesh out by 50% (In a script attached to the model’s prefab);

Renderer renderer = GetComponent();
Color color = renderer.material.color;
color.a = 0.5f;
renderer.material.color = color;

to fade a specific colour in the material, use the SetColor function;

Renderer renderer = GetComponent();
Color color = renderer.material.color;
color.a = 0.5f;
renderer.material.SetColor("_Color", color);
001a-FadeBonusTile-x32
Single mesh model – Standard shader set to Fade
001b-TransparentBonusTile-x32
Single Mesh Model – Standard shader set to Transparent

To fade in/out a multi-mesh mesh model;

If the model has multiple meshes, we need to reference the material(s) for each mesh.

(It can be useful to create an instance of each material.
That way the alpha change will only effect that instance of the model & not every model that uses the material)

We may also need to manually set the models ZWrite value
The ZWrite value determines the order in which each mesh is drawn to screen.

002a-FadeMinesUnordered2
Multi-Mesh model – Standard Shader set to Fade – Default ZWrite order
002b-TransparentMinesUnordered2
Multi-Mesh model – Standard shader set to Transparent – Default ZWrite order

To put this in code (as a script attached to the models prefab);

First create a global variable to hold the renderer for each mesh;

Renderer[] rendererObjects;

Then in the init or start method, the renderer array is initialised & populated,
the ZWrite value (draw order) is defined (rather simply here)

 for (int counter = 0; counter < rendererObjects.Length; counter++)
 {
   Material material = rendererObjects[counter].material;
   material.SetInt("_ZWrite", counter);
 }

Once thats done & we are ready to fade – In the main code we can fade the model in or out (in this example I fade the model out – then destroy the instance once it is completely transparent);

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

   Color color = rendererObjects[counter].material.color;
   color.a -= (Time.deltaTime / 1f) % 1.0f;

   if (color.a <= 0)
      Destroy(gameObject);

   else
      rendererObjects[counter].material.SetColor("_Color", color);

}

The final result should look something like this;

003a-FadeMinesOrdered2
Multi-mesh model – Standard shader set to Fade – ZWrite manually defined.
003b-TransparentMinesOrdered2
Multi-Mesh model – Standard shader set to Transparent – ZWrite manually defined.

Play the game (WebGL)


Next Post: 2.8 Tileset C64

Last post: 2.6 Pause & Resume

Contents page.


Adventures in Unity – 2.6 Pause & Resume

Official_unity_logo

Whenever a bonus is collected a message box is displayed and the game pauses momentarily.

It’s not a function of the game I particularly like; and I probably wouldn’t use it in the final release – But, since the point of this project is to try things out & help me learn Unity, I figured I’d try and replicate it anyway.

Bounder3b

When displaying the message box, the entire game should pause – including problematic functions like particles/physics/etc.. things controlled by the Unity engine.

Google found me a solution pretty quickly – Time.timeScale

To freeze the game set Time.timeScale = 0 – To resume the game set Time.timeScale = 1.

Time.timeScale is a float variable.
Every loop, Unity multiplies Time.deltaTime against Time.timeScale.
This determines the speed of the game – For anything that references Time.deltaTime – Any update that doesn’t reference Time.deltaTime will be unaffected (Though you really should be using Time.deltaTime).

It’s usually used to pause games & tends to be set with values 0 or 1.
Though it can be used to dynamically slow/speed up gameplay – values higher than 1 may legally require that yakety sax be played at all times.


With everything paused; the next problem was how to animate the message box – (fade in, display for x seconds, fade out).

I can run the animation without using Time.DeltaTime – But then I won’t receive any of the benefits.

Another quick google search identified the solution – A homebrew Time.deltaTime using replacement using Time.realtimeSinceStartup.

Unity documentation explains ‘realtimeSinceStartup returns the time since startup, not affected by Time.timeScale. realtimeSinceStartup also keeps increasing while the player is paused (in the background). Using realtimeSinceStartup is useful when you want to pause the game by setting Time.timeScale to zero, but still want to be able to measure time somehow.

I use The replacement Time.deltaTime as a timer;

  IEnumerator PauseTimer(float delay)
  {

     float start = Time.realtimeSinceStartup;
     timerComplete = false;

     while (Time.realtimeSinceStartup < start + delay)
     {
          yield return null;
     }

     timerComplete = true;

}

Bounder2a.gif


Play the game (WebGL)


Next Post: 2.7 Mesh Fade

Last post: 2.5b Palette Shift

Contents page.


Adventures in Unity – 2.5b 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;

  • Colour Lerp.
  • HBSC
  • Palette Swap
  • Quad Cut-Out
  • Shader
  • Texture Sheet Animation.

The first three implementations, were covered in the last post.

  • Colour Lerp.
  • HBSC
  • Palette Swap

This post will expand on the second half;

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

4. Quad Cut-Out

This method uses two textures – One ‘base’ texture displays the standard start/goal tile texture – The other ‘top’ texture; is a copy of the top face of the tile texture; the face showing the start/goal banner image & text – with everything except the letters (start & goal) removed (transparent instead).

QuadTexture2a
Left image shows the base tile texture.
Right image shows the quad texture for the same tile.

The base texture is used to draw the tile model. The top texture is applied to a quad – a child of the tile – which is positioned just over the top of the base tile.

The colour lerp code is then applied to the the top texture – ensuring only the text changes colour while the background remains the same.

How this works…

First a quad (this will be created locally) and a material holding the quads texture (this will be passed as a reference in the Unity editor) are defined as global variables;

GameObject quad;
public Material material1;

In start, the quad is created & configured.

if (type == Type.Enter || type == Type.Exit)
{

     quad = GameObject.CreatePrimitive(PrimitiveType.Quad);
     rend = quad.GetComponent();
     rend.material = material1;
     quad.transform.parent = transform;
 
      quad.transform.Rotate(90, 0, 0);

}

Since this method uses the colour lerp code, we need somewhere to hold the current colour.

public Color lerpedColor = Color.white;

Then every loop, the position of the quad is updated (to keep up with the tile as it scrolls down the screen). The next colour for the banner flash is calculated and assigned to the quads texture.

quad.transform.position = new Vector3(transform.position.x, transform.position.y + 0.275f, transform.position.z);
lerpedColor = Color.Lerp(Color.white, Color.black, Mathf.PingPong(Time.time, 1));
quad.GetComponent().material.color = lerpedColor;

This works well, but it does require a quad to be created (another draw call per loop), as well as an extra texture (more work & extra complications)  – Neither is a big issue, but it’s more complications than I think should be required.

I think the result looks fine, though I suspect it would look much better if I’d removed the text from the base texture – As it is, the base text seems to interfere – giving a slight glow – behind the flashing letters of the quads texture.

QuadCutout-1b


5. Shader

The original version of the shader, I  checked the colour of the current _MainTex pixel – & changed any which were white. This worked horribly – It seems that before I got to the texture it has been already modified somewhere along the pipeline.

2016-08-10-1945-00---adaptive2

Instead I take a more long-winded approach.

How this works…

Alongside the auto-generated input variables (_Color, _MainTex, _Glossiness & _Metallic – all generated by Unity when creating the shader).

The custom input variables are added;
_Color1 – Used to set the start colour in the loop (white)
_Color2 – Used to set the start colour in the loop (black)
_CutOutTex – From the Quad Cut-Out method, this is a copy of _MainTex, but with everything except the letters (start & goal) removed (transparent instead).

ShaderTexture2a
Left image shows _MainTex.
Right side shows _CutOutTex

In the surf method, after the colour of the _MainTex is sampled, the equivalent colour from _CutOutTex is identified.

If the alpha values of the sampled _CutOutTex colour is > 0 (not transparent) – Then this is a letter (in the start/goal banner) which needs to be colour shifted – The current colour shift colour is calculated and drawn to the screen.

If the alpha values from the _CutOutTex colour is <= 0 (transparent) – Then this is not part of the banner text and the sample from _MainTex (the default banner texture) is drawn..

Shader "Custom/NewSurfaceShader" {
     Properties {
          _Color ("Color", Color) = (1,1,1,1)
          _MainTex ("Albedo (RGB)", 2D) = "white" {}
          _Glossiness ("Smoothness", Range(0,1)) = 0.5
          _Metallic ("Metallic", Range(0,1)) = 0.0
          _CutOutTex("CutOut", 2D) = "white" { }
          _Color1("Color1", Color) = (1.0, 0.0, 0.0, 1.0)
          _Color2("Color2", Color) = (0.0, 0.0, 1.0, 1.0)
          _Speed("Speed", float) = 0.2
     }

     SubShader {
          Tags { "RenderType"="Opaque" }
          LOD 200
		
          CGPROGRAM
          // Physically based Standard lighting model, and enable shadows on all light types
          #pragma surface surf Standard fullforwardshadows

          // Use shader model 3.0 target, to get nicer looking lighting
          #pragma target 3.0

          sampler2D _MainTex;

          struct Input {
               float2 uv_MainTex;
          };

          half _Glossiness;
          half _Metallic;
          fixed4 _Color;
          sampler2D _CutOutTex;
          uniform fixed4 _Color1;
          uniform fixed4 _Color2;
          uniform float  _Speed;

          void surf (Input IN, inout SurfaceOutputStandard o) {
          // Albedo comes from a texture tinted by color
          fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
          fixed4 c2 = tex2D(_CutOutTex, IN.uv_MainTex);

          if (c2.a > 0) {
               fixed4 c3 = lerp(_Color1, _Color2, abs(fmod(_Time.a * _Speed, 2.0) - 1.0));

               //if (c.r > 0.99 && c.g > 0.99)
                    c = (c * c3) * _Color;
               }
               else
                    c = c * _Color;

               o.Albedo = c.rgb;
               // Metallic and smoothness come from slider variables
               o.Metallic = _Metallic;
               o.Smoothness = _Glossiness;
               o.Alpha = c.a;
          }
          ENDCG
     }

     FallBack "Diffuse"
}

I haven’t written anything for shaders in a number of years – last time was HLSL for XNA. So the learning curve here was for both figuring out nity & figuring out Unity’s shaders. I’m not particularly happy with the code I’ve put together – I’m very confident there ere are far more succinct ways to achieve the same result – But the the animation looks okay.

Shader2-1b


6. Texture Sheet Animation.

This is the method I’ve decided to use – It works almost like a traditional animation/flip book – using a series of images, each slightly different from the next – so when they are swapped a speed they appear to show movement.

In this case, the models texture is split into a grid – Each cell of the grid contains a frame of the animation (in this case 8 frames are used).
The model is drawn referencing one cell of the grid\one frame of the animation (UV mapping is explained badly in my last post).
Code (either a Unity script or shader) is used as a timer – to determine when to change to the next frame.

withframesLarge
Animation texture – With individual frames/cells highlighted.

For the code, I referenced Unity3D wiki – Animating Tiled texture
The page offers six code examples – one java/five C#

I had the most success using AnimateTiledTexture & AnimateSpriteSheet classes
Setting with the AnimateTiledTexture code because it was the easiest for me to tweak in order for it to animate the way I needed…
The goal/start texture have frames from white to black – I use ping-pong setup to run backwards and forward through the animation frames – from white to black, then in reverse from black to white – halving the final texture size.

With the code in place I needed to configure the assets – This was much harder to setup than expected – which, for a modern 3D gaming environment was a little disappointing – setting up was more guess work than design – I found it is far easier to setup an animated texture in (the ten year old) XNA.

With first attempt – I made the mistake of using a non-power or two sized texture – This resulted in animated tiles which were blurry, and jitter – jumping around as it forced through each frame – It looked pretty horrible

2016-08-10-2012-47---adaptive2

The image quality improved after resizing the texture to a power of two
and (in the textures Material settings) defining the ‘Tiling’ values to match the frame size
(8 frames in total – 4 columns, 2 rows – Tiling X = 0.25 Y = 0.5 )

This produced a noticeably sharper – non-jittery texture – but displayed a lot of artifacts.
2016-08-10-2017-57---adaptive2
I tried changing the text import settings to advanced; turning off mipmaps, mapping, etc… anything that could be turned off was turnd off – setting the wrap mode to clamp, filter to point – none of which had any effect.

What ultimatly worked for me was setting the texture type to cursor.
I have no idea why cursor settings resolve the issue – or if the next update will break it & put me back to square one.
But for now I have nice, clean, sharp animated start/goals tiles

2016-08-12-2007-58---2


Play the game (WebGL)


Next post: 2.6 Pause & Resume

Last post: 2.4 Edge Padding

Contents page.