Sunday 6 November 2016

Perlin noise for ocean waves

Apologies for the tardiness of this post, I was busy the last few weeks as I was doing some work experience with a local software company. I had a chance to learn a few things about game development on consoles using C++.

Meanwhile, I put together a quick example on how to use Perlin noise to create basic waves in order to simulate ocean movement. The example you will see also allow to modify specular highlights, which was part of the project I was working on for the aforementioned company. The water texture came form here.

This is a picture of the final result:



Setting up

Drag in several planes and places them one next to each other and create a larger plane with all those combined of whatever size you want. 

For the shader, I used a slightly modified version of a standard pixel lighting shader, which you can find here. This post is about the wave movement, so I will not post anything about the shader itself, also because I pretty much used the one I linked.

The "Wave" script

We only need one script and, just like we previously saw in the other project which involved Perlin noise, it's very easy.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class Waves : MonoBehaviour {

 public float scale = 10.0f;
 public float speed = 1.0f;
 public float resolution = 1;

 Mesh mesh;
 MeshCollider mc;

 Vector3[] vertices;
 Vector3[] newvertices;

 float randomizer = 1.0f;

 void Start () {

 
  mc = GetComponent<MeshCollider> ();
  mesh = GetComponent<MeshFilter> ().mesh; 
 
 }
 

 void Update () {

  randomizer = -Time.time/2;
  vertices = mesh.vertices;
  newvertices = new Vector3[vertices.Length];

  if (resolution <= 0)
   resolution = 0;


  for (int i = 0; i < vertices.Length; i++) {

   Vector3 ver = vertices [i];
   ver.y = Mathf.PerlinNoise ( (speed * randomizer) +(vertices [i].x + transform.position.x) / resolution, -(speed * randomizer) + (vertices [i].z+ transform.position.z) / resolution) * scale;
   newvertices [i] = ver;

  }
                mesh.vertices = newvertices
  mc.sharedMesh = mesh;
  mesh.RecalculateBounds ();
  mesh.RecalculateNormals ();
 
 }
}

The 3 public variables will allow you to change the waves a bit, as you will see later.

The "randomizer" float parameter is nothing but a time variable.

As we can see, in the Start method we simply get our plane mesh and the mesh collider.
 We can choose whether to update the mesh collider or not and we'll see what difference will make in a moment.

In the Update method we update the randomizer variable first. We then get the vertices of the current mesh and create a new array of Vector3 which will store the new vertices. In the for loop we simply iterate through the vertices of the mesh and assign a new y value to them, in order to create height. This new vertex is then places in the recently created array which, at the end of the for loop, will replace the current mesh vertex array (line 41). On the next line we update the mesh collider as well, based on the just updated plane mesh.

If we were to omit line 42, which updates the mesh collider, this is what we will see in the scene editor:


Even though the plane mesh has waves, the collider is still flat. Obviously, that happens because we did not update the mesh collider which, currently, is still using the standard plane mesh.

If we assign the new plane mesh (with waves) to the mesh collider, the collider itself will match the shape of our plane.

Conclusion

This is an easy way to create ocean movement. Using the standard planes provided by Unity is no the best idea, we should use ocean meshes, which normally have much greater vertex density which allow for much smoother undulation.

Example scene here.

Monday 5 September 2016

Sprite depth for top down 2D

This is going to be a very short and quick post about sprites in Unity.
I discovered a nice simple trick on how to dynamically change the depth of a sprite for top down 2D games. For this project, I used this asset and the character comes from here.

Imagine you have your player character roaming around the game. If you simply place the sprites in the scene, they should automatically be set to the same Sorting Layer, and therefore, this should happen:



There is no depth whatsoever, the player is constantly drawn behind the trees, which is fine when he is walking "behind" them, but it looks just wrong when he is in front of them.

Of course, changing the player's sorting layer won't make any difference:


I therefore had to write a very simple script which allows the sprites to dynamically change their sorting layer according to their position.

This is DynamicSortingLayer script:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class DynamicSortingLayer : MonoBehaviour {

 public float yOffset;

 public float by;

 float layer;

 SpriteRenderer rend;

 Vector3 centerBottom;

 // Use this for initialization
 void Start () {
  
  rend = GetComponent<SpriteRenderer> (); 
 }
 

 void Update () {

  centerBottom = transform.TransformPoint(rend.sprite.bounds.min);

  layer = centerBottom.y + yOffset;

  rend.sortingOrder = -(int)(layer * 100);
 
 }
}

The sorting layer is assigned depending on the position of the sprite, however, when we get the position of the object we usually get its anchor position, which is normally its center. In this case, we want to get the position of the bottom of a sprite. For example, for our player, we want to assign the layer according to its feet Y position, because that's the point which determines whether he is in front or behind the trees.

That's what we do on line 22, we get the bottom-centre points of the sprite and store it into centerBottom. After adding an offset value to it (for minor adjustments), we multiply the Y value position for 100, in order to preserve decimal numbers as we are going to cast it as an int value. Of course, we also need to negate the final value, as we want the sprite at the lowest part of the screen (with lower Y value) to be in front of the other ones and they therefore need a higher value of sorting layer.

This is the final result:


Conclusion

A very quick tutorial on how to create depth in a 2D world. To optimize the script you could separate moving objects from static ones. For, example, the trees do not need the get the sorting layer constantly reassigned as they are not moving. You could assign the sorting layer once in the Start method and leave the Update empty. The player, however, will need to continuously change its sorting layer to ensure it will be drawn correctly in the scene. I placed some colliders onto our objects and put together a simple scene which you can try the WebGL version here.


