Mastering Unity 2D Game Development(Second Edition)
上QQ阅读APP看书,第一时间看更新

Classes

Architecting the core of your game from the beginning is an often-skipped process. Many developers are too eager to build their game and just start placing assets in a scene as they go. This kind of practice is fine for prototypes (mostly, however, even with prototypes, a level of architecture is usually required). When building your actual project, however, without setting up a proper architecture from the beginning, you are heading toward a world of utter mess.

When we say architecture, it doesn't mean that you need to design everything (but it helps). You just need to ensure that you plan what you are going to build before you build it instead of thinking about stuff and checking Google for information on how to do it. Even if you are using some kind of an agile method, you should have a good framework and goal for each step. This will guide you on what should be done and when, not just designing the project on the fly.

MonoDevelop

When you create a new script in Unity, you will create a new class. To create a new script, select Create | C# Script.

When you double-click on the script, MonoDevelop will open. MonoDevelop is the Integrated Development Environment (IDE) that comes with Unity:

MonoDevelop an IDE

This means that all code will be open, edited, and ran from within MonoDevelop. You can use other editors, such as Microsoft Visual Studio, but we will use MonoDevelop exclusively in this text.

Note

If double-clicking on a script causes an editor other than MonoDevelop to open, you can change the default editor back to MonoDevelop by selecting Edit | Preferences | External Tools and making sure MonoDevelop is set as the External Script Editor:

When a new script is created, its name must also match the class, as shown in the following screenshot:

The object-orientated design

Unity is a fully object-orientated (OO) system with strict interfaces to ensure that the engine knows what to expect and when, so why shouldn't your game follow the same pattern? Unity is also component-based, which is something you should take into account while designing how your game will be put together.

At the core of any OO design, the focus is on reusability. If a set of attributes is repeatedly used across multiple objects, then they should be separated into one common class and shared. In addition to this, you should also reduce the amount of code that is doing the same job. This means that we can more easily make changes to this base set without having to re-edit all the classes that might need those attributes. The following diagram shows two approaches of using a base class to define common attributes over multiple code implementations:

Another facet of OO design is to employ interfaces to govern exactly how a class should look if you have multiple objects of the same type. For example, if you have an Enemy class structure that defines how enemies in general should work, then, using that same structure, you specify all the enemy implementations. Interfaces can also define behaviors or methods on a class, so you can ensure that all the classes that implement that interface will always have the same common abilities, such as all the enemies will have patrol, Fight, and run away methods. This means that if you have an enemy object, it will always have those methods attached to them when you refer to them in the code.

The following diagram shows how you can plan for multiple inheritances, allowing you to add a common behavior pattern to each group of entities:

Knowing this helps us design our game effectively and ensures that we architect it correctly from the beginning.

We'll discuss these patterns in more detail when we implement them in the following sections.

The game structure

To keep in line with the preceding architecture set, we'll design the layout of the class to support a flexible structure that will be easily extended in the future.

The common game object

As almost every entity in our game will have statistics and some basic behaviors, we start with a generic object (Entity) to define the attributes that all the entities in our game will have. As there is only one entity type, we don't need to set up an interface for this object as all the other game objects will just use this one definition, as shown in the following diagram:

This shows that we have several common attributes for things such as health and strength.

Create a new class within your Scripts folder by selecting Create | C# Script. Name the script Entity.

Type the following code into your new Entity class:

using UnityEngine; 
public class Entity : ScriptableObject { 
  public string Name; 
  public int Age; 
  string Faction; 
  public string Occupation; 
  public int Level = 1; 
  public int Health = 2; 
  public int Strength = 1; 
  public int Magic = 0; 
  public int Defense = 0; 
  public int Speed = 1; 
  public int Damage = 1; 
  public int Armor = 0; 
  public int NoOfAttacks = 1; 
  public string Weapon; 
  public Vector2 Position; 
} 

Tip

An interesting feature of Unity involves its handling of public variables. Whenever a variable is set to public, its values can be easily accessed and adjusted from the GameObject's inspector, rather than actually adjusting it within the script.

The player object

Basing the player's character on the Entity object makes the definition of the player a lot simpler. So, you only need to focus on what is specific to the player's character itself, that is, the differences between the player and all the other game entities:

So, the player character we see here is the only one who has Inventory, Money, and Skills since they are specific to our hero's work in our game. Create another class within your Scripts folder. Name the script Player. In the following code, the player inherits all the properties from the Entity class:

using UnityEngine; 
public class Player : Entity { 
  public string[] inventory; 
  public string[] skills; 
  public int money; 
} 

Note

Preferably, all the attributes of any class should be of the read-only type outside of the class itself (unless there is a very good reason for it). This is to ensure that you don't mistakenly change a class's value without knowing why. It might sound easier to just keep updating everything, but at some point, while you are debugging, you will want to know why things are changing. If any code updates these values, then you will literally spend hours trying to find why. If you need to change values, then you need to implement behaviors (see the following sections).

To show you how to build the architecture progressively in this project, we will add more classes to each section; we'll keep things simple and build the project with a strong foundation.

We already have our base entity in place from which all the game entities as well as our player are driven, so let's look at implementing them further.