Colour Cycle Effect Unity Shader

Just added a new colour cycle effect transparent Unity shader to the free shaders collection. This shader can be used to overlay a y-axis colour cycle effect over a texture

The code for the shader is shown below:

Shader "Unlit/ColourCycleTexture"
{
	Properties
	{
		_MainTex("Color (RGB) Alpha (A)", 2D) = "white" {}
		_WaveSpeed ("WaveSpeed", Range(-1000, 1000)) = 20
		_Frequency ("Frequency", Range(0, 100)) = 10
		_Amplitude ("Amplitude", Range(0, 3)) = 0.02
		_RedScale("Red Scale", Range(0, 3)) = 1
		_GreenScale("Green Scale", Range(0, 3)) = 1
		_BlueScale("Blue Scale", Range(0, 3)) = 1
	}
	SubShader
	{
		Tags{ "Queue" = "Transparent" "RenderType" = "Transparent" }
		Blend SrcAlpha OneMinusSrcAlpha
		LOD 100

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float2 uv : TEXCOORD0;
				float4 vertex : SV_POSITION;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			fixed _Frequency;
			fixed _WaveSpeed;
			fixed _Amplitude;
			fixed _RedScale;
			fixed _GreenScale;
			fixed _BlueScale;

			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				return o;
			}

			fixed4 frag (v2f i) : SV_Target
			{
				fixed2 uvs = i.uv;
				fixed4 col = tex2D(_MainTex, uvs);
				fixed d = sin(uvs.y * _Frequency + _Time * _WaveSpeed) * _Amplitude;
				col.r += d * _RedScale;
				col.g += d * _GreenScale;
				col.b += d * _BlueScale;
				return col;
			}
			ENDCG
		}
	}
}

The shader works by adjusting the colour of the texel pulled from the texture applying a wave effect which is then scaled by red, green and blue scaling factors which can be used to change the colour of the overlaid effect. Here are a few parameters to try:

Produce a nice upwards red scroll effect:
Wave Speed – -150
Frequency – -20
Amplitude – -1
Red Scale – 0
Green Scale – 0.4
Blue Scale – 0

Produce a slow flash:
Wave Speed – 150
Frequency – 1
Amplitude – 1
Red Scale – 0
Green Scale – 0.4
Blue Scale – 0

You may need to adjust the red, green and blue scaling factors to match the colour of your texture.

Wavy Text Unity Shader

Just added a new wavy transparent shader to the free shaders collection. This shade can be used to apply x and y axis waves to textures which looks nice on bitmapped based text, e.g.:

Wavy Transparent Unity Shader

Wavy Transparent Unity Shader

The code for the shader is shown below:

Shader "Unlit/WaveTransTexture"
{
	Properties
	{
		_MainTex("Color (RGB) Alpha (A)", 2D) = "white" {}
		_WaveSpeedX ("X Axis WaveSpeed", Range(0, 100)) = 20
		_FrequencyX ("X Axis Frequency", Range(0, 100)) = 10
		_AmplitudeX ("X Axis Amplitude", Range(0, 100)) = 0.02
		_WaveSpeedY ("Y Axis WaveSpeed", Range(0, 100)) = 20
		_FrequencyY ("Y Axis Frequency", Range(0, 100)) = 10
		_AmplitudeY ("Y Axis Amplitude", Range(0, 100)) = 0.02
	}
	SubShader
	{
		Tags{ "Queue" = "Transparent" "RenderType" = "Transparent" }
		Blend SrcAlpha OneMinusSrcAlpha
		LOD 100

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float2 uv : TEXCOORD0;
				float4 vertex : SV_POSITION;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			fixed _FrequencyX;
			fixed _AmplitudeX;
			fixed _WaveSpeedX;
			fixed _FrequencyY;
			fixed _AmplitudeY;
			fixed _WaveSpeedY;

			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				return o;
			}

			fixed4 frag (v2f i) : SV_Target
			{
				fixed2 uvs = i.uv;
				uvs.x += sin(uvs.y * _FrequencyX + _Time * _WaveSpeedX) * _AmplitudeX;
				uvs.x = (uvs.x * 0.9) + 0.05;
				uvs.y += cos(uvs.x * _FrequencyY + _Time * _WaveSpeedY) * _AmplitudeY;
				uvs.y = (uvs.y * 0.9) + 0.05;
				fixed4 col = tex2D(_MainTex, uvs);
				return col;
			}
			ENDCG
		}
	}
}

