Lesson 24 - Method Inputs and Returns

Tutorial Series: Introduction to Unity with C# Series

Previous Article  |  Next Article


Transcript

Okay, so before we move on into more closely looking at the Unity API, I want to round out our understanding of methods which includes understanding how to define input parameters to add variability within the contents of the method definitions as well as look a little closer at return types. So adding these features to your method definitions lets you then get variable input and outputs from those methods when you call them. Before we get into that let's just first clean up our testing environment here a bit, let's deselect the AnimalFactory script, attach the Test game object, and also you've probably noticed this annoying warning message left over from the VariablesAndOperations script, because the variable val found in MyMethod3() is defined but never used, so let's just make use of that by outputting it to the Debug.Log(). So just type in Debug.Log(val) and then input val. Just as a formality, just so I can get rid of that message now that we've made use of that variable. All right, so return types revisited, I refer to a method in previous video which contains some code that had a return type. I try to explain it, sort of shoe horn into that video, that wasn't really dedicated to understanding method returns.

Let's look at that a little bit closer, that was in the RPGClasses script here. We define this return type in the UseWeapon() method. First, I probably could have made the return value itself a lot less confusing, so let me just Quickly rework this for you, in order to clarify what I was getting at. Let's make a temporary variable in this method to store this string, since we've gotten used to storing strings of variables. For the sake of clarity let's just do this. Define a local variable called, "phrase", and use this string that we've already created, and also let's define other local string called combinedPhrase which will be the result of concatenating that string with the WeaponType. That one right there. Then simply return that combination. I hope that right away looks a little bit more intuitive to you, I didn't have to store the strings in these temporary variables, but it's hopefully a bit cleaner and easier to discern what I was intending by presenting it this way. Now the key point I was getting at was when we're saying a method has return value, what the return keyword at the end of the method, we're saying we expect the method to execute in full, as usual, but in it's place leave whatever that method is returning to the caller. Right?

Where this method is being called, called the caller. This will be left behind in the exact place where the method is being called. Going over to where this method is called, in the RPGController script, we realize that after this method completes executing, it's going to leave behind in this code block, right there, right within the parentheses of the Debug.Log(), it's going to leave behind, or return to this code block, whatever we've specified in that method definition. So, in this case we are returning a string, so in all of our methods that return void, nothing in other words, we didn't really have anything, any options other than just call the method and let it execute the code. When we have a method that has something to return to the calling block of code, we can basically treat that call as ultimately leaving in it's place, whatever it's returning. In this method, this is returning a string, and we can do the exact same thing we do with any string. We can assign it to, assign it back into a variable for example, or output it to the Debug.Log() like we did. The Debug.Log() takes as an input, a string, but we could do it this way. Return first the, take the string that's being returned from UseWeapon() and put it into a temporary variable here. Let's define the temporary variable called, offenseMessage, and simply call that and overturn that string into that temporary variable.

Now it might look a little bit less confusing, if you then reference that local variable, that holds that return string value. Also to reiterate really quickly, yet again, I also want to make clear the one-to-one relationship that you have between the type of whatever is being returned, and the type specified before the method name, right here. That part that we normally write as void, you may think is being redundant by specifying the type there as well as conforming to the type that we are returning, but there is a technical reason for that, that you don't really have to worry about. One thing that you should know about this part of the method definition, is that this part that does not belong within the confines of the squiggly brackets, but this part, the method definition header, you can call it there. It's actually called the method signature. This method signature right now says that it's a method that has a return that is of type string, It's public, access modifier as well as the method identifier.

Now also part of the method signature are the parenthesis, which we haven't really been dealing with. We've been leaving empty thus far in our method definitions. Well, just like the earlier part of the method signature, that determines the method's return type, or you could say what it outputs. Within these parenthesis, we can determine what the method can input. In other words, something that you can pass into the method where the method is being called. We've already seen this with method calls, with methods that had already been defined for us, like the Debug.Log() method where we pass in strings because that method takes in strings. We haven't seen how this actually works on the definition side of things, so let's make a method definition that accepts a single input parameter. We don't really have to worry about this method anymore, so minimize it, and make a new method definition, public void, let's call it Deflect().In the parentheses we'll define an input parameter, that's what it's called on the method definition's side. A single input parameter of type string with the local variable name, weapon. Now we can reference that within the contents of that method definition, so let's make a variable here, another one called, phrase, a temporary local variable. Local to this Deflect() method. Don't get it confused with the variable, local variable in the Use Weapon() method, and simply write in a simple message.

