Methods

The “action” part of programming comes from “calling” (also known as “invoking”) something called a “method” (also known as a “function”). In this lesson we will learn how to define and call our own methods, and then review a few more which are special to Unity. Finally, we will create our own “Magic 8 Ball” demo and cover how to link a method to the click of a button, and drive the output of a label.

Method Declarations

A method requires at least three things, but usually has four:

  • a return data type
  • an identifier name
  • a parameter set
  • a body (optional if you also mark the method as “abstract”, “extern”, or “partial”, but those are advanced features for later.)

Here are a few samples:

void DoStuff () {
	Debug.Log("Hello World");
}

This method is declared with a return type of “void” which is a special case and means it doesn’t return anything. The identifier name is “DoStuff” and the parameter set is empty (there are parenthesis with nothing inside). The method “body” is everything from the open bracket to the close bracket “{” and “}”. You can place “statements” inside a method body, which I have done here. When our method is called, we will see the message “Hello World” printed to the console.

public bool IsRegistered (string myID) {
	return string.Equals(myID, "Jon");
}

This example method begins with the key word “public”, which you may remember using with variables in the previous lesson. Like variables, methods default to “private” unless you specify otherwise. By making this method public, other classes can “invoke” it. Next we see a return type of “bool” (true or false). Note that unless your method’s return type is “void” you must have a statement inside the method which actually “returns” a value of the type you indicated, or the compiler will complain at you, “not all code paths return a value”. This will usually be the last line of the method, because any statements after the “return” will not be able to execute. The identifier is “IsRegistered” and we take a single parameter called “myID” which must be a type of “string”.

private int Multiply (int a, int b) {
	return a * b;
}

I marked the final example method “private” which is unnecessary since it is private by default, but sometimes you may want to re-iterate your intentions to other programmers. This method will return an “int” (a numeric value which uses whole numbers). The identifier is “Multiply” and it takes two parameters. Note that each parameter must specify its own data type and identifier name, and multiple parameters are separated by commas.

Method Invocations

To “call” or “invoke” your method, you use a statement which references the method Identifier followed by any necessary parameters, and then a semi-colon. Note that you don’t need to respecify the type or name of the parameter, and you can pass along any values or existing variables as long as the data type matches.

In a minute I will show code which calls each of our demo methods, but before that, let’s add a few variables to our class to make it more interesting.

public string test;
public int value1;
public int value2;

The statements we will write which call our methods need to be “executed” themselves, and the easiest way to do that is to plug in to a method which Unity will call for us. Add the following code in the Start method:

void Start ()
{
	DoStuff();

	if (IsRegistered(test)) {
		Debug.Log("Registered");
	} else {
		Debug.Log("Not Registered");
	}

	int value = Multiply(value1, value2);
	Debug.Log("Multiplication result: " + value);
}

The first statement we have made “calls” our first example method “DoStuff”. It is very simple because it neither accepts or returns any value.

Next we have something called an “if statement” which allows us to branch and selectively execute code.  Whatever appears within the open and close parenthesis is evaluated as either true or false and that evaluation determines which path to follow.  Because our method returns a true or false value, we can simply invoke it in place.  If the value which our “IsRegistered” method returns is “true”, then we will see the message “Registered” printed to the console. Otherwise, the “else” condition takes over and we will see the message “Not Registered” printed instead. Much like a method, the code statements which are selectively executed are wrapped by an open and close bracket.

Tip:
An “if statement” is a special kind of “selection” statement. There are some additional features I didn’t show in this example, such as the ability to use one or more “else if” branches, and the ability to make compound conditions (such as “condition1 OR condition2” and “condition1 AND condition2”). See the reference for more: https://msdn.microsoft.com/en-us/library/5011f09h.aspx

We must pass a string as a parameter when invoking “IsRegistered”, but don’t be confused by the name of the parameter in our method declaration and the name of what we pass in our statement – the names don’t have to match.  In fact the value doesn’t even have to be named.  I could have passed a literal value such as “Bob” (note that I would have included the quotes in code).  The name in the parameter declaration is used in the method implementation (the statements appearing inside the method), but otherwise doesn’t matter.  What DOES matter is that we pass “something” of the correct data type.  If you accidentally pass the wrong type of data, the compiler will generate an error and complain at you, “The best overloaded method match for ‘CLASS.METHOD(ARG)’ has some invalid arguments”. In this example, we used the string variable called “test” which we defined earlier in our class.