The code works by applying a sine wave to the y-axis source texture coordinate and pumping that into the x-axis texture coordinate, a similar thing is done with the x-axis source texture coordinate, except that a cosine wave is applied to it. Note that when using this shader you should leave a little space around the bitmap text (around 10% of the image size) to prevent borders from showing.

Creating a Unity leaderboard using node.js and redis

I’ve recently added a new node.js based leaderboard system for Unity to Github. I’m going to use something like this in my next game which will feature global leaderboards as a major focus of the game.You can grab the code from Github.

Let start by taking a look at what the repo contains:

Node.js Server

The server side code is written in JavaScript using Node.js with Redis for super fast storage. To use this code you will need a web server that allows you to host your own node.js scripts. There are plenty of super cheap VPS providers around these days so if you go that route take a look at my article on installing node on your own VPS here. You will also need Redis, take a look at my article on installing Redis here.

Once you have everything installed and setup, run main.js located in the Server folder to set the leaderboards server off going:


node main.js

Once running, the server will be listening for connections from Unity on the default port which is set in server.js.

The entire leaderboards service consists of 3 files:

  • server.js – The web server which interacts with the Uniy app
  • leaderboard.js – The leaderboard code which communicates with Redis to store an retrieve persistent data
  • main.js – A main file to create the server and set it off running

The code in server,js is straight forward and much of it was already covered in this article here. The main changes include dealing with data hiding, we now pass the data base64 encoded via the d parameter:

var vars = body.split("=");
if (vars[0] == "d")
{
	var data = Buffer.from(vars[1], "base64");
	console.log("Data: " + data);
	this.processCommand(data.toString(), res);
}

Here we decode the base 64 string data then pass it on to be processed. This enables me to encrypt the data at the other end then decrypt it when it arrives at this end (not covered in this article), this will allow me to hide what is sent and minimise the chance that someone spams my leaderboards with fake data.

The next major change is to handle commands that are passed to the server, to allow the client to perform different actions such as submit a score or get the users rank etc..

leaderboard.js was added to take care of all actions that are related to dealing with the actual leaderboard data, such as chatting to Redis about what to do with the data. The leaderboard class handles all of this including pushing multiple commands to Redis in one go instead of sending them separately to speed things up. For example sending scores to multiple leaderboards at the same time:

setUserScores(userName, scores, cb)
{
	var multi = this.client.multi();
	for (var t = 1; t < this.MAX_BOARDS + 1; t++)
	{
		var name = this.boardName + ":" + t;
		if (scores[t - 1] > 0 && scores[t - 1] < this.MAX_SCORE)
			multi.zadd([name, scores[t - 1], userName]);
	}
	multi.exec((err, replies) =>
	{
		if (cb) cb(err);
	});
}

Here we issue many zadd commands to redis at the same time.

Unity Client

The Unity client code is implemented in Leaderboards.cs located in the Client folder. Lets take a quick look at the code:

using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
using System;
using System.Text;

public class Leaderboards : MonoBehaviour
{
    public const int RANK_INVALID = -1; // The rank is invalid

    private static string _Url = "http://localhost";
    private static string _Port = "8080";

    // Submits users score to the server.
    //
    // @param which             Leaderboard index to submit score to
    // @param score             Score to submit
    // @param userName          Name of user to submit score for
    // @param OnScoreSubmitted  Callback to call when operation completes or fails, a bool is passed to the callback which
    //                          is true if an error occurred or false if not
    //
    public void SubmitScore(int which, int score, string userName, Action<bool> OnScoreSubmitted)
    {
        StartCoroutine(SubmitScoreToServer(which, score, userName, OnScoreSubmitted));
    }