Friday 19 August 2016

First person melee combat

I believe it's time for some more combat mechanics. In this section, we'll see how we can create a simple but effective melee combat system for a first person controller. The idea is to create something that resembles the combat system you saw in Skyrim or other RPGs of similar fashion.

I am going to use the first person controller found in the standard asset, plus additional assets found here and a nice skybox downloadable here. As for our foe, I found this asset which suits our needs perfectly.

Setting up the scene

Start by creating the scene. I dropped the first person character controller on a plane, placed a skybox to make the level look pretty and finally position the axe as a child of the first person camera in the controller. This is what the game window looks like:



And below is a srceenshot of the hierarchy as it looks after placing these objects in the scene:


You can see that the axe model is placed as a child of the FirstPersonCharacter, which is where the camera is.

Place a capsule collider on it and set it to trigger. You don't need to be super accurate, something like this should do it:


Create now a new layer, which we can call Axe, and set the axe model to this layer.

Since we are here, let's drag the goblin model in the scene too. That particular model looks a bit too big, so I scaled it down to 0.45 for each axis in its transform. Once in the scene, you can remove the script attached to it and place a capsule collider on it and make it fit nicely around the model. Also, the rigidbody should be checked as isKinematic.


This is pretty much all we need in the scene now. The goblin model already comes with some animation, which we are going to script later.

We should now have the player, with the axe, and our goblin, and they both should collide with each other. However, this is our first problem:



When we get close to the goblin model, the axe passes through it. Unless this is something you want to have, Also, if we had to use the collider on our weapon and not set it to trigger, it could be a little problematic for the player to move around, as it might get stuck in other models when rotating and so on.

There are a couple of ways we can fix this: we could write a shader for the axe in order to have it rendered on top of everything, or we can use 2 cameras. We are going with the latter.

Create a new camera and attach it to the FirstPersonCharacter and set the transform to 0,0,0. Let's call it OverlayCamera. The camera should now be in the exact position as the first person camera that comes with the FPScontroller.Now, in the inspector for this camera, set the Clear Flags to Depth Only, the Culling Mask to Axe, and the Depth to 1.



Now, the other camera, which is a component of FirstPersonCharacter. Leave the Clear flags to Skybox, change the Culling mask by removing the Axe layer, and leave the Depth to 0.

At this point, the OverlayCamera is rendering only the axe (or anything on the Axe layer) and it always draws on top of the FirstPersonCharacter camera, because of the Depth parameter of value 1.


No matter how close we get to the goblin, the axe will always be drawn on top.

Axe animation

We should now create an attack animation for the axe. Honestly, I'm really not the best person to show you how to properly create an animation in Unity, I'm still trying to master the art myself. However, I will show you a GIF picture of the final result so you can have an idea of what I am trying to achieve:


This is the attack animation. I also created a couple of idle animation, you will see them in the playable scene. Later on, we will need to add animation events.

The animation controller for the axe is shown below:


There is a sub state machine which I use to trigger some random idle animations, not something we should be concerned about for our purpose. We should in fact focus on the Axe_Attack state, which holds the animation just created. This state can be triggered by Any State via the trigger Attack and will return to the Axe_Idle state once the animation has reached the end. And don't forget to remove Loop from the Axe_Attack animation.

Here are the inspector screenshots of the 2 transition states side by side:


We can now start writing some scripts.

Scripting time


Create a C# script called Axe.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class Axe : MonoBehaviour {

 Animator anim;

 CapsuleCollider cc;


 // Use this for initialization
 void Start () {

  anim = GetComponent<Animator> ();

  cc = GetComponent<CapsuleCollider> ();
  cc.enabled = false;
 
 }


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

  if (Input.GetMouseButtonDown (0))    
   anim.SetTrigger ("Attack");  
 
 }

 public void ActivateCollider(int active)
 {
  if (active == 0)
   cc.enabled = false;
  else
   cc.enabled = true;
 }
  



 void OnTriggerEnter(Collider c)
 {
  if (c.gameObject.GetComponent<IAxeHittable>()!=null)
{                               cc.enabled = false;
    c.gameObject.SendMessage ("OnGetHitByAxe", 5);

 }
}
}

Pretty simple, after getting all our references we check in the Update function for any mouse click to trigger the attack animation.

The ActivateCollider method is called by animation events: when the axe is about half way extended, we activate the collider in order to trigger the collision. As the axe is animating back to idle position, we deactivate it.


This is necessary, otherwise every time we get close to the goblin it will register a collision, even if we are not attacking.

Also, we deactivate the collider immediately after hitting.

To detect whether the weapon has hit something we use interfaces. If the object collided with implements the interface IAxeHittable, we send a message to it, calling the method OnGetHitByAxe.

This is the interface:

1
2
3
4
public interface IAxeHittable {

 void OnGetHitByAxe(float hitValue);
}

Now, we can create a Goblin script which will implement the interface and attach it to our goblin model.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class Goblin : MonoBehaviour, IAxeHittable {

 // Use this for initialization
 void Start () {
 
 }
 
 // Update is called once per frame
 void Update () {
 
 }

 public void OnGetHitByAxe(float hitValue)
 {
  Debug.Log ("Goblin got hit by Axe!");
 }
}

As you can see, we MUST implement the method OnGetHitByAxe, which we'll use for handling damage to the enemy. At this point, all we do is to print to the console.

Conclusion