The “IsRegistered” method was marked public, which makes no difference in the example presented here. A class can call any of its methods regardless of whether the method is marked public, private, or protected. However, if another class had a reference to an instance of this class, we would be able to “call” any public methods it contains through dot notation with a statement such as with the following:

bool result = instance.IsRegistered("Joe");

Finally we call the “Multiply” method by passing two comma separated variables of the correct type, in this case our “value1” and “value2” variables.  The returned value of a method can be assigned to a variable and used again later – when we print a message telling us the result of the multiplication.

Create a new scene and attach your script to something in the scene (the camera or an empty game object is fine). Use the inspector to assign values to the variables and then run the scene. To see the output of the example, make sure to look in the console window (from the menu bar choose “Window -> Console”).

MonoBehaviour Methods

The two most common methods to get familiar with are the two included in the template (“Start” and “Update”). However there are many more template methods you can plug into. Following are a few of the ones I use most frequently.

“Awake” is called immediately following the instantiation of an object. It is the earliest method call you can plug in to, and may be a good location for “initialization” (this might include getting references to other components or creating instances from prefabs, etc) although at other times you might need to wait until “Start” to make sure everything is active and properly configured. Note that both Awake and Start will only be called once in an object’s lifetime.

“OnEnable” is called next. I often use OnEnable to register event listeners and OnDisable to remove them. One key difference between this method and Awake or Start is that it can be called multiple times – each time the object’s “enabled” state changes, so only certain types of initialization code make sense to go here.

“Start” will be called before the first frame “Update” method (after all objects have run their Awake and OnEnable methods) as long as the object it is on is enabled. Like the “Awake” method it can be a good place to initialize your objects.

“Update” is called once per frame, at a variable time interval, and provides a convenient handle to the “game loop”. “FixedUpdate” is similar but runs at a fixed time interval and is used to interact with Physics. “LateUpdate” is called after Update has finished and provides an option for you to have logic in response to other changes.

“OnDestroy” is called when you remove a GameObject from the scene. It is only called once in an object’s lifetime and can be a good place to clean up anything which was created during initialization. For example, if you created a texture or mesh, you would want to make sure they also get destroyed here.

Tip:
Refer to the MonoBehaviour class reference to familiarize yourself with all of the available methods:
http://docs.unity3d.com/ScriptReference/MonoBehaviour.html

Then check out this link on the “execution order” (what method will be called at what time) here:
http://docs.unity3d.com/Manual/ExecutionOrder.html

Magic 8 Ball

Create a new scene. Add a Panel (from the menu bar choose GameObject -> UI -> Panel). Next add a Button and Text object and parent them both to the panel. The button will be used to trigger a response from our Magic 8 Ball, and the Text is the label where the answer will be displayed. Create a new script called “Magic” and paste the following code:

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

public class Magic : MonoBehaviour 
{
	public string[] responses;
	[SerializeField] Text _label;

	public void AskQuestion ()
	{
		int index = UnityEngine.Random.Range(0, responses.Length);
		_label.text = responses[index];
	}
}

On line 7 of this script we declared a special type of variable which I haven’t shown you yet – an array. An array is a collection of values of the specified type. The declaration of our “responses” variable looks the same as declaring a normal string variable, except that we added an open and close square bracket after the data type. You can make any variable declaration an array by using this form. Because our variable is public, it will appear in the inspector, and we will populate its values there.

TIP:
There are many types of arrays including single-dimensional, multi-dimensional, and jagged arrays. There are also many ways to initialize them. It would be worthwhile to research the full range of options available to you: https://msdn.microsoft.com/en-us/library/aa288453(v=vs.71).aspx

We also created a variable to hold a reference to a Text component. We used the mark “[SerializeField]” instead of “public” because we need to assign the reference in the inspector, but no other script “needs to know” about this label or its use – it is something “internal” to the implementation of our script and could easily change at any time.

On line 10 we define a method which we will use as an “event listener” – a method which is automatically invoked based on triggered events. In this case our “event” is the press of an UI button. Note that our method needs to be public in order to connect it to the button through the inspector.