    private IEnumerator SubmitScoreToServer(int which, int score, string userName, Action<bool> OnScoreSubmitted)
    {
        Debug.Log("Submitting score");

        // Create a form that will contain our data
        WWWForm form = new WWWForm();
        StringBuilder sb = new StringBuilder("m=score");
        sb.Append("&w=");
        sb.Append(which.ToString());
        sb.Append("&s=");
        sb.Append(score.ToString());
        sb.Append("&n=");
        sb.Append(userName);
        byte[] bytes = Encoding.UTF8.GetBytes(sb.ToString());
        form.AddField("d", Convert.ToBase64String(bytes));

        // Create a POST web request with our form data
        UnityWebRequest www = UnityWebRequest.Post(_Url + ":" + _Port, form);
        // Send the request and yield until the send completes
        yield return www.Send();

        if (www.isError)
        {
            // There was an error
            Debug.Log(www.error);
            if (OnScoreSubmitted != null)
                OnScoreSubmitted(true);
        }
        else
        {
            if (www.responseCode == 200)
            {
                // Response code 200 signifies that the server had no issues with the data we sent
                Debug.Log("Score send complete!");
                Debug.Log("Response:" + www.downloadHandler.text);
                if (OnScoreSubmitted != null)
                    OnScoreSubmitted(false);
            }
            else
            {
                // Any other response signifies that there was an issue with the data we sent
                Debug.Log("Score send error response code:" + www.responseCode.ToString());
                if (OnScoreSubmitted != null)
                    OnScoreSubmitted(true);
            }
        }
    }

    // Submits a collection of scores to the server.
    //
    // @param scores            Scores to submit, once score per leaderboard
    // @param userName          Name of user to submit score for
    // @param OnScoresSubmitted Callback to call when operation completes or fails, a bool is passed to the callback which
    //                          is true if an error occurred or false if not
    //
    public void SubmitScores(int[] scores, string userName, Action<bool> OnScoresSubmitted)
    {
        StartCoroutine(SubmitScoresToServer(scores, userName, OnScoresSubmitted));
    }

    private IEnumerator SubmitScoresToServer(int[] scores, string userName, Action<bool> OnScoresSubmitted)
    {
        Debug.Log("Submitting scores");

        WWWForm form = new WWWForm();
        StringBuilder sb = new StringBuilder("m=scores");
        sb.Append("&a=");
        for (int t = 0; t < scores.Length; t++)
        {
            sb.Append(scores[t].ToString());
            if (t < scores.Length - 1)
                sb.Append(",");
        }
        sb.Append("&n=");
        sb.Append(userName);
        byte[] bytes = Encoding.UTF8.GetBytes(sb.ToString());
        form.AddField("d", Convert.ToBase64String(bytes));

        UnityWebRequest www = UnityWebRequest.Post(_Url + ":" + _Port, form);
        yield return www.Send();

        if (www.isError)
        {
            Debug.Log(www.error);
            if (OnScoresSubmitted != null)
                OnScoresSubmitted(true);
        }
        else
        {
            if (www.responseCode == 200)
            {
                Debug.Log("Scores sent complete!");
                Debug.Log("Response:" + www.downloadHandler.text);
                if (OnScoresSubmitted != null)
                    OnScoresSubmitted(false);
            }
            else
            {
                Debug.Log("Scores sent error response code:" + www.responseCode.ToString());
                if (OnScoresSubmitted != null)
                    OnScoresSubmitted(true);
            }
        }
    }

    // Gets the user rank from the server.
    //
    // @param which             Leaderboard index to submit score to
    // @param score             Score to submit
    // @param userName          Name of user to submit score for
    // @param OnScoreSubmitted  Callback to call when operation completes or fails, an int is passed to the callback which
    //                          represents the users rank
    //
    public void GetRank(int which, string userName, Action<int> OnRankRetrieved)
    {
        StartCoroutine(GetRankFromServer(which, userName, OnRankRetrieved));
    }