We can reference that input parameter there. Why not, since we're not returning anything, we'll just put it right into the Debug.Log() here, within the method definition, just to show that you can do that. Here in the RPGController, let's call that method, so it's part of the wizard's weapon, remember? It's part of that field for our wizard. Call that Deflect() method, there you see within IntelliSense, it's telling us it expects an input parameter, it's expecting a string. Let's then input. You can put in any string, but let's put in this one. knight.weapon.WeaponType. Which we know is a Sword. In Unity you're going to want to enable that, so we can see the output to the console. There you see the message, "You deflected the combatant's sword." Pretty straight forward. Now let's do a single input and return instead. Let's add the, let's make the phrase return, as we kind of had before with returning a string. Return phrase. It's going to complain to us because it's going to want us to define the return type here in the method signature, so it all corresponds one to one. Back here, we're going to have to change this reference, or we're now going to have to return it to something, so let's go, string defenseMessage. Let's return it to that temporary variable there. Then output that to the Debug.Log(). We'll get the same output, but now we have combined both an input parameter and a return. Now we don't need any longer this message output, so let's comment that out. There you go. Same output.

The way I would describe the relationship between input parameters and the rest of the method definition is it's kind of like when you go somewhere and they ask you to fill out a form, so you're handed this form to fill out, which already has a bunch of pre-written stuff, stuff that you can't change, and stays the same with every form copy. That information that you can change when you grab a new form or call it, is much like the hardcoded contents of your method definition. Each time you call your method, that hard coded stuff always stays the same, but when you're handed a form, you'll get all of these blank areas in the form that expect you input some new information, the information that isn't already in the form, some variable information which can then be used by the rest of the form, you could say. Forms are usually organized in a way that implies what kind of information it accepts as input in different places. For example, you can input your name in the name portion, your address in the address portion and so on, so with method definitions that have input parameters, those input parameters define these blank spots in your method, which can later at method call, be filled in with the right kind of information being expected. Analogous to when you're filling out a form, it expects you to fill it out with a particular type of information. Though it's not as strict as in code, because nobody is, and you're faced with a compiler, when you're filling out forms.

This type of information is ultimately determined by the parameter type. That's why that's important, right there. Within the parentheses of method definition. With this at our disposal we add variability to our otherwise rigid "'Groundhog Day" method code. We allow inputs to be specified, at method call that can be then referenced throughout the method execution filling in those blanks in the form as I said earlier. Just so you know, typically on the calling side, the input parameter is referred to as the input argument. I mean that's how I usually hear it, although it varies, people sometimes use those terms interchangeably. Perhaps keep that in mind when referring to this two-sided coin of method definitions versus method call. Handy as always was you saw IntelliSense, which was when you type in the method call, nit reminds us that we need to supply those input arguments, and it shows you also if you have a return. Another thing that you should be aware of is that you can add as many input parameters as you need. You can add them to the method signature in the parentheses as we did with the other input parameters, but separating them with a comma. Let's do this, let's add another input parameter to this method called... Let's make it an int called Degradation, right? It's going to refer to a field that we haven't created yet, so let's create that now. We don't have to make this public, so I'll just say private,. private int, Degradation. We don't need to access this outside of this class, I know that ahead of time. That's why I'm making it private, not a big deal. We'll reference that local lower case D, degradation int, that we're going to provide at the method call. We'll reference it in the method definition as follows, so whatever integer we input at the call, we will minus it from our existing amount, integer amount, the upper case D, Degradation field. Degradation -= degradation, lower case D.

Simple, not getting too complex here. Here we'll just add some extra information. This is going to look a little bit unwieldy here. Actually, I'm going to put on another line as we saw before, use of white space is pretty much up to your liking. To make it look a little bit easier on the eyes and not flow off the edge of the screen, I'll concatenate another string here. Degraded your weapon to then we'll output the value for Degradation, capital D, the field, and still return phrase. Now we've added an integer at the input, that now is going to change this integer field variable and we're going to output that with the returned string. Now we know when we go back here, we need to, it says build failed, so we need to input the integer value. Let's just say 30. What do you think it's going to output, try to predict it. "You deflected the combatant's sword," should put a space after that, "and degraded your weapon to 70." Alright, pretty simple, let's just fix that little hiccup there. Put a space right there, that'll fix that. Alright, let's do something a little bit more interesting. Let's use an object as an input parameter. Right, you can use an object too. An object is just as valid a type as any other, so let's actually let's input a Knight, an entire Knight can go into this method. I will call it knight, lower case to be referenced here. As we see this reference no longer applies, we'll have to change that. Let's leave that.

