Thursday, February 18, 2016

Unity waypoint system with NavMesh

Unity version: 5.3
Mode: 3D 
Level: Beginner

I wanted to create a patrolling NPC for my game so that requires a waypoint system. It is fairly easy to do with the NavMeshAgent.

First let's create our test scene and add a couple of empty GameObjects.

Create a GameObject as a container, name it WaypointsForNPC, or whatever is your character's name, just so you know who they belong to. Make sure the location is at zero.


Create a child GameObject and name it Waypoint, you should add an icon to it so you can see the location in the editor. I have provided a simple 'flag' icon you can import as an asset. You can then just make it a prefab and make copies, name them accordingly.




Place the waypoints on the map, on terrain or plane (as long as it's static and generally walkable)



Next, create the NPC. Here we just use a sphere object. Add NavMeshAgent to it.


Next, use the Navigation tab and hit Bake in the bottom. This will bake the navigation mesh for you and it should show the walkable area in the scene view.

Now let's start coding. Select your NPC and add a new script. Let's call it NPCWalkToWaypoint. Open up the script and add these lines before the Start() -method:

    public Transform[] waypoints;
    public bool loop = false;

    private NavMeshAgent agent;
    private int waypointCount, waypointIteration;


First is an array of Transforms. These are your waypoints. Next is a boolean value, determining if we should loop the walking, meaning the NPC will walk forever between the waypoints. NavMeshAgent is the component you use to move the NPC and the two integers are used to control what waypoints to use.

Now let's initialize those values in Start() -method:

    agent = GetComponent<NavMeshAgent> ();
    agent.SetDestination (waypoints [0].position);

    waypointCount = waypoints.Length;
    waypointIteration = 0;


Alright, first we fetch the NavMeshAgent component from the NPC this script is attached to, then we set its destination to the first waypoint in the array. Notice we don't do any checks if the array is empty, for simplicity's sake we just assume it has something.

Onwards to Update() method:


    if (agent.hasPath) {
        if (agent.remainingDistance < .5f) {
            if (waypointIteration < waypointCount) { 

               agent.SetDestination (
                   waypoints [waypointIteration].position);
                   waypointIteration++;
            } else {
                if (loop) {
                    waypointIteration = 0;

                }
            }
    }



First we check if the agent has any path calculate currently (meaning its destination is set). Then we check if we reached the destination. You can play with the value here but for my purposes I found 0.5 works well. If yes then we check if we have any more waypoints to travel to. If no more is found, just set the iteration to 0 and the next update will send the NPC to the first waypoint.


Head back to the editor and drag the waypoints to the array on the component.



Hit play and witness your patrolling NPC.



Whole script:

using UnityEngine;
using System.Collections;

public class NPCWalkToWaypoint : MonoBehaviour
{

    public Transform[] waypoints;
    public bool loop = false;

    private NavMeshAgent agent;
    private int waypointCount, waypointIteration;

    void Start ()
    {
        agent = GetComponent<NavMeshAgent> ();
        agent.SetDestination (waypoints [0].position);

        waypointCount = waypoints.Length;
        waypointIteration = 0;
    }

    void Update ()
    {
        if (agent.hasPath) {
            if (agent.remainingDistance < .5f) {
                if (waypointIteration < waypointCount) {
                    agent.SetDestination (waypoints [waypointIteration].position);
                    waypointIteration++;
                } else {
                    if (loop) {
                        waypointIteration = 0;
                    }
                }
            }
        }
    }
}

Thursday, August 20, 2015

Game Audio Jam

After playing some games in the GameBoy Jam 4, I wanted to try organizing my own jam. Since sites like GameJolt and itch.io provide the framework for that, I thought I'd go for it.

I chose to do it on GameJolt for no particular reason other than I'm more familiar with the site. Starting the game jam is really easy. You just pick a name and corresponding hashtag and start writing rules, adding pages and resources.

I picked Game Audio as the topic because I haven't seen a jam that explicitly focuses on sounds and music. It is interesting to see what kind of cool audio experiences people can create.

The challenge in the beginning is to find people to join. The link has been shared on Twitter and Reddit during the first 2 days since creation and shows promise, people seem interested in the topic. Another challenge is that I picked a time possibly too close to bigger jams. People might have exhausted their creativity in Ludum Dare, which starts this weekend. To compensate that, I gave participants 3 weeks time to finish the game and started promoting the jam a week ahead of starting time.

The jam starts 26th of August 2015. Anyone can join and if your sound skills are rusty or non-existent, use the jam to learn more.

See the rules here: http://jams.gamejolt.io/gameaudiojam

Saturday, May 9, 2015

What's going on?

It's been a long time since I posted anything. My blog seems to get visitors every day and I guess a lot of you are looking for continued tutorial for the RPG/Roguelike. I am not sure if the next part will ever come but it looks like the Unity team has created their own tutorial for this.

http://www.unity3d.com/learn/tutorials/projects/2d-roguelike

I know some of you don't like video tutorials but this looks pretty decent and straightforward to me, and more professional anyway. ;)

Thanks for reading my tutorials, perhaps I will continue with something else. For now, those vids could be the best thing for you if you're looking for making a 2D roguelike with Unity.

Sunday, September 21, 2014

Post delays

Thanks for checking this blog, if you're here for the Unity RPG tutorial, I apologize for the delay of the coming post. I have written some of it but have been busy elsewhere so it has been delayed.

What's coming?

- Refactoring / rewriting some of the tutorial parts to make them more clear
- Collision detection
- Free movement as an alternative to turn based

- A case study of quickly creating a game with GameMaker and posting it to Google Play (much later)

Wednesday, August 20, 2014

Turn based RPG with Unity 2D - Part 4: Enemy/NPC AI (moving)

Part 1: Grid movement
Part 2: Camera control
Part 3: Lighting
Part 4: NPC AI

---


Unity version: 4.5
Language used: C#

Making the NPC


For the purpose of this tutorial, let's copy the player sprite and just change the tint to something else. Like red. This will be our NPC.

Drag the player sprite to your hierarchy and name the new GameObject as First NPC. Or anything you like. Now, select the object and change its tint from the inspector:


Now we need to give the NPC a tag. In the top of the inspector click the box that says Tag and add a new tag called "npc":



Don't forget to select your npc GameObject again and assign that tag to it.

Scripting the NPC


Next create a new interface called NPCinterface (just create a new C# script and remove everything in it, then write the interface):


1
2
3
public interface NPCinterface {
   void ExecuteAI();
}

This is where you can define methods for the NPC that can be used from other scripts. Next create another script and call it NPCController. This will be our abstract class that all NPC inherit from. We can define common methods in this class:


1
2
3
4
5
6
7
8
9
using UnityEngine;
using System.Collections;

abstract public class NPCController : MonoBehaviour, NPCinterface {

 public void ExecuteAI() {
 
 }
}


Notice the keyword abstract and also the NPCinterface reference. This means we implement NPCinterface and must define the contents of ExecuteAI( ) method here.

Select the NPC from your hierarchy window and from the inspector, select Add component / new script. Name this script FirstNPC. Hit Create and Add. Open the script and change it like so:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
using UnityEngine;
using System.Collections;

public class FirstNPC : NPCController {

 void Start () {
  pos = transform.position;
 }
 
 void Update () {
  if (moving) {
   transform.position = pos;
   moving = false;
  } 
 }
}


You will get some errors because pos and moving are not defined but we will do that next. Notice that we added NPCController here instead of MonoBehaviour (because MonoBehaviour is implemented in NPCController already).

Go back to your NPCController script and add these properties:


protected Vector2 pos;
protected bool  moving = false;


Protected means these properties will be accessible to the child objects (in this case our FirstNPC script). Next add this piece of code to the ExecuteAI -method:


int direction = Random.Range (1, 5);

if (direction == 1) {
 pos += Vector2.up;
 moving = true;
}
else if (direction == 2) {
 pos += Vector2.right;
 moving = true;
}
else if (direction == 3) {
 pos -= Vector2.up;
 moving = true;
}
else if (direction == 4) {
 pos -= Vector2.right;
 moving = true;
}
else {
 ; // do nothing
}


In this script, first we get a random direction. We have 4 directions so the range starts from 1 and goes up to 5 (the range starts from 0 so the fifth element is actually 4) . We then set the position depending on this random direction. The Update -method in our FirstNPC class will handle the actual moving.

So... how do we call the ExecuteAI? Our player object is the center of attention in this type of game, the turns are pretty much dependent on what the player does. So let's open up our PlayerController -script and do some changes.

OK, our game only has 1 NPC but let's look to the future and determine that there will be more. Add these properties to the PlayerController -script:


private GameObject[] npcObjects;
private NPCController[] npcControllers;


In the Start() -method, add these lines:


// Find all NPCs with the tag "npc":
npcObjects = GameObject.FindGameObjectsWithTag ("npc");

npcControllers = new NPCController[npcObjects.Length];

// Iterate through them and store their NPCController to
// npcControllers array:
for(int i = 0; i < npcObjects.Length; i++) {
 npcControllers[i] = (NPCController) npcObjects[i]
  .GetComponent(typeof(NPCController));
}


The code comments describe what this code snippet does. Next let's do a convenience method called ExecuteNPCAI where we iterate through the npcController array and execute the AI for each NPC:


private void ExecuteNPCAI() {
 foreach(NPCController npc in npcControllers) {
  npc.ExecuteAI();
 }
}


Now call this method from the CheckInput -method in each keystroke check like so:


if (Input.GetKeyDown(KeyCode.D) || Input.GetKeyDown(KeyCode.RightArrow)) {
 pos += Vector2.right;
 moving = true;
 ExecuteNPCAI();
}


Now test your game, the NPC should move randomly whenever you move the player character. Of course there's no collision detection yet so the character might go under the player or off the grid.

GIF of the movement:



Stay tuned for more tutorials.

All of the scripts as a whole:

NPCinterface:


1
2
3
public interface NPCinterface {
 void ExecuteAI();
}


NPCController:


 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
using UnityEngine;
using System.Collections;

abstract public class NPCController : MonoBehaviour, NPCinterface {

 protected Vector2 pos;
 protected bool  moving = false;

 public void ExecuteAI() {
  int direction = Random.Range (1, 5);

  if (direction == 1) {
   pos += Vector2.up;
   moving = true;
  }
  else if (direction == 2) {
   pos += Vector2.right;
   moving = true;
  }
  else if (direction == 3) {
   pos -= Vector2.up;
   moving = true;
  }
  else if (direction == 4) {
   pos -= Vector2.right;
   moving = true;
  }
  else {
   ; // do nothing
  }
 }
}


FirstNPC:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
using UnityEngine;
using System.Collections;

public class FirstNPC : NPCController {

 void Start () {
  pos = transform.position;
 }
 
 void Update () {
  if (moving) {
   transform.position = pos;
   moving = false;
  } 
 }
}


PlayerController:


 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
71
72
73
74
75
76
77
78
using UnityEngine;
using System.Collections;

public class PlayerController : MonoBehaviour {
 
 private Vector2 pos;
 private GameObject[] npcObjects;
 private NPCController[] npcControllers;
 private bool moving = false;
 
 void Start () {
  // First store our current position when the
  // script is initialized.
  pos = transform.position;
  
  // Find all NPCs with the tag "npc":
  npcObjects = GameObject.FindGameObjectsWithTag ("npc");
  
  npcControllers = new NPCController[npcObjects.Length];
  
  // Iterate through them and store their NPCController to
  // npcControllers array:
  for(int i = 0; i < npcObjects.Length; i++) {
   npcControllers[i] = (NPCController) npcObjects[i]
    .GetComponent(typeof(NPCController));
  }
 }
 
 void Update () {
  
  CheckInput();
  
  if(moving) {
   transform.position = pos;
   moving = false;
  }
  
 }
 
 private void CheckInput() {
  // WASD control
  // We add the direction to our position,
  // this moves the character 1 unit (32 pixels)
  if (Input.GetKeyDown(KeyCode.D) || Input.GetKeyDown(KeyCode.RightArrow)) {
   pos += Vector2.right;
   moving = true;
   ExecuteNPCAI();
  }
  
  // For left, we have to substract the direction
  else if (Input.GetKeyDown(KeyCode.A) || Input.GetKeyDown(KeyCode.LeftArrow)) {
   pos -= Vector2.right;
   moving = true;
   ExecuteNPCAI();
  }
  else if (Input.GetKeyDown(KeyCode.W) || Input.GetKeyDown(KeyCode.UpArrow)) {
   pos += Vector2.up;
   moving = true;
   ExecuteNPCAI();
  }
  
  // Same as for the left, substraction for down
  else if (Input.GetKeyDown(KeyCode.S) || Input.GetKeyDown(KeyCode.DownArrow)) {
   pos -= Vector2.up;
   moving = true;
   ExecuteNPCAI();
  }
  
  // Then change the transform to the new position with
  // the given speed:
 }
 
 private void ExecuteNPCAI() {
  foreach(NPCController npc in npcControllers) {
   npc.ExecuteAI();
  }
 }
}


---
Part 1: Grid movement
Part 2: Camera control
Part 3: Lighting
Part 4: NPC AI

Code is highlighted with hilite.me

Monday, August 18, 2014

Changing the default Mouse Cursor in Unity (hardware acceleration)

Unity version: 4.5
Language used: C#

I checked some outdated tutorials how to change the default system mouse cursor in Unity but it lagged considerably. So I searched the Unity manual (now why didn't I check there first?). However, I couldn't get this working right away and wondered what was wrong, until I took another look at the inspector. When you're importing your cursor, select the Cursor from the Texture type in the inspector:



Then in your main GameObject (or your camera script) add these lines:


   .
   .
   .

   public Texture2D cursorTexture;
   private Vector2 hotSpot = Vector2.zero;

   void Start() {
      Cursor.SetCursor(cursorTexture, hotSpot, CursorMode.Auto);
   }

   .
   .
   .


CursorMode.Auto means the hardware mouse acceleration will be used on platforms that support it. The hotspot is like the offset location of the cursor. In this case the tip (at 0,0) is the effective part of the cursor.

Next, drag the cursor texture from the Assets to the Cursor Texture field in the object's script:



On Mac, I couldn't get the cursor showing in the editor while testing. Building and running worked fine though.

That's it, thanks for reading. :)

--------------------------------------
Code highlighted with hilite.me

Tuesday, August 12, 2014

Turn based RPG with Unity 2D - Part 3: Lighting The Map and Pixel Perfect Camera


First, let's do some changes to our script and make the sprites look crisp whatever the resolution (provided that the aspect ratio stays the same in fullscreen mode).

Open the CamController script and add this line to the Start method:

Camera.main.orthographicSize = Screen.height / 2 * 1 / 32;

Basically we just apply the math from the last post to the camera's size variable.


You'll notice the difference in higher resolutions in fullscreen mode.

Also, if you didn't do it before, select the player sprite from your assets and in the inspector, change the Filter Mode to Point and hit Apply. This will make the sprite look like it's meant to look:




Lighting the map


To add some dynamic lighting to our map, create a new material in the assets. Like so:



In the inspector, change the shader to Sprites/Diffuse:


Now select the map from the hierarchy (the object with grass grid) and in the inspector, change its material to this new material:


You can add the material to anything you want to be affected by light but let's leave the player sprite alone for now because he's always illuminated. The player sprite might disappear after you add the material, so bring him up by adjusting the Order in Layer (or adding another sorting layer for the player altogether):



Next, add a new point light from the GameObject / Create Other menu:



You must adjust the light so it shows on the scene. In the hierarchy window, drop the light object to the player object (this will make it the player object's child and it will follow the player). Also use the arrows on the light object to move it to the center of the player object:



Change the light object's Z-value to something negative to make it show on the map. The higher the number (to negative), the farther the light goes from the player. Set it to -1.5 for now. You can experiment with it for better results.

You can also move the light in the Z-axis in 3D mode. Just click the little 2D button on top of your scene window to toggle the mode.


To make it really dark, like night time, change the ambient lighting from Edit / Render Settings:



The result:



Thanks for reading! More tutorials coming up.

---

Part 1: Grid movement
Part 2: Camera control
Part 3: Lighting
Part 4: NPC AI

Code highlighted with: hilite.me