    private IEnumerator GetRankFromServer(int which, string userName, Action<int> OnRankRetrieved)
    {
        Debug.Log("Getting rank");

        WWWForm form = new WWWForm();
        StringBuilder sb = new StringBuilder("m=rank");
        sb.Append("&w=");
        sb.Append(which.ToString());
        sb.Append("&n=");
        sb.Append(userName);
        byte[] bytes = Encoding.UTF8.GetBytes(sb.ToString());
        form.AddField("d", Convert.ToBase64String(bytes));

        UnityWebRequest www = UnityWebRequest.Post(_Url + ":" + _Port, form);
        yield return www.Send();

        if (www.isError)
        {
            Debug.Log(www.error);
            if (OnRankRetrieved != null)
                OnRankRetrieved(RANK_INVALID);
        }
        else
        {
            if (www.responseCode == 200)
            {
                Debug.Log("Get rank complete!");
                Debug.Log("Response:" + www.downloadHandler.text);
                if (OnRankRetrieved != null)
                {
                    int rank = RANK_INVALID;
                    int.TryParse(www.downloadHandler.text, out rank);
                    OnRankRetrieved(rank);
                }
            }
            else
            {
                Debug.Log("Get rank error response code:" + www.responseCode.ToString());
                if (OnRankRetrieved != null)
                    OnRankRetrieved(RANK_INVALID);
            }
        }
    }

    // Gets the users ranks from the server.
    //
    // @param userName          Name of user to submit score for
    // @param OnRanksRetrieved  Callback to call when operation completes or fails, callback has an array of ints, with
    //                          each element representing a leaerboard rank
    //
    public void GetRanks(string userName, Action<int[]> OnRanksRetrieved)
    {
        StartCoroutine(GetRanksFromServer(userName, OnRanksRetrieved));
    }

    private IEnumerator GetRanksFromServer(string userName, Action<int[]> OnRanksRetrieved)
    {
        Debug.Log("Getting ranks");

        WWWForm form = new WWWForm();
        StringBuilder sb = new StringBuilder("m=ranks");
        sb.Append("&n=");
        sb.Append(userName);
        byte[] bytes = Encoding.UTF8.GetBytes(sb.ToString());
        form.AddField("d", Convert.ToBase64String(bytes));

        UnityWebRequest www = UnityWebRequest.Post(_Url + ":" + _Port, form);
        yield return www.Send();

        if (www.isError)
        {
            Debug.Log(www.error);
            if (OnRanksRetrieved != null)
                OnRanksRetrieved(null);
        }
        else
        {
            if (www.responseCode == 200)
            {
                Debug.Log("Get ranks complete!");
                Debug.Log("Response:" + www.downloadHandler.text);
                if (OnRanksRetrieved != null)
                {
                    string[] sranks = www.downloadHandler.text.Split(',');
                    int[] ranks = new int[sranks.Length];
                    int i = 0;
                    foreach (string s in sranks)
                    {
                        ranks[i] = RANK_INVALID;
                        int.TryParse(sranks[i], out ranks[i]);
                        i++;
                    }
                    OnRanksRetrieved(ranks);
                }
            }
            else
            {
                Debug.Log("Get ranks error response code:" + www.responseCode.ToString());
                if (OnRanksRetrieved != null)
                    OnRanksRetrieved(null);
            }
        }
    }
}

This class contains four methods at present:

  • SubmitScore – Submits a score to the server
  • SubmitScores – Submits a collection of scores to the server
  • GetRank – Gets the users rank from the server
  • GetRanks – Gets all of the users ranks from the server

Internally each request to the server is called via a coroutine and the supplied callback that we pass is called when a response from the server comes in, e.g.:

Leaderboards lbds = GetComponent<Leaderboards>();
lbds.GetRank(which, (int rank) =>
{
    // Do something with the result
});