This is the basic structure on how to create some simple melee combat mechanics. As always, this can be improved to become a much more efficient and flexible system, which can be as complex or simple as your project would require.

Example scene here

Thursday 11 August 2016

Landscape generation with Perlin noise

Unless you have been living under a rock, you have probably heard of a game called Minecraft.
I'm a fan of the game and I was always fascinated by the idea of creating an infinite, randomized world. Minecraft uses something called Perlin noise to procedurally create the landscape.

This type of noise was created in 1983 by Ken Perlin, who ended up winning an Academy Award for technical achievement for developing the algorithm.

Perlin noise can be use with different purposes. If you look at a texture image of perlin noise in black and white you will quickly notice that it looks like a heightmap. A height map is just a representation of the different heights of a terrain and it's useful too display mountain chains, abysses, valleys, ecc.

Because of this property, this type of noise becomes extremely useful when creating random terrain.

Luckily for us, Unity has already a function which enables us to use Perlin noise without having to re write the whole algorithm ourselves. The method is Mathf.Perlin(float x, float y).

We are now going to create a Minecraft-like landscape using cubes and Perlin noise.

First, create a simple cube and apply a green material to it. This is going to be our "grass" cube.



Save it as a prefab as this is going to be the basic building block for the landscape.

Now, the script. You will be surprised how easy it can be to achieve such thing. Create an empty game object and a new C# script called LandscapeGenerator and attach it to it.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class LandscapeGenerator : MonoBehaviour {

 public static LandscapeGenerator instance;

 public GameObject grass;
 public int mapSizeX = 50;
 public int mapSizeZ = 50;

 public float heightScale = 20f;
 public float detailScale = 20f;

 float seed;
 // Use this for initialization
 void Start () {

  if (instance == null)
   instance = this;  
 
  

  for (int x = 0; x < mapSizeX; x++) {
   for (int z = 0; z < mapSizeZ; z++) {
    float y = 0;
    GameObject g = Instantiate (grass) as GameObject;
    g.transform.position = new Vector3 (x, y, z);
    g.transform.SetParent (this.transform);
   
   }
  }
 
 }
 
 // Update is called once per frame
 void Update () {

 }
}

I think the script is pretty easy and at this stage it won't do anything special, it simply lays out a basic,flat map for us.

As the cubes are all 1 by 1 in size, the 2 variables x and z are incremented by one, so the blocks are going to be placed one next to each other without any gap between them or overlapping.

This is the result you should see:


Rather boring. Imagine if the whole world of Minecraft was like this.

You might be tempted to modify line 23 to add some randomness to the Y value in order to create height for the plane. Using pure random functions, the line could become something like this:


23 
float y = Random.Range (0, 20);

With the value of 20 being the maximum height possible for a cube.

However, this is pretty much what you are going to end up with:


I don't think you would be able to have players walking around this "world".

This is because pure randomness is simply not suitable for our purpose, and this is where Perlin noise come in. The randomness of this algorithm is smoother and will give us the effect we trying to achieve.

Let's modify that line again implementing the Perlin noise function:

23
int y = (int)(Mathf.PerlinNoise ((x + seed) / detailScale, (z + seed) / detailScale) * heightScale);

This is a standard implementation of the Perling function. As you can see, there is also a seed variable added to the x  and y parameters. If you omit that, every time you run the game you will obtain the same exact landscape. The Perlin function will return a value based on the 2 float parameters passed in. If such parameters are always the same, the value returned will be the same each time. The seed variable is defined as such:


seed = (float)Network.time;

This simply generate a random value which is then added to the 2 parameters.

I could go on and explain the purpose of the 2 parameters detailScale and heightScale, but I believe I will just confuse you, therefore, I created a simple scene which will let you see the effects that thos 2 variables have when used in our method.

Check it out here.

In the end, what you should see is something like this:


I think you will agree with me when I say that this looks much more playable than the previous one.

We can take things a bit further by adding different block types, let's say snow and sand.

Just create 2 other prefabs made of a cube and 2 materials, one yellow(ish) and one white.


We can then modify the Start function of our script using if statements to check the Y value returned by the Perlin function and instantiate a different block prefab accordingly:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
 int y = ()int(Mathf.PerlinNoise ((x + seed) / detailScale, (z + seed) / detailScale) * heightScale);
 GameObject g;

 if(y<=5)
 g = Instantiate (sand) as GameObject;

 else if(y>=(heightScale-5))
  g = Instantiate (snow) as GameObject;

 else
  g = Instantiate (grass) as GameObject;
    
 g.transform.position = new Vector3 (x, y, z);
 g.transform.SetParent (this.transform);

As you can see, all we do is to check the y value returned by the Perlin method and we instantiate different types of cubes depending on their Y position.

Now we have a more colorful landscape:


Just as a final touch, we could add some water. Create a water prefab cube, set its collider to trigger (so the player can "walk in it") and adjust its color with a slight transparency.

Then we can add the code to spawn water in the landscape. Please bear in mind that this is quick and dirty way to do it, just for the sake of having some water in our scene.

While referring to the previous script, I modified the code starting from line 4, like this:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
 if (y <= 5) {
   g = Instantiate (sand) as GameObject;
   g.transform.position = new Vector3 (x, y, z);
   int tempY = y+1;

   while (tempY < 5) {
    GameObject w = Instantiate (water) as GameObject;
     
     
    Vector3 pos = new Vector3 (g.transform.position.x, tempY, g.transform.position.z);
    w.transform.position = pos;
    tempY++;
   }

  }

