To reiterate a bit on what we just learned about objects and classes, you can think of it this way: a class is the blueprint. It is not an object. It is just the formless description of one. While the instance is the actual object and actual objects in the actual real world have to be created somewhere. They have to live somewhere, right? So where do objects in code live? Well just as human objects live on plots of land on Earth, objects in coding live on memory sectors, little plots of land on memory chips, right? So that is what happens when you use a new keyword when you are creating a new object in memory, right? In your code, you use the name for your object that you created, your variable name, in other words, or your identifier, to keep reference to this object throughout your code that is in memory. And when you make this object reference a field, a class level variable, remember, you get a very interesting containment structure that I will show you in a moment.
When we are creating our instance of our class, such as this field inside of another class, we are able to utilize this class just like any other type int, bool, etc. because it is a type, a user-defined type. We can achieve this complex functionality in any single class by incorporating or containing references to outside classes in this way, and of course those classes that are used as fields within another class themselves can contain reference to yet more outside classes, leading to the sort of multi-container Russian doll aspect of more complex containment. One of the benefits of this feature of Object-oriented programming, boxing up entire classes into a single field with another class, is you can abstract away information that isn't immediately necessary to know in depth, which I will show you in action in a moment. In the previous video, we talked about this class/object relationship using a concept we are all familiar with: a human being or a Person, right? But the concept is no less applicable to virtual objects that you might have within your game. So let's expand on our knowledge of classes versus objects with a more game-like example.
Let's write a new class here. Let's actually create a new script. Because it is not a MonoBehaviour, it is not directly embedding into Unity. The class name and the script name can be different and this will be kind of interesting. Please don't get confused. I will show you in a moment. Let's call this RPGClasses and open that script up. Sorry, get rid of that error message. I don't know why it is there. And get rid of MonoBehaviour because we don't need it. Again, we are not going to directly attach it to a game object as well as get rid of Start() and Update(). As I said, we don't need to have the class the same name as our script name, RPGClasses in this instance, because it is not a MonoBehaviour. So let's just make that reference right here of class Wizard, alright? We don't want to write anything in this class just yet. We just know we are going to want a Wizard, and down here we are going to write another class. Say, public class Weapon. I didn't want to confuse you but I wanted you to see it all in one file so it is easy for you to visualize right in front of your face, all these classes and how they are related to each other. The reason, again, I am able to do this, but you are not able to do with MonoBehaviour scripts attached to GameObjects, is because this is not directly embedded in the Unity engine right now. We are not deriving from MonoBehaviour, so that is why we are allowed to write all of these classes in one file, otherwise don't do this. This is not how you are going to be doing things.
We are going to want a Weapon along with our Wizard class. That is it. Just these two classes right now. Let's first define our Weapon. Okay, so public. And as we mentioned earlier, public means it is accessible to an outside class to view. Even though this is in the same script file, this is still an outside class. It is its own class, so make the field here public. It is going to be a string, and just give it a name. We will call it WeaponType. And we are not going to assign it anything, because we are going to want to assign it later when we create the object of type Weapon. We are also going to want to create a method, so make that public again for the same reason. And usually, we type in void but I am not doing that here. I will quickly write out this method and explain in a moment why we didn't write in void, but we wrote string instead. Let's call this method definition UseWeapon() and just have one line of code in this method. Write in this new keyword called ‘return’, and we are going to use the quotes to designate the string and glue together with that string using the concatenation plus operator as we have used before. The WeaponType, and in this literal string write: “The player has struck with the weapon:” + WeaponType. That is what is going to append on the end of that. And for a Wizard, what we will do is we will just give it a single field of type Weapon. So, public type Weapon with the name weapon = new Weapon(). So we are creating the object directly within our Wizard class but our wizard itself has not be instantiated yet. We create a weapon instance in our Wizard class. That instance becomes a instance when we create and instance of wizard.
I hope that didn't confuse you. We will see this in action in a moment. As before, what we are going to want to do next, after I save that, is create a MonoBehaviour script so we can output to Unity. Create a new script. We will call it RPGController. In this case, we can't change the name of the script as that will make it different from the class name within our script, because it is a model behavior and it is being attached to our game object. Actually, before I forget as usual, I should do that. I am going to our Test game object. We are going to attach this RPGController output, it is going to be responsible for outputting to Unity. Here in RpgController, let's type in this code. We are going to use void Start(). We are going to use Start() as usual so we can at least output it, see it in Unity. And in Start(), we are going to create our instance of our object Wizard. Type this in. Then using the dot accessor, we will access the public Weapon, and because the Weapon has a field of type WeaponType, the string field... Sorry, not type WeaponType, it has a field of type string called WeaponType. We will access that field name by just doing the same as we did before with the dot accessor. Because it is a string, it is going to need some sort of assignment of a string to it, so we are going to give it a string name = "wand”. Our wizard now has a wand, or at least a string called wand.
As usual, output that to our Debug.Log() And we are going to want to reference our UseWeapon() method found in the Weapon class, so type this in. There is UseWeapon(), nice and handy available to us through IntelliSense because it is public. Let's output that to Unity and see the message that we get. There. “The player has struck with the weapon wand.” This is a bit more of a complex use of everything we have come to know thus far. First we created a class definition for a game character called Wizard. Right now it only has a single field and that is for a weapon. In this example, we decided to make a custom type for a Weapon called weapon, and that class in turn, contains a string called WeaponType, as well as a slightly different looking method to ones we are used to seeing. It has called UseWeapon() here. Finally, we make use of these classes by creating an instance of our Wizard in the Player Controller class that we called RPGController. When that instance is created, it creates an instance of the Weapon field as we showed earlier. We get a new weapon when we create the instance of a Wizard. By using the dot accessor and with the help of our trusty IntelliSense, we are able to peer into these multiple levels of containment. The dot accessor can be seen as zooming into each set of squiggly brackets. We are opening up those containers within containers, so we first peer into what our wizard object holds. It holds a single field of type weapon. It is a weapon of type Weapon. Then, we peer into the weapon it is holding and we see that the weapon has a WeaponType,which is a string and we also have a method in our weapon called UseWeapon().
I threw in there something new, and that is I had our UseWeapon() method return a string. But in this case, we want to return the string back to the calling code block. Basically, you can look at it like when UseWeapon() is called in our RPGController Start() method. It executes and then returns that string. It is like opening that box of instructions, executing them, and poof.After the box executes, it disappears and in its place is a string of characters, the phrase we use in the UseWeapon() method. This string is being returned to our executing or calling code block. It is returning this string. That is why it is no longer void. This is our return type. Void means there is no return. Here we call that method to return that string. As I said, we determine that return value type right before the method name where your method definition is created. So where we normally put void, we simply replaced it with string. That is necessary. If I type in void like we are used to, there is a mismatch. Down here in our error pane, it says weapon.UseWeapon() returns void. A return keyword must not be followed by an object expression. Kind of a vague error code. It is basically saying you can't have something return, after that method executes, return to that code block that calls this method. You can't have a return type and then say that there is no return type, which is what void means. The return type has to be the same type of which you specify here. This is a string and we concatenate at the end of it another string. That is of type string, so it has to be string. Or a compatible type, in this case, string.
Lesson 20 - Object Containment and Method Returns