Its also worth mentioning that when we send data to the server we make some effort to hide it which makes it a little harder for someone to spam the server with false scores, e.g.:

// Create a form that will contain our data
WWWForm form = new WWWForm();
StringBuilder sb = new StringBuilder("m=score");
sb.Append("&w=");
sb.Append(which.ToString());
sb.Append("&s=");
sb.Append(score.ToString());
sb.Append("&n=");
sb.Append(userName);
byte[] bytes = Encoding.UTF8.GetBytes(sb.ToString());
form.AddField("d", Convert.ToBase64String(bytes));

Note how we firstly encode the data to bytes then convert it to a base 64 string, this enables us to add a step between, where we can encrypt the byte data using whatever means we like.

Whats coming next….

I plan to extend the server to allow queries of the data, so I can for example view all of the leaderboards from a website and possibly on device.

Installing Redis to Windows / Linux

I recently began adding support for a global leaderboard system to my latest Unity game and decided that instead of the huge overhead related to going the RDBMS SQL route that i would have a crack at implementing back-end storage using a NoSQL database instead, which whilst much faster than traditional RDBMS is much more restrictive in terms of query. In fact Redis is for all intents and purposes almost impossible to query in any kind of depth. That said, I am not particularly interested in its relational query features but more in its ability to store and run basic queries on data very quickly, which Redis does extremely well.

Note that I am writing this article with a view to using Redis from Node.js, I will cover using Redis from .NET Core in a later article. If you missed my article on installing node.js then look here.

What is Redis

Redis is a fast in memory key/value store that can be used like a database. It differs aainly to SQL in that complex queries cannot be ran on the data, but if you do not need to carry out complex queries on the data (for example a persistent game world map or a leaderboard) then something like redis is ideal for storing the data.

Installing Redis

Windows

For Windows download the MSI release from here then install it. Redis will be installed as a service. You can take a look in Task Manager to ensure that Redis is running. To confirm the install open up command prompt and run Redis command line interface redis-cli.

Linux

Installing redis on Linux is not so easy because you need to build the latest stable release, firstly lets get the tools you are going to need to build Redis:


sudo apt-get update
sudo apt-get install build-essential
sudo apt-get install tcl8.5

Download and untar the latest stable build:


wget http://download.redis.io/releases/redis-stable.tar.gz
tar xzf redis-stable.tar.gz

Now lets build Redis:


cd redis-stable
make
make test
sudo make install

Run Redis as a background daemon (a script has been provided to do this), note that you will be asked to set some options, just choose the defaults for the moment:


cd utils
sudo ./install_server.sh

The above script creates a script in etc\init.d called redis_6379 (the numbers represent the port that you selected during install).

To get Redis to run automatically on boot:


sudo update-rc.d redis_6379 defaults

Installing Redis client library for Node.js

This is actually very simple, run the following:


npm install redis

We can now reference Redis from Node.js by importing Redis in our Node.js code:


var redis = require("redis");

Testing Redis with Node.js

Create a new file called redis_test.js and add the following code:


var redis = require("redis");

// Create the redis client
var client = redis.createClient();
client.on("error", function (err)
{
console.log("Redis error: " + err);
});

// Try out a simple command
client.set("my_key", "some value");

For more information on Redis see the Redis website

Getting Unity 3D and node.js talking

I’m working on a Unity 3D game at the moment that needs a global leaderboard system that works across platform and not tied into the likes of Google Play or Game Centre. After taking a look at various technologies including my old favourite .NET (specifically thew newish .NET Core) I decided to use node.js because a) I want to learn it and b) .NET is a bit of overkill I think.

So I installed node.js then wrote a small server:

"use strict";

var http = require("http");

class Server
{
	constructor()
	{
		this.port = 8080;
		this.ip = "localhost";

		this.start();
	}