Actually, I don't really like how this looks, so I'm going to actually show you something you're not going to use very often, at least probably not in Unity, but it's a handy method called Format(), available to the String class, built into C#. Let's rework this entire phrase here, and return from the Format() method is going to be a formatted string. Let's access that method, like so. We can put in any string we want, this is how the Format() method works. Actually, this is going to look a little bit funny, so what I'm going to do is I'm going to make sure that the entire string is on one line. I'll put it here, "You deflected the" this is where the magic happens, I'll explain this is in a minute, it's going to look a little bit funny until I explain it. That, degraded your weapon to that. Now what are those references? Here I'm just going after the string we have other input parameters, see it tells us it takes an arg object. That's not going to make a lot of sense right now, but anyways. I'll just input the things we have available to us ... Get the type of the knight, we know it's going to be knight, that's the first argument, the second argument is going to be the WeaponType, and the third one is going to be Degradation.

This looks pretty funny at this point. Other than this part, which really just, I reformatted so it's looks a little bit easier so it doesn't all trail off on one line. The real difficulty when looking at this is probably the relationship between these place holders, is what they are and the other argument. There's the first string argument here, separated with a comma. The second argument, and third, and fourth argument here. The relationship between these placeholders and the arguments is where the {0} is defined, whatever you have in the first spot, the second argument, right here will get placed into wherever that {0} placeholder is, and same goes for here, that will go into the first placeholder and last one will go into the second one. This is more useful in things typically that use a lot of text formatting, not so frequently used in Unity, but nonetheless, I thought I might as well show you right now. Otherwise don't get too bent out of shape about this. It does the exact same thing that we did before with all those pluses and concatenation that I think looked just a little bit more wonky than this, so that's why I showed you that. Now, okay, now that we're taking in a knight as a method input parameter, on the calling side we need to actually input, not weapon type, which is a string, we need to input a knight. We already have a knight and let's go back to the output. "You deflected the knights sword and degraded your weapon to 70."

Alright, so a question that's often asked at this point is, when and why would you ever use input parameters? I would say it's most often used when you expect to call a method more than once, typically, and you expect it to have something different about it each time you call it. In other words you don't expect your method to be like Groundhog Day, repeating the exact same thing every time. Another instance would be if the method is being called from an external class, such as what we did here, and it requires the information to be sent to it from that external class in which it's being called. Again, like you just saw. Now methods can reference input parameters as well as class level fields, so if it's a method, it doesn't need anything external to that class passed into it, and say it just manipulates the values of certain fields within a class that it's defined. It probably won't even need input parameters, or to return anything to the caller for that matter. Which is exactly how we've been using most methods we define in Unity, so far within our MonoBehaviour classes. They have all been methods that just handle fields and local variables within the same class they are defined. So far that is pretty much all we've seen with our own use of method definitions we've created When and why use return types? The same question often comes up with return types. The rule of thumb here is just to define methods, default to just returning void, not returning anything at all, and only returning something to the caller in the case where something has to survive after the method process completes.

Remember how I showed you that variables declared in methods don't survive between method calls? Unless their fields at the class level for example? But variables that are declared first in the methods, sometimes they need to be returned. They have to survive outside of that method when it's called and executes. In this case by returning a value after the method completes, there is a sort of escape clause, a special case scenario where after the method completes, you have this surviving bit of information at the end of it that you can immediately hand off to the rest of your code. It's up to you as a programmer to decide whether or not to use a return for method. The deciding factor is often whether or not we need something to survive outside the method once it completes. So keep that in mind. I have a bit of an analogy for this, so if you're a science geek like me, you may know about black holes, well just about everyone knows about black holes, and that nothing escapes black holes, right? Nothing can be returned from a black hole, once something goes in, back to the scope that lies outside of it, that's why they're black, not even light, the fastest thing in the universe, can travel fast enough to escape the gravitational pull of black holes. Well, scientists have actually come to learn that there is an escape clause in black holes. Something called Hawking Radiation, after the physicist, Stephen Hawking, which is basically Quantum particles popping outside of the bounds of the black hole. That escape clause where that information survives, that quantum information that can escape the inner confines of a black hole, is kind of like a methods return, allowing something to pop back out of whatever happens inside of the processes within a method.