All this does is to instantiate the water gameobject on top of the sand blocks. A very cheap way to add some water, but it will work for our purpose.

Here's the scene now:


Conclusion

This is a decent example on how to implement Perlin noise to create a random landscape. Minecraft uses the exact same technique to achieve the effect, and as you can see, it works quite well. We could expand on this (and perhaps, I will) by adding cave generation and digging system, something I might consider doing in the future. To conclude, I just added the FPS controller which you can find in the Standard Assets and I put a nice skybox (this one) so you can test the scene out here.

Thursday 4 August 2016

Pathfinding part 2: the map editor

This post is a sort of follow up of the previous one. I thought I would show you how I created the simple level editor to layout the map I used for the path finding tutorial.

This will involve editor scripting and, although is very simple, is a good demonstration on how we can manipulate the Unity editor to improve workflow and create tools.

Obviously, every project is different and will require a different approach. For my particular case, I needed to create a tile map which could be made of 3 different types of tiles: grass, sand and wall. I didn't want to start from an empty map, so the way I did it was to create an initial map of a certain size of all grass tiles and then place the sand and wall tiles. That was my idea and that is what I'm going to show you.

By creating a map editor we can then create many more maps very quickly without having to place each tile prefab one by one, which can be very time consuming.

There are 2 main scripts for this project: one is the Map script, which is the one that featured the GeneratePath method we saw in the last post. This script will only be in charge of laying out the tiles and it's the script we will reference in the second script: MapEditor. This simply overrides the inspector for the Map script and is in charge of all the editor stuff.

Before I show you the scripts, I will display a picture of the inspector:

Fig 1

I highlighted to sections of the inspector with 2 different colors for a reason you will see shortly.

So, this is the MapEditor script:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
[CustomEditor(typeof(Map))]
public class MapEditor : Editor
{
 Ray r;
 RaycastHit hit;
 bool md = false;

 string[] tiles = { "Grass", "Sand", "Wall" };
 int ind = 0;

 public override void OnInspectorGUI()
 {
  Map map = (Map)target;
  DrawDefaultInspector ();

  if (GUILayout.Button ("Generate MAp")) {
   map.GenerateMap (); 
   //SerializedObject obj = new SerializedObject(map.ti
  }
   
  ind = EditorGUILayout.Popup ("Choose Tile",ind,tiles);
 }

 void OnSceneGUI()
 {

  int controlID = GUIUtility.GetControlID (FocusType.Passive);

  r = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
 
  if (Physics.Raycast (r, out hit, 100, LayerMask.GetMask ("Tile"))) {
   
   
   if (Event.current.type == EventType.mouseDown) {
    GUIUtility.hotControl = controlID;
    md = true;

   }

   if(md)
    hit.collider.gameObject.GetComponent<Tile> ().ChangeType (ind);

  }

  if (Event.current.isMouse && Event.current.type == EventType.MouseUp) {
   GUIUtility.hotControl = 0;
   md = false;
  }


 }


}

Some of the code has been already explained in a previous post I made related to custom inspector. Line 1 and the inheritance from Editor on line 2 are necessary when creating a custom inspector.

Let's focus on the 2 methods in the script.

OnInspectorGui is overridden in order to draw a new inspector. Firstly, we get a reference to the script we want to redraw the inspector for, which is the Map script. The target variable is a built in variable that represents a reference to the object, which needs to be casted accordingly (line 13).

Then we draw the default inspector (line 14), which will display all the public variables that are highlighted in the yellow rectangle in Fig 1. And that's what DrawDefaultInspector() does, simply displays the inspector you would expect to see if you were not overriding it.

It is important to point out that, if you decide to override the drawing process with the method OnIspectorGui(), you MUST call the DrawDefaultInspector() function. If you omit that, this is what you will get:


You'll get nothing. Once you override that method, all the inspector drawing is handled to you.

Going back to the script, on line 16 I created a button which will be displayed in the inspector. By pressing the button, the GenerateMap() method in the Map script is called, which we are going to analyze next.

After that, on line 21 I created a pop up field which will let the user choose what tile to place. these 2 elements (button and pop up field) are visible in Fig 1 in the red rectangle.

The EditorGUILayout.Popup method will return an integer that represents the index of the item selected. As for the lists of selectable items, we pass an array of strings as parameter.

Before move to the next part, OnSceneGui(), let's quickly have a look at the GenerateMap() function in the Map script. I won't post the whole script as there are other things that are unrelated to our purpose, so let's see the method only.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
 public void GenerateMap()
    {
  tileParent = new GameObject("Tiles");
  tiles = new GameObject[mapWidth, mapHeight];
  tilescale = grassPrefab.transform.localScale.x;
  levelEditorMap = new int[mapWidth, mapHeight];

  for (int x = 0; x < mapWidth; x++)
  {
   for (int y = 0; y < mapHeight; y++)
   {
    levelEditorMap[x, y] = (int)Tiles.GRASS;
   }
  }

        GameObject g = null;

        for (int x = 0; x < mapWidth; x++)
        {
            for (int y = 0; y < mapHeight; y++)
            {

  currentPosition = new Vector3(startingPosition.x + (float)(x * (tilescale + 0.05f)) , startingPosition.y + (float)(y * (tilescale + 0.05f)), 0);
  g = Instantiate (grassPrefab, currentPosition, Quaternion.identity) as GameObject;
  g.GetComponent<Tile> ().type = (int)Tiles.GRASS;
  g.transform.SetParent(tileParent.transform);
                g.GetComponent<Tile>().arrayX = x;
                g.GetComponent<Tile>().arrayY= y;
                g.GetComponent<Tile>().mapReferece = this;
  tiles[x, y] = g;

              
            }
           
        }
  Debug.Log ("Length now is " + tiles.Length);
  GeneratePathGraph ();
    
    }