	start()
	{
		this.server = http.createServer((req, res) =>
		{
			this.processRequest(req, res);
		});

		this.server.on("clientError", (err, socket) =>
		{
			socket.end("HTTP/1.1 400 Bad Request\r\n\r\n");
		});
		console.log("Server created");
	}

	listen()
	{
		this.server.listen(this.port, this.ip);
		console.log("Server listening for connections");
	}

	processRequest(req, res)
	{
		// Process the request from the client
		// We are only supporting POST
		if (req.method === "POST")
		{
			// Post data may be sent in chunks so need to build it up
			var body = "";
			req.on("data", (data) =>
			{
				body += data;
				// Prevent large files from benig posted
				if (body.length > 1024)
				{
					// Tell Unity that the data sent was too large
					res.writeHead(413, "Payload Too Large", {"Content-Type": "text/html"});
					res.end("Error 413");
				}
			});
			req.on("end", () =>
			{
				// Now that we have all data from the client, we process it
				console.log("Received data: " + body);
				// Split the key / pair values and print them out
				var vars = body.split("&");
				for (var t = 0; t < vars.length; t++)
				{
					var pair = vars[t].split("=");
					var key = decodeURIComponent(pair[0]);
					var val = decodeURIComponent(pair[1]);
					console.log(key + ":" + val);
				}
				// Tell Unity that we received the data OK
				res.writeHead(200, {"Content-Type": "text/plain"});
				res.end("OK");
			});
		}
		else
		{
			// Tell Unity that the HTTP method was not allowed
			res.writeHead(405, "Method Not Allowed", {"Content-Type": "text/html"});
			res.end("Error 405");
		}
	}

}

module.exports.Server = Server;

Create a file called server.js and add the above code.

Note that the above code is designed as a module so you can import it into your own code, e.g.:

var server = require('./server');

var httpServer = new server.Server();
httpServer.listen();

Create a file called main.js and add the above code. Run the server by running node main.js

In the server code we create an instance of a HTTP client then we begin listening for connections. When a connection comes in we read the POST data and print out the key / value pairs that are sent.

On the Unity side we write the following code to test out our mini server:

using UnityEngine;
using UnityEngine.Networking;
using System.Collections;

public class Leaderboards : MonoBehaviour
{
    private static string _Url = "http://localhost";
    private static string _Port = "8080";

    public void SubmitScore(int which, int score)
    {
        StartCoroutine(SubmitScoreToServer(which, score));
    }

    private IEnumerator SubmitScoreToServer(int which, int score)
    {
        Debug.Log("Submitting score");

        // Create a form that will contain our data
        WWWForm form = new WWWForm();
        form.AddField("which", which.ToString());
        form.AddField("score", score.ToString());

        // Create a POST web request with our form data
        UnityWebRequest www = UnityWebRequest.Post(_Url + ":" + _Port, form);
        // Send the request and yield until the send completes
        yield return www.Send();

        if (www.isError)
        {
            // There was an error
            Debug.Log(www.error);
        }
        else
        {
            if (www.responseCode == 200)
            {
                // Response code 200 signifies that the server had no issues with the data we went
                Debug.Log("Form sent complete!");
                Debug.Log("Response:" + www.downloadHandler.text);
            }
            else
            {
                // Any other response signifies that there was an issue with the data we sent
                Debug.Log("Error response code:" + www.responseCode.ToString());
            }
        }
    }
}

In the above code we create a form, add the data that we wish to send then create a web request (UnityWebRequest) to send the form to our test server. We send the request then yield the coroutine until the send completes. We finally test the response code to ensure that our data was sent ok,

To test, add the component to an object and then call SubmitScore(1, 100), you should see the following displayed from node:

Server created
Server listening for connections
Received data: which=1&score=100
which:1
score:100

And in the Unity console you should see:
Submitting score
Form sent complete!
Response:OK

At the moment this is still pretty much test code and doesn’t handle security other than to check for idiots sending really POST data. You should really encrypt the data sent to the server to prevent even bigger idiots from spamming your server with false information.

