Pathfinding can be a relatively advanced task, mostly because the logic takes a moment to grasp. We will be using a form of pathfinding to highlight all of the tiles that a unit can reach. When one of those tiles is selected the unit will follow the best path to the target. To make it more interesting, I will add three different movement types: a walking unit which must go around enemy units and tiles with too large a jump delta, a flying unit, and a teleporting unit.
Tactics RPG State Machine
This week we are going to create a State Machine which, over time, will handle all of the states which ultimately control our game’s logic. Initially I will create a state which is responsible for initialization (creating the game board, etc.) and then we will add another state which allows us to move the tile selection indicator around the board using events from our Input Controller. We will also add a simple Camera Rig to make sure that the game camera is always looking at something relevant.
Tactics RPG User Input Controller
In this lesson we will be writing a component to manage user input. We will work with Unity’s Input Manager so that your game should work across a variety of input devices (keyboard, controller, etc). The component we write will be reusable so that any script requiring input can receive and act on these events.
Tactics RPG Board Generator
In this lesson we will focus on creating one of our pre-production tools. We will create a scene from which we can generate boards to fight on. Along the way we will create an editor script for enhancing the inspector which will allow us to both randomly generate and hand modify the end result. Finally we will use scriptable objects to persist our data.
Tactics RPG Project Setup
In this project we will create and configure a new project to hold our Tactics RPG game engine. We will add folders for organization, import some existing assets, and create a few more assets which we will begin implementing in future lessons.
Tactics RPG Series Intro
I’ve decided to start a new section of my blog where I create working game samples and then blog about them. I have been working on a Tactics RPG game for awhile now and you can see the results so far in this sample video:
or download the and try out the project yourself.
Using Git with Unity
In the last post, I introduced the concept of Source Control and introduced several options available to you. In this post I want to focus on just one option and show some sample workflow of it’s use with Unity. We will create a new Unity project and configure it for versioning, initialize a local repository, make some commits, branches, and merges, as well as discuss some patterns of source control you should consider using. Continue reading
Intro to Source Control
Today I would like to talk about something which isn’t actually programming, but which every programmer should know – source control (also known as version or revision control). You will see this topic listed on most job listings as something you should be familiar with, but many hobbyists go far too long (sometimes waiting until getting hired into a big company) before realizing its many benefits. Source control can be extremely valuable even if you work solo on a project! Continue reading
Create an MMORPG in 30 Minutes!
It’s one of the goals of most aspiring Game creators – “How do I make an MMORPG?” Most of the time you hear answers like, “Try something easy first”. But not today. For the first time ever, I will reveal the secrets that let you create awesome MMORPG’s in Unity with almost no effort. Continue reading
Structs
Structs are like Classes, but completely different. Sorry 🙂 If you don’t know what a struct is or when you would use it, or if you don’t know the difference between passing by reference and passing by value, this lesson is for you.
Structs in Unity
Since this series is based on learning C# for Unity, let’s start by pointing out some places where you may have already been using Structs:
- Vector2, Vector3 and Vector4
- Rect
- Color and Color32
- Bounds
- Touch
In particular, the various forms of Vector(2-4) are used all over. You will see them used to store everything from the position, rotation, and scale of a transform to the velocity of a Rigidbody, or the location of a touch or mouse click on the screen.
What is a Struct?
A struct is sort of like a compound data type. It looks very similar to a class, because you can define fields and methods in very much the same way. The following example defines a struct and class which are nearly identical:
public struct PointA
{
public int x;
public int y;
}
public class PointB
{
public int x;
public int y;
}
In this example, the only noticeable difference is in the keywords – “struct” instead of “class”. Some other differences between the two include:
- A struct cannot inherit from a base type like a class can
- Structs cannot have parameterless constructors
- All of a structs fields must be assigned before leaving a constructor
- structs are passed by value, whereas an instance of a class is passed by reference
The last point, to me, is the most important. There are many significant differences between “value” types and “reference” types that impact when, and how, you should use them.
Reference Types
When we say that an instance of a class is passed by reference, what is actually happening is that we get a “pointer” to the address in memory of the object and then pass “that” value around. This is important because an instance of a class can actually be very large, containing many fields and even other objects. In that sort of scenario, to copy and pass along the entire thing could negatively affect performance, and is why you only pass the address instead.
Reference types are allocated on the “heap” and are cleaned up by something called “garbage collection”. Garbage collection is a process that happens automatically but is slow and usually accounts for hitches in your game’s frame-rate. For this reason, you don’t want to frequently create objects and allow them to go out of scope. The following example is a big no-no:
// YOU SHOULD NOT DO THIS
void Update ()
{
// Create an instanace of a class with local scope in the Update loop (called every frame)
List<GameObject> objects = new List<GameObject>();
// Imagine stuff is done with this list of objects (it is populated and iterated over etc)
for (int i = 0; i < objects.Count; ++i)
{
}
// When the method ends the objects list goes out of scope and will at some point need
// to be garbage collected
}
Value Types
When we say that something is passed by value, what is actually happening is that the variable is fully cloned / copied, and the copy is passed along while the original is left intact. Structs are value types and are passed by value. This means that structs are ideally small data structures.
Value types are allocated on the “stack” which means that their memory is easy to reclaim and they do not have an affect on “garbage collection”. Unlike the Update loop example with reference types, it is totally acceptable to create value types and allow them to go out of scope without fear of an impending slow-down or memory problem. For example, the following is totally acceptable:
// This is OK
void Update ()
{
// Create a local variable to a value type - struct
Vector3 offset = new Vector3 (UnityEngine.Random.Range (-1, 1), 0, 0);
// Do stuff with it
Vector3 pos = transform.localPosition;
pos += offset * Time.deltaTime;
transform.localPosition = pos;
// Your structs memory will be easily reclaimed as it goes out of scope here
}
Gotchas
It is tempting to try to use a struct like an instance of a class, but because it is passed by value, there are several gotchas that are often encountered. Consider the following example:
using UnityEngine;
using System.Collections;
public class Demo : MonoBehaviour
{
public Vector3 v1;
public Vector3 v2 { get; private set; }
void Start ()
{
v1.Set(1,2,3);
v1.x = 4;
v2.Set(1,2,3); // ** (Note 2)
v2.x = 4; // * (Note 1)
Debug.Log(v1.ToString());
Debug.Log(v2.ToString());
}
}
* (Note 1) This sample won’t compile due to this line. You will get the error, “error CS1612: Cannot modify a value type return value of `Demo.v2′. Consider storing the value in a temporary variable”. The compiler is trying to protect you from a logical error (which I will explain in a minute), and is suggesting that you first get a new struct, modify that new struct, and assign it back.
** (Note 2) This line is far more dangerous because it will compile and run but does not appear to have worked.
If this code were to compile and run, you would see the following output:
(4.0, 2.0, 3.0) (0.0, 0.0, 0.0)
This is NOT what you might have expected. So what’s going on? C# automatically creates a hidden backer property for ‘v2’. When you use the getter (by simply referencing ‘v2’) C# provides a copy of the backer, not the actual backer – remember that this is because structs are passed by value not by reference. In the line marked by Note 2, what is happening is that you get a copy of the backer, modify the copy in place, and then the information is immediately lost because it is not assigned back to anything.
The following example is similar – it illustrates how the concept of a reference type vs a value type is often overlooked and causes problems. Here we hold a reference to a list, which holds references to Vector3’s:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Demo : MonoBehaviour
{
void Start ()
{
List<Vector3> coords = new List<Vector3>();
coords.Add( new Vector3(0, 0, 0) );
coords[0].Set(1, 2, 3);
coords[0].x = 4; // error CS1612 (see above example and comment out this line to compile)
Debug.Log(coords[0].ToString()); // Will be (0.0, 0.0, 0.0) - Not what you expected!
}
}
In contrast, the following example will work as you expect (or at least as you would have expected before I started scaring / confusing you with the previous examples)
using UnityEngine;
using System.Collections;
public class Foo
{
public Vector3 pos;
}
public class Demo : MonoBehaviour
{
void Start ()
{
Foo myFoo = new Foo();
myFoo.pos.Set(1, 2, 3);
myFoo.pos.x = 4; // no compilation error
Debug.Log(myFoo.pos.ToString()); // Will be (4.0, 2.0, 3.0) - As you expect!
}
}
Why does this example work when the others don’t? The answer is because we have a reference to the ‘myFoo’ object – not a reference to the object’s field. The object holds the struct’s values directly (as a Field) and modifies it directly without a problem.
Had the Foo implemented its Vector3 as a property instead of as a field (even with a specified backing field), it would have been a problem – see the following example:
using UnityEngine;
using System.Collections;
public class Foo
{
public Vector3 pos { get { return _pos; } set { _pos = value; } }
private Vector3 _pos;
}
public class Demo : MonoBehaviour
{
void Start ()
{
Foo myFoo = new Foo();
myFoo.pos.Set(1, 2, 3);
myFoo.pos.x = 4; // error CS1612 (see above example and comment out this line to compile)
Debug.Log(myFoo.pos.ToString()); // Will be (0.0, 0.0, 0.0) - Not what you expected!
}
}
Many of these problems are alleviated if you can get into the mindset of treating your structs as “immutable” (this means you never change the values of any fields), or actually make them immutable (if it is your struct).
Summary
In this lesson, we introduced the struct and compared when, where and why you would use one over a class. We showed some of the limitations and gotchas of structs, but also their benefits. Used correctly, structs are a very valuable and efficient tool to add to your programming arsenal.