Our method has two statements. The first (line 12) creates a temporary variable called index, to which we assign a random number. We generate our random number using the “Range” method, which allows us to specify a min (inclusive) and max (exclusive) value from which to produce the result. If our array of responses held 3 strings, then we would generate either a 0, 1, or 2 from this method, because the value which we specified for “min” is 0 and the value we specified for “max” is dynamic based on the number of responses in the array.

The next statement (line 13) assigns the text variable of our label to be one of the responses from our array. The individual values within the array are accessed by “index” (a number value inside of square brackets). Note that arrays are zero-based, which means that the first element in the array is found at index 0. There are a couple of gotcha’s which could occur in this line or lines similar to it:

  • If the array had not been initialized (it is null by default), it would create an error – “NullReferenceException: Object reference not set to an instance of an object”. Our array is initialized because it is public and Unity serializes it as an empty array.
  • If we had not added any elements to the array, we would generate another error – “IndexOutOfRangeException: Array index is out of range.”. This would happen because the Random value we would generate would then be 0, and an index of 0 points to the first element in an array, but our array would have no elements.

Head back over to Unity and attach the script to something in the scene. Note that it doesn’t really matter where you attach it, but a root level object like the “Canvas” or a specially made “Controller” game object (just an empty GameObject) are pretty good candidates that someone might expect to use.

Assign the script’s label reference (note that it will appear as “Label” in the inspector even though we used “_label” in code) by dragging and dropping the Text object from the scene into the field on our script’s inspector.

Expand the “Responses” field by clicking the arrow, and set the size to hold as many responses as you want to fill in (should be at least one or the demo will produce an error, because my sample code does not show error handling). Add some responses like: “Yes Definitely!”, “Not a chance.”, and “Maybe.” to the array.

Select the Button in the Hierarchy panel. Now look at the bottom of the “Button” component in the inspector and you will see an “On Click” group which is currently empty. Click the “+” at the bottom of that group to add an “event listener”. Drag whichever object holds your Magic script onto the new object field which appeared. To the right of the object field is a function selector, currently it says “No Function”. Use that to select “Magic -> AskQuestion()”.

Now everything is setup. Run the scene. Ask your question and click for a response, which will be randomly chosen and displayed in our label.

Summary

In this lesson we learned how to write and invoke custom methods. We covered methods with and without return types as well as with and without parameters, including the option of multiple parameters. We reviewed a few of the methods you may frequently find useful as a Unity developer, which have a special meaning when included on any MonoBehaviour based script. Finally, we created an example program based on the Magic 8 Ball. There we created a method which acted as a kind of “event handler”, which means that we were able to trigger some of our code based on user interaction.

2 thoughts on “Methods

  1. Thanks a lot for this lesson!

    I found it very easy to follow, and really liked your examples. I also laughed every time you warned about the situations where the compiler would “complain” at me.

    During the Magic 8 Ball part I was about to give up. I followed everything to the letter but it wouldn’t work. I even opened your next lesson ready to quit, when it occurred to me that I might have made a mistake when I parented the objects in the hierarchy. After deleting the default text child object from the Button, and parenting the text object I created to Button, and then assigning said Text to the script… somehow… it worked!

    I think I had made a mistake when assigning a text object onto the label part of the script in the inspector. After more testing I found that it isn’t necessary to create another text component. It seems that when you create a button, Unity automatically creates a text object and makes it a child of the button? Just using that was enough.

    Anyway it worked and I’m happy. I even changed the text to give it a little more flavor as seen here:

    https://veuwer.com/i/34qb.gif

    Thank you again for the lesson! I find text-based lessons easier to get into my head, mostly because I narrate them to myself forcing me to really pay attention, whereas in video lessons I lost focus and begin “just hear” what someone else is saying instead of “getting” it.

    1. I’m glad you liked the lesson!

      Now that you mention it, I can definitely see why that might have been confusing. I had originally created a scene where there was a button which may have said something like “Press Me” and a separate Label not on the button that revealed the result. This would be yet another area where I could improve by adding more pictures 🙂

      Still, what you did clearly works and was probably easier to do. Great job on sticking to it and not giving up.

      Also feel free to ask if you get stuck in the future – it is helpful to me to know where I am not clear, and other readers might get stuck in the same place.

Leave a Reply

Your email address will not be published. Required fields are marked *