Next I will be looking at redis as a persistent store for my leaderboard data. I will be extending the node.js server code to include this functionality. I may do a blog on redis soon and then one another that includes details on how to use redis to create a leaderboard.

2D water shader in Unity 3D

I recently found the need to spruce up the water in a new 2D game that I am working on, so I decided to have a play around with Unity shaders again. Here’s the code to the shader:

Shader "Unlit/WaterTexture"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_ScrollX ("X Scroll Speed", Range(-10, 10)) = 0
		_WaveSpeed("WaveSpeed", Range(0, 100)) = 10
		_FrequencyX("X Axis Frequency", Range(0, 100)) = 34
		_AmplitudeX("X Axis Amplitude", Range(0, 100)) = 0.005
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" }
		LOD 100

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float2 uv : TEXCOORD0;
				float4 vertex : SV_POSITION;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			fixed _ScrollX;
			fixed _FrequencyX;
			fixed _AmplitudeX;
			fixed _WaveSpeed;

			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				return o;
			}

			fixed4 frag(v2f i) : SV_Target
			{
				fixed time = _Time * _WaveSpeed;
				fixed2 uvs = i.uv;
				fixed offset = (_ScrollX * _Time);
				uvs.x += offset;
				uvs.x = fmod(uvs.x, 1);
				uvs.x += sin(uvs.y * _FrequencyX + time) * _AmplitudeX;
				fixed2 uvs2 = i.uv;
				uvs2.x -= offset;
				uvs2.x = fmod(uvs2.x, 1);
				if (uvs2.x < 0)
					uvs2.x += 1;
				uvs2.y = 1 - i.uv.y;
				uvs2.x += sin(uvs2.y * _FrequencyX + time) * _AmplitudeX;
				fixed4 col = tex2D(_MainTex, uvs);
				fixed4 col2 = tex2D(_MainTex, uvs2);
				col = (col + col2) / 2;
				return col;
			}

			ENDCG
		}
	}
}

The main section of the code is the fragment shader, it plays with the textures U coordinates by applying a sine wave to them, it then does the same with the pixel on the opposite side of the V axis then merges them both together, it also scrolls the textures, this produces a nice running water effect.

You can grab all shaders that I make public from Github

Feel free to re-use the shader in your own creations.

Cartoon Artwork

I got to the point developing hobby games where I was fed up of using programmer art work so I decided to learn how to create a little art work, so I got a hold of a copy of DrawPlus (vector art work software) and started to learn how to draw various things using vectors,. Whilst the quality is not FAB or anything near what a proper game artist produces, I have fun doing it and it looks passable. I’ve started to create some bits for my next game (it has no title yet) but its a large collection of mini-games. Here are a few bits:

Grumpies Characters

Grumpies Characters

Cartoon Buildings

Cartoon Buildings

Cartoon Tree

Cartoon Tree

Cartoon Fence

Cartoon Fence

Cartoon Cloud

Cartoon Cloud

Cartoon Bubble Character

Cartoon Bubble Character

Cartoon Pipes

Cartoon Pipes

Feel free to use any of the images on this page (with exception to the characters) in your own games.

Installing and running Node.js on a VPS

Introduction

I recently had the requirement to implement a global none app store specific leaderboard system that can track scores and players for a mobile game that I am developing in Unity3D. After much investigation node.js seems to be the technology to use to create a server to handle it. I want it to be as quick as possible without having to resort to C++, plus I’ve been looking for an excuse to play with node.js in more detail. Ok, so I went to ovh.net and purchased one of their super cheap VPS (Virtual private server). I opted for Debian 8 LAMP (this is the Debian 8 Linux OS with, Apache web server, MySQL and PHP). After reading up the set up instructions, I managed to get logged into my VPS using PUTTY on Windows.

Installing node.js on a VPS

First thing to do is install node.js to the VPS. In the Putty terminal type the following:

Download and run the Nodesource PPA (personal package archive) installer script:

cd ~
curl -sL https://deb.nodesource.com/setup_6.x -o nodesource_setup.sh
sudo bash nodesource_setup.sh