Right, final rule of thumb I would say is just write methods with void and without input parameters until, or unless, it becomes obvious that that won't be enough, and you'll maybe want to consider using some sort of input parameters or output returns and also remember to use IntelliSense as your guide. It's really helpful to remind you whether or not a method you created needs a return or an input parameter. I think that's about it for methods for now. A lot of information, but it's important for understanding how to use methods which we'll be using extensively as we go back to coding our prototype project. Alright, thanks a lot, I'll see you next video.


Related Articles in this Tutorial:

Lesson 1 - Who This Course is For

Lesson 2 - What to Expect from this Course

Lesson 3 - Installation and Getting Started

Lesson 4 - Starting the First Project

Lesson 5 - Prototype Workflow

Lesson 6 - Basic Code Review

Lesson 7 - Game Loop Primer

Lesson 8 - Prototyping Continued

Lesson 9 - C# Fundamentals and Hello World

Lesson 10 - Variables and Operations

Lesson 11 - Variables and Operations Continued

Lesson 12 - Floats, Bools and Casting

Lesson 13 - If Statement Conditionals

Lesson 14 - If Statements Continued

Lesson 15 - Complex Evaluations and States

Lesson 16 - Code Syntax vs. Style

Lesson 17 - Variable Scope

Lesson 18 - Object-Oriented Programming Intro

Lesson 19 - OOP, Access Modifiers, Instantiation

Lesson 20 - Object Containment and Method Returns

Lesson 21 - "Has-A" Object Containment

Lesson 22 - "Is-A" Inheritance Containment

Lesson 23 - Static Fields and Methods

Lesson 24 - Method Inputs and Returns

Lesson 25 - Reference vs. Value Types

Lesson 26 - Introduction to Polymorphism

Lesson 27 - Navigating the Unity API

Lesson 28 - Applying What You've Learned and Refactoring

Lesson 29 - Constructors, Local Variables in the Update Method

Lesson 30 - Collecting Collectibles, Items and Powerups

Lesson 31 - Spawning and Managing Prefab Powerups

Lesson 32 - Implementing Powerup State Logic

Lesson 33 - Displaying Text, OnGUI, Method Overloading

Lesson 34 - Referencing Instantiated GameObjects, Parenting

Lesson 35 - Understanding the Lerp Method

Lesson 36 - Creating Pseudo Animations in Code

Lesson 37 - Understanding Generic Classes and Methods

Lesson 38 - Animations Using SpriteSheets and Animator

Lesson 39 - Working with Arrays and Loops

Lesson 40 - Debugging Unity Projects with Visual Studio

Lesson 41 - Camera Movement and LateUpdate

Lesson 42 - Playing Audio Clips

Lesson 43 - Routing Audio, Mixers and Effects

Lesson 44 - Adding Scoring Mechanics and Enhancements

Lesson 45 - Scene Loading and Game Over Manager

Lesson 46 - Understanding Properties

Lesson 47 - Controller Mapping and Input Manager

Lesson 48 - Understanding Enums

Lesson 49 - Dealing with Null References

Lesson 50 - Handling Variable Framerates with time.DeltaTime

Lesson 51 - Preparing the Project for Final Build

Lesson 52 - Final Build and Project Settings

Lesson 53 - Introduction to the Unity Physics Engine

Lesson 54 - Understanding FixedUpdate vs. Update

Lesson 55 - Movement Using Physics

Lesson 56 - Attack Script and Collision Events with OnCollisionEnter2D

Lesson 57 - Projectiles and Stomping Attack

Lesson 58 - Parallax Background and Scrolling Camera

Lesson 59 - Infinitely Tiling Background Sprites

Lesson 60 - OOP Enemy Classes

Lesson 61 - OOP Enemy Classes Continued

Lesson 62 - Trigger Colliders and Causing Damage

Lesson 63 - Multi-Dimensional Arrays and Procedural Platforms

Lesson 64 - Finishing Touches

Lesson 65 - Series Wrap


Comments

Please login or register to add a comment