I initialize all the objects I need first, and I iterate through the 2 dimensional array levelEditorMap and initialize each element to a grass type tile. The tileParent object is just a parent object for all the tiles that we are going to spawn when generating the map. The other 2 dimensional array tiles will contain the actual gameobjects, the "physical" tiles we see in the scene, whereas levelEditorMap is the "logical" part. Honestly, there is really no need to separate the two, but that's how the path generation logic was set up previously so I left it as it is.

From line 18 to 35 is where the generation occurs. the position of each tile is represented by the Vector3 currentPosition. For both X and Y axis I added a little offset of 0.05 which will place a little space in between the tiles so they can be easily distinguished from each other. Other thatn that, all I do is to instantiate a grassPrefab (a simple quad colored green), assign some references to the Tile script of each instantiated grass prefab clone and that's it. Simple enough.

This is what you will see once you press the "Generate Map" button:


You can see the parent object highlighted in the red rectangle in the hierarchy and the grass tiles instantiated in the scene. Remember, the game is not running, this happened in the editor.

Now we can go back to the OnSceneGui method of the MapEditor script.

The hot control id is taken as it will become usefull when we are placing the tiles. Because we are placing objects by clicking we don't want to select the just placed object in the inspector, otherwise we will loose the focus that is now on the object containing the MapEditor script. That means that every time we place a tile we will need to re select that object in order to place another tile because we have now selected the in the inspector the tile we have just placed. I know, confusing, try it yourself by commenting out lines 27, 35 and 46, see what happens.

On line 29 we raycast from the camera. I know that raycasting is usually done differently (Camera.main.ScreenToPointRay...), but when we are doing a raycast in the editor, that is how you proceed. It took me a while to find out and I was glad when I did.

Then I simply check that the ray we are casting is hitting the tiles (line 31). If that's the case and we are clicking (line 34), we set the boolean variable md (mouse down) to true. When that variable is true, the tile that is being hit by the raycast will change type (I will show that script next) according to the index of the tile chosen with the pop up field we saw earlier in the inspector. the reason why I used a boolean variable is that so I can keep the mouse button down and drag the cursor around in order to change the type of tile as I go, without having to keep clicking for each tile.

Last pieace of code, when the mouse is released, we let go of the hot control (line 45 to 48).

To confuse you even more, check the ChangeType method in the Tile script:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public void ChangeType(int t)
 {
  type = t;

  switch (t) {
  case (int)Map.Tiles.WALL:

   AssignColorToMaterial (this.gameObject, grey);
   originalColor = grey;
   movementCost = Mathf.Infinity;

   break;

  case (int)Map.Tiles.GRASS:
   AssignColorToMaterial (this.gameObject, green);
   originalColor = green;
   movementCost = 1;
   break;

  case (int)Map.Tiles.SAND:
   AssignColorToMaterial (this.gameObject, sand);
   originalColor = sand;
   movementCost = 15;
   break;
  }
 }

This method will change the type of the tile (color and movement cost) according to the integer passed. If you don;t know about movement cost and why it's there, please read the previous post about path finding.

One important thing to notice is how we assign the new color to the material. You would be tempted to simply get the renderer reference and change the material color as you normally would in a script, but you cannot do that in the editor. This will cause a material leaking error.

If you want to achieve this, you will need to do a trick. That trick is the AssignColotToMaterial function, which is displayed below:



1
2
3
4
5
6
void AssignColorToMaterial(GameObject g,Color c)
 {
  var tempMaterial = new Material(g.GetComponent<Renderer>().sharedMaterial);
  tempMaterial.color = c;
  g.GetComponent<Renderer>().sharedMaterial = tempMaterial;
 }

We basically create a temporary material, change its color and assign it to the gameobject. This step is necessary in order to to what we want.

That's all folks.

This is all probably very confusing and I was struggling myself to understand what is going on, however, as it always is very much the case in coding, is all about trying it yourself and getting your hands dirty.

I hope this post will help you getting started in editor scripting and creating an awesome custom level editor.

Wednesday 6 July 2016

Pathfinding for tile maps

Unity already comes with a pretty awesome feature which can be used for path finding and obstacle avoidance. I'm of course referring to the Nav Mesh and its other components.

However, I found that not very useful when it comes to 2D and tile map kind of games. It is of course possible to use it if you decide to utilize quads and planes, but I wanted to implement my own path finding system.

It turns out, it is not really hard, especially when there are algorithms out there that have been tested and used for years. For this project, I will show you how I wrote my "own version" of the Dijkstra algorithms. No, not the guy from The Witcher 3.

I will not post the whole project code as it is extensive and rather messy at this stage, however, I will put the main functions and classes needed.

First of all, we need a map, a simple grid of objects and our player of course.
In my experiment I created a small "level editor" by coding a custom editor so it would be easier to layout an awesome map.

And this is the awesome map itself:


Awesome indeed.

As you can see, I have 3 types of tiles: the "grass" tile, green color, the "sand" tile, yellow(ish) color and the "wall" tile, grey color.

The reason is to show how smart this algorithm can be. Every type of tile has a "cost": the grass tile costs 1 to move in to, the sand tile 15 and the wall is pretty much set to infinity.