Install node.js:

sudo apt-get install nodejs

Install the build essentials for some packages that require compilation

sudo apt-get install build-essential

Testing out the node.js installation

Ok, so now we have node.js installed, its time to create a hello world and run it to ensure that it works. In terminal create hello.js:

cd ~
nano hello.js

If you do not have the nano text editor installed then you can install it as follows:

sudo apt-get install nano

Once the file is open add the following code:

#!/usr/bin/env nodejs
var http = require('http');
http.createServer(function (req, res)
{
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World!\n');
}).listen(8080, 'localhost');
console.log('Server is running at http://localhost:8080/');

Make the script executable:

chmod +x ./hello.js

Run the script:

./hello.js

Note that the script is blocking because it is sat in an infinite loop waiting for connections, so you can no longer type anything into terminal. To test, run another instance of terminal (Putty on Windows) and enter the following to test the script:

curl http://localhost:8080

You should see “Hello World” printed out which is the response from the hello.js script.

Installing and using Process Manager (PM2) for node.js

The next issue we need to look at is how to make our script run in the background so it does not block. To do that we need install a tool called PM2 (process manager for node.js):

sudo npm install -g pm2

Once installed we can set our script off running as a background process with:
pm2 start hello.js

And to make PM2 re-run when the server reboots:

pm2 startup systemd

Note that when PM2 restarts it will restart all of its running processes.

Getting node.js to work with Apache

The general idea is to run the node.js server on a different port and forward requests to a specific url to this port using a reverse proxy. To do this we need to update an Apache config file httpd.conf. Note that if you do not find this file in the /etc/apache2/ directory then you will need to create it and add the following text:

ProxyPass /node http://localhost:8000/
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so

And add the following line to /etc/apache2/apache2.conf to ensure that the file gets included

Include /etc/apache2/httpd.conf

Note that it is likely that the proxy module is not enabled on Apache, in which case enable it using:

a2enmod proxy_http

Now requests to http://www.yourdomain.com/node will be forwarded to localhost:8000, adjust the original hello world node.js script to listen on port 8000 and give it a test.

Hiding UI elements in Unity!

Ok, so recently I tried to find a way to hide UI elements in Unity 5, looked far and wide and apparently its not possible to do something like element.visble = false. So, the easiest solution I found was to set the game objects localScale to 0, e.g:

transform.localScale = new Vector3(0, 0, 0);

And to make visible again use

transform.localScale = new Vector3(1.0f, 1.0f, 1.0f);    // Or whatever original scale the object had

 

Tiny Bombs Boom! is a Go for Android, iPhone and iPad on Google Play

Tiny Bombs GO Icon

Tiny Bombs GO for Android

Tiny Bombs Boom! – Save the holiday inn hotel

Available FREE for Android on Google Play

Available FREE for iPhone and iPad on the App Store

Bad guys have hidden bombs on every floor of the holiday inn hotel, your job is to find and defuse them and save the hotel. Tiny Bombs Boom! mixes match-3 style game play with action creating a tense addictive mix as the player chases over 1000 floors eliminating explosives, new and unique ways of helping or punishing the player are added to the mix by way of power-ups and hazards.

Tiny Bombs GO in-game screen shot

Tiny Bombs Boom in-game screen shot

Tiny Bombs GO in-game screen shot

Tiny Bombs Boom in-game screen shot

As the player progresses from floor to floor of ever increasing difficulty, the game is expanded by unlocking mini-games. Each mini-game is unique and completely different from the main line of play. Each mini-game features progressive difficulty play that puts the player under ever increasing pressure to complete the next round.

Tiny Bombs GO in-game screen shot

Tiny Bombs Boom in-game screen shot

Tiny Bombs GO in-game screen shot

Tiny Bombs Boom in-game screen shot

Ten mini-games are available at launch with plans to add more as time goes by.

Special level unlock codes can be added to unlock the extra features, these will be released to the press over the coming months.