Creating an RTS in Unity: Part IV

Update: This can now also be found at stormtek.geek.nz/rts_tutorial/part4.php, where the entire tutorial is now being hosted.

The time has now come for us to lay the foundations for interacting with objects in our world. There are 3 main types of objects with which we will want to interact with in various ways. These can broadly be classified as Unit, Buildings, and Resources. Units will be running round collecting things and attacking things. Buildings will form the majority of our bases. And Resources exist to be collected so that we can build up our base and army, enabling us to take over the world.

However, all 3 of these types of objects will share a number of things in common.

  • They all exist somewhere inside our world
  • They can all be selected
  • They all have a name
  • They can all take damage

With this in mind, let us make use of inheritance to cut down on the amount of code we need to think about and maintain.

To get us started, let’s create a new folder in our Assets directory called WorldObject. There should now be 3 folders sitting in this directory – Player, RTS, and the newly created WorldObject. Inside the WorldObject folder create another 3 folders called Building, Resource, and Unit. We will not be doing anything in these today, but we will make use of them much later in the project. Finally, add a new C# script into the WorldObject folder and call it WorldObject. This script is where we shall define our basic world object and it’s core behaviour.

World Object

First up let’s create the following public variables at the top of our class. Remember, by declaring them public we can assign values directly within Unity, which is useful for testing and for easily changing details if we need to.

public string objectName;
public Texture2D buildImage;
public int cost, sellValue, hitPoints, maxHitPoints;

We will not any of these variables right now, but it is useful to set them up anyway. Also, some of these details will not make as much sense for a Resource, but those details can simply be ignored at run time. And it may be that we actually want to implement a resource that the user must construct first anyway (which would actually blur the lines between building and resource even further).

Next we shall add a couple of protected methods. Since these are protected, rather than private, they can be accessed by any subclasses of WorldObject. However, since they are not public they are not able to be reached by any random external classes.

protected Player player;
protected string[] actions = {};
protected bool currentlySelected = false;

As with our HUD last time, we want to be able to access the player that might own a given WorldObject. We also make use of a boolean variable to determine whether someone has currently selected this object. And finally, we will allow an object to define a list of actions that it can perform. Exactly what the results of those actions are will be left up to a subclass to determine. This could be, for example, building other objects, repairing objects, researching upgrades – it all depends on what the object is and what it is able to do in the game.

In order to make sure that a subclass can have it’s own implementation of Unity methods, while still being able to run the default implementations of those methods, we need to declare them protected virtual. This is one of those ‘This is how C# / Unity require things to be’ situations. Make sure the following methods are declared after your variables, replacing any code that Unity provided for you.

protected virtual void Awake() {

}

protected virtual void Start () {
    player = transform.root.GetComponentInChildren< Player >();
}

protected virtual void Update () {

}

protected virtual void OnGUI() {
}

I can’t remember the exact difference between Awake and Start, but I do know that Start happens later after a bunch of other things have been initialized. Notice that once again we are getting the reference to the player very early on.

There are two other things that we wish to define now for our WorldObject. The first is the ability to tell it whether it has been selected or not. We will do this by adding the method SetSelection as outlined below.

public void SetSelection(bool selected) {
    currentlySelected = selected;
}

For now all this does is change the selection state to whatever was specified. The other thing to add is a pair of methods to look after actions. We will provide an accessor for all the methods that we have defined as well as a virtual method for performing an action. By declaring a method virtual we are telling C# that any subclass must provide an implementation for that method. The methods should look as below.

public string[] GetActions() {
    return actions;
}

public virtual void PerformAction(string actionToPerform) {
    //it is up to children with specific actions to determine what to do with each of those actions
}

And that wraps up the framework for our WorldObject. We have nothing to add this to just yet, but we will get onto that in the next couple of parts as we begin to define buildings and units. A shorter entry this time, but it provides us a good platform to build upon. Once again, the complete code for this is up on github under Part 4.

Advertisements

5 thoughts on “Creating an RTS in Unity: Part IV

  1. Hello Elgar,

    In the start method of worldobject.cs , how can it find player , given that it’s transform.root doesn’t contain player.cs. This can be achieved by either dropping player reference directly in the editor or using GameObject.find.

    Am I missing something obvious here. I tried these out but unlike you I’m using UnityScript for coding.

    Regards

    • In the start method of WorldObject it is attempting to find player. If the WorldObject belongs to a player (so the actual object is a child of a Player object with player.cs attached) then it will find player. If it is a standalone object it will not find player. What this means is that when we add checks later on we can use the check

      if(player)
      

      to check whether the player object was found (since it will not be null if we found player in Start()), indicating the object actually belongs to a player. Having this reference also allows us to perform checks with other objects to see if they belong to the same player or not.

  2. ShockFactor says:

    FYI – (quoting from the docs)
    The difference between Awake and Start is that Start is only called if the script instance is enabled. This allows you to delay any initialization code, until it is really needed. Awake is always called before any Start functions. This allows you to order initialization of scripts.

    • Yea, I recall reading something like that. I couldn’t remember exactly where, and it has not been an issue so far. But it is good to keep it in mind. Thanks for pointing this out. 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s