When calculating the path you will see that the player (the little capsule on the bottom left corner) will attempt to avoid the sand when possible as it is "too expensive" to move in to. In simple words, it will try to find the least expensive path, which is not necessarily the shortest.

Here's a picture which illustrates this:


The player is trying to get to the tile in the red circle. It could very well cross the "sand field" by walking straight down south, instead, the calculated path would rather go around it. Each sand tile costs 15 action points (let's call them such) to move in to, and the player would have 5 sand tiles in front of him to reach the target tile, which makes a total of 75. By going around it as shown by the line, he would have to traverse 10 grass tiles, which cost 1 AP each, for a total of 10. So, 10 APs are much cheaper than 75.

The first class we need is the Node class:



public class Node  {

    public List<Node> tileNeighbors;
    public Map mapReference;

    public int x, y;

    public float distanceFromSource;
    public Node previousNode;

    public Node()
    {
        tileNeighbors = new List<Node>();
  //Debug.Log ("List initialized");
    }

    public float DistanceTo(Node n)
    {
        float d = 0;
        Vector3 current = mapReference.GetTilePositionAt(x, y);
        Vector3 to = mapReference.GetTilePositionAt(n.x, n.y);
        d = Vector2.Distance(current, to);

        return d;
    
    }


}

This class represents each tile on the map. The most important parameters in this class are: the list of nodes, which will contain all the neighbors tiles, the float distanceFromSource, which is the tile's physical distance form the source and a reference to the previous Node with the shortest path.

As part of the Dijkstra algorithm, we need to generate a so called "graph" with this Node class.
A graph is nothing but a two dimensional array of nodes.

  void GeneratePathGraph()
    {

        graph = new Node[mapWidth, mapHeight];

        //Populate neighbours list
        for (int x = 0; x < mapWidth; x++)
        {
            for (int y = 0; y < mapHeight; y++)
            {
                graph[x, y] = new Node();
                graph[x, y].mapReference = this;
                graph[x, y].x = x;
                graph[x, y].y = y;


            
            }
        }

  for (int x = 0; x < mapWidth; x++) {
   for (int y = 0; y < mapHeight; y++) {

    if (x > 0) {
     graph [x, y].tileNeighbors.Add (graph [x - 1, y]);

     //Diagonals
     /*
     if (y < mapHeight - 1) {
      graph [x, y].neighbours.Add (graph [x-1, y + 1]);
     }
     if (y > 0) {
      graph [x, y].neighbours.Add (graph [x-1, y - 1]);
     }*/
    }
    if (x < mapWidth - 1) {
     graph [x, y].tileNeighbors.Add (graph [x + 1, y]);

     //Diagonals
     /*
     if (y < mapHeight - 1) {
      graph [x, y].neighbours.Add (graph [x+1, y + 1]);
     }
     if (y > 0) {
      graph [x, y].neighbours.Add (graph [x+1, y - 1]);
     }*/
    }
    if (y > 0) {
     graph [x, y].tileNeighbors.Add (graph [x, y - 1]);
    }
    if (y < mapHeight - 1) {
     graph [x, y].tileNeighbors.Add (graph [x, y + 1]);
    }
   }
  }     
    
    }

With this function I generate this array and populate it with nodes. Also, through a second iteration, I populate the list of neighbors of each node with the neighbors tiles. As you can see, I commented out the diagonal tiles, and that is simply because I do not want diagonal movement. If you wish to have it, simply uncomment those lines.

Now, to the path generating function.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
   public  void GeneratePath()
    {

       
  
       List<Node> uncheckedNodes = new List<Node>();
       List<Node> currentPath = new List<Node>();

       Node sourceNode = graph[unitToMove.GetComponent<Unit>().currentTileX,
           unitToMove.GetComponent<Unit>().currentTileY];

       Node targetNode = graph[targetX,targetY];

    

  sourceNode.distanceFromSource = 0;
  sourceNode.previousNode = null;

 foreach (Node n in graph) {

   if (n != sourceNode) {    
    n.distanceFromSource = Mathf.Infinity;
    n.previousNode = null;
   }
   uncheckedNodes.Add (n);
  }

  while (uncheckedNodes.Count > 0) {   
 
   Node nodeWithMinDistance = GetNodeWithMinDistance (uncheckedNodes);

   if (nodeWithMinDistance == targetNode)
    break;
   
   uncheckedNodes.Remove (nodeWithMinDistance);

   foreach (Node n in nodeWithMinDistance.tileNeighbors) {
    float distance = nodeWithMinDistance.distanceFromSource + n.DistanceTo(nodeWithMinDistance)+ GetTileMovementCost (n.x, n.y);
   
    if (distance < n.distanceFromSource) {
     n.distanceFromSource = distance;
     n.previousNode = nodeWithMinDistance;
    }
   }
  
  } // Close while

  if (targetNode.previousNode == null) {
   Debug.Log ("No Path foud");
   line.SetVertexCount (0);
  }
  else {

   Node current = targetNode;
   while (current != null) {
    currentPath.Add (current);
    current = current.previousNode;
   }
 
   currentPath.Reverse ();
   unitToMove.GetComponent<Unit> ().currentPath = currentPath;

   DrawPath (currentPath);
  
  }


    }

Declare 2 lists of nodes, one for the unvisited nodes and the other one which will hold the generated path.

After that, create 2 node, the source node and the target node. The source node coordinates are taken from the Unit script, which is attached to the player. The script simply raycast downwards and detect the tile underneath and retrieves its coordinates, so it is constantly updated.

Similar thing for the target node, this time the mouse will update the coordinates, again, by raycast.

We then initialize the source node: its distance from the source is 0 (that node IS the source) and there's no previous node before it.

Next, we initialize every node in the our graph array: set their distance to infinity and the previous node reference to null. Also, add every node to the list of unvisited nodes.

On line 28 we start iterating inside this list. We create a temporary node object (nodeWithMinDistance ) and we set it to the node in the list with the least distance from the source. That's what the method GetNodeWithMinDistance(...) does, simply iterates inside the list and return the one with the minimum distance. In the first iteration, it should return the source node, as it has a distance of 0 and all the others have infinity.

This is the point where we check if we have reached our target node, in that case we break out of the while loop.

Also, very impostant, we remove the returned node from the list of unvisited nodes.

If not, we iterate inside the neighbor list of the returned node.

For each neighbr node we assign it a value to the  distanceFromSource parameter. This value, temporarily stored in the distance parameter, is calculated by adding the value of the same parameter of the current node (the one containing the neighbor list we are iterating in. In the first iteration of the while loop, is the source node), added to the physical distance between the tiles and also the movement cost.

Here is what's happening:

Let's assume that we are considering diagonals too. Starting from the source node on the bottom left, we have 3 neigbors tiles. The one above will have a distance from the source of 2. That is because the distance of the source node (which is 0) is added to the physical distance of the 2 tiles (which is 1 in this case as the tiles are nothing but 1x1 cubes) and its movement cost is added as well, which is 1 (because it's a grass tile). The total is 2.

For the diagonal tile, the distance is 2.4 because the physical distance between that tile and the source node is 1.4 (square root of 2 basically), plus its movement cost of course.

It is important to consider the physical distance for diagonal tiles for one reason: if diagonal tiles are considered at the same distance as adjacent tiles, this could happen:



Going frm tile A to tile B, the path generated has no reason to prefer adjacent tile to diagonal tiles, as they are mathematically at the same distance. So rather than going through a straight line, it might decide to act like in the picture above.

By calculating the physical distance of each tile, diagonals will have a value of distance from the source a little higher, which makes them a less favorable path to follow.

Continuing with our script, if the distance calculated is smaller than the current distance, that means that a shorter path has been found and the distanceFromSource parameter is updated.

This process will continue for each node until we reach the target node.

When we do, the program will exit the while loop.

At this point, if the previousNode of the target node is null means that no path was found, so the while loop iterated through the whole list of nodes without finding a route.

If not, we simply populate the currentPath list by grabbing the previousNode node in each previousNode reference of each node starting from the target node. This is a linked list technique, you can find plenty of tutorials on it online.

At this point the currentPath list contains all the nodes, from target to source. After reversing it, we can assign it to the player (called unitToMove) so it has a path to follow and we can draw the path using line renderers.

It is important to kow that with this system, the player will still be able to travers WALL tiles if no other path is found. That is because the value assigned is not (obviously) infinity, so it can be calculated. To avoid that we can simply detect if the path goes through tiles of type wall and break it when it happens.

Nonetheless, this is one of the ways we can implement path finding for tile based games.

Example scene here.


Tuesday 21 June 2016

Using "ghosts" to create a "wrapped" world

Ok the title of this post could be a little misleading.

What I want to show you with this small project is how we can create a wrapping effect for our player in a 2D environment. And what I mean by that is I'm trying to achieve the "Pac-Man" effect or the "Asteroid" effect: the player exits the screen from one end, reappears from the other one.

These are the assets used: tiles and the racoon.

Here is a screenshot of the effect I'm talking about:


As you can see by my beautiful arrows, the character is leaving the screen to the left and coming back in the screen from the right side.

To achieve this effect we use ghosts.

What are ghosts you say? Well they are nothing more than copies of the player object which move in relation to it outside of the visible screen.

If we look at the scene, this is what is actually happening:



The "real" player character is the one in the red circle, the other ones are just copies of it.

For this particular project I used only 3 ghosts, you can have up to 8 if you want the effect to work for all directions. I didn't want, for example, the character to pop out from the bottom whenever I jumped from the very top platform. That was a choice I made, which is not necessarily what you might want for your game.

We can achieve this with one simple script:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
public class Ghosts : MonoBehaviour {

 float screenWidth;
 float screenHeight;

 Vector3 topLeft;
 Vector3 bottomRight;

 GameObject[] ghosts;
 Vector3[] ghostPosition;

 // Use this for initialization
 void Start () {

  topLeft = Camera.main.ViewportToWorldPoint(new Vector3(0,1,transform.position.z));
  bottomRight = Camera.main.ViewportToWorldPoint(new Vector3(1,0,transform.position.z));

  screenWidth = bottomRight.x - topLeft.x;
  screenHeight = topLeft.y - bottomRight.y;

  ghosts = new GameObject[3];
  ghostPosition = new Vector3[3];

  ghostPosition [0] = new Vector3 (screenWidth, 0, 0);
  ghosts[0] = Instantiate (this.gameObject, transform.position + ghostPosition[0], this.gameObject.transform.rotation)as GameObject;

  ghostPosition [1] =  new Vector3 (-screenWidth, 0, 0);
  ghosts[1] = Instantiate (this.gameObject, transform.position + ghostPosition[1], this.gameObject.transform.rotation)as GameObject;

  ghostPosition [2] =  new Vector3 (0, screenHeight, 0);
  ghosts[2] = Instantiate (this.gameObject, transform.position + ghostPosition[2], this.gameObject.transform.rotation)as GameObject;

 foreach (GameObject g in ghosts) {
   DestroyImmediate (g.GetComponent<Ghosts> ());
   DestroyImmediate (g.GetComponent<Rigidbody2D> ());
   DestroyImmediate (g.GetComponent<BoxCollider2D> ());
   DestroyImmediate (g.GetComponent<Movement> ());
  }
 
 
 }

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

  CheckGhostsPosition ();
  PlaceGhosts ();
 
 }

 void CheckGhostsPosition()
 {
  foreach (GameObject g in ghosts) {
   if (g.transform.position.x < bottomRight.x &&  g.transform.position.x > topLeft.x &&
    g.transform.position.y > bottomRight.y && g.transform.position.y < topLeft.y)
    this.gameObject.transform.position = g.transform.position;
  }
 }
 void PlaceGhosts()
 {
  int i = 0;
  foreach (GameObject g in ghosts) {
   g.transform.position = transform.position + ghostPosition [i];
   g.transform.rotation = transform.rotation;
   g.transform.localScale = transform.localScale;
   i++;
  }

 }
}

The character gameobject also has 2 other scripts attached, one for the movement and one for the animation. I have separated movement and animation for a reason which I will explain shortly.

To begin with, we need to get the screen width and height in world space: this is what happens from line 15 to 19. I get the bottom right and top left corner position of the screen in world space coordinates and I calculate the width and the height of the viewport.

This is done because the ghosts, for example, the 2 side ones will be placed a screen width distance away from the main character.

From line 24 to 31 I instantiate the 3 ghosts, store them in an array and place them accordingly. I also store their position in a Vector3 array so it makes it easier to iterate their position later in the script.

The foreach loop on line 33 is used to eliminate all unnecessary components from the ghosts I just instantiated. First of all, the Ghosts script that we are writing right now: we don't want the new instances to create new ones themselves or this will result in a infinite amount of ghosts!

I eliminate the Rigidbody2D  and the CircleCollider2D as I don't want the ghosts to bump into things while off screen. Also, they don't need the Movement script, as they will be placed around the scene from this script. The only component they need is the CharacterAnimation script, which simply handles their animation just like the main character.

The CheckGhostsPosition() method is called every frame and it checks for the position of every ghost. If one of them happens to be within the screen, the main character will be immediately "teleported" to that position so that the ghosts will be again placed outside the visible screen.

Finally the PlaceGhosts() simply iterate through all the ghosts and place them according to the main character position.

I will post the other 2 script, CharacterAnimation and Movement, however, I will not go through them
because, well....they are so simple.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public class CharacterAnimation : MonoBehaviour {

 Animator anim;

 // Use this for initialization
 void Start () {

  anim = GetComponent<Animator> ();
 
 }
 
 // Update is called once per frame
 void Update () {

  float x = Input.GetAxis ("Horizontal");
  anim.SetFloat ("Speed", Mathf.Abs(x));


 }
}



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public class Movement : MonoBehaviour {

 public float runSpeed = 5;
 public float jumpForce = 50;


 Rigidbody2D rb2d;
 CircleCollider2D cc2d;



 bool isFacingRight = true;
 public bool grounded;

 // Use this for initialization
 void Start () {

  rb2d = GetComponent<Rigidbody2D> ();
  cc2d = GetComponent<CircleCollider2D> ();
 
 }
 
 // Update is called once per frame
 void Update () {

  float x = Input.GetAxis ("Horizontal");

  MoveChar (x);    

  if (Input.GetKeyDown (KeyCode.Space) && grounded) {
   rb2d.AddForce (new Vector2 (0,jumpForce),ForceMode2D.Impulse);
   grounded = false;
  }
 }

 void MoveChar(float x)
 {
  Vector3 nextPos = new Vector3 ( x * runSpeed * Time.deltaTime, 0,0);

  transform.position = transform.position + nextPos;

  if (x > 0 && !isFacingRight || x < 0 && isFacingRight)
   Flip ();

 }

 void Flip()
 {
  transform.localScale = new Vector3 (transform.localScale.x * -1, transform.localScale.y, transform.localScale.z);
  isFacingRight = !isFacingRight;
 }

 void OnCollisionEnter2D(Collision2D c)
 {
  if (c.collider.tag.Equals ("Ground"))
   grounded = true;

 }




}


An example scene is playable here.

Tuesday 31 May 2016

Yet another unfinished project

This is "High Five Knight", a small idea me and my girlfriend attempted to put together.

It's still in progress, although we do not have much time to work on it, especially her as she provides all the art and she is very busy doing other things.

So I though I would put it here as it might never get released :)

It's a very simple infinite runner, designed for mobile devices. The main character keeps running and comes across NPCs of 2 types, he has to high five the "friendly" ones and punch the foes. And of course, jumping over gaps.

For this project I created all the animations, using the mecanim system, wrote all the scripts and all the shaders. The game is optimized for mobiles, all the spawning objects are written with object pooling and all the shaders are as efficient as possible. The only light processed is ambient light and I used a Stencil buffer for the main character shadow so it only renders on the ground tiles.

I had to spend a lot of time with the profiler to understand how to obtain the best frame rate and how to speed the game up, eventually I managed to get it to run the best I could.

There is no music but you will hear sound effects.

I hope to find to time to finish it one day. Nonetheless, it is a fun little project that I am happy to leave here on the blog as a good example of a simple mobile game.

Here are some screenshots:


The APK file is downloadable here