An Artist's Guide to C# in Unity3D: Functions and Methods
This article is part of an ongoing series for code-leaning artists who want to learn Unity 3D. In this article we learn about functions and methods.
This article is part of an ongoing series for code-leaning artists who want to learn Unity 3D. In this article we learn about functions and methods.
Read Previous: Flow Control
Congratulations for making it this far, you motivated learner!
This is the last topic in our C-sharp fundamentals section. After this, you could pretty much write your own game with the knowledge you already have—pretty much.
So, let's continue with functions and methods. For our purposes, I’ll use the terms function and method almost interchangeably. The difference between a function and a method is purely contextual—similar to the difference between a man and a husband (or a woman and a wife). A husband is just a man within the context of matrimony. All husbands are men, but not all men are husbands.
The same applies to functions and methods. All methods are just functions that are associated with an object. Incidentally, in C-sharp all functions are associated with an object, so the difference is negligible. We’ll learn more about objects in an upcoming lesson about classes.
For this lesson let's stick mostly with the term function. Where I use the term method, you can understand that to mean the same thing as function.
Function Basics
A function is a chunk of code that you assign to an identifier, the same way a variable is a piece of data that you also assign to an identifier. If a variable is a container for data, then a function is a container for code. We use functions for organization, good program structure and code reusability.
If we view functions as containers for code, we should mention a few things about this container. For one thing, once the code is written, it helps to think of the function as a black box, or at least a self-contained box. This box accepts specific input data from one end, crunches this data internally, and then spits new data out of another end.
The basic syntax looks like this:
access_level return_type function_name(parameter_list) { function_body }
The components of a function are:
function name — this is the identifier you’ll use to reference and call the function
parameter list — the input data provided to function, usually represented as a comma-separated list of variables. If the function doesn’t accept any parameters this is left blank.
function body — this is your function’s code—the guts of the function. Every time the function is called, this code is executed from top to bottom.
return type — when the function is called it returns a value of this data type
access level — functions have different access levels to determine who (which objects) can call them. It makes more sense to cover this in an upcoming lesson about classes, so I won't say anymore about this here. We will skip the access level for all our examples in this lesson.
Let’s look at the example of a temperature conversion function. This function could accept a temperature value in Fahrenheit, run its internal conversion equation, and then output a new temperature value in Celsius. We’ll call the function TemperatureConverter
.
If you’re using the Fundamentals.cs
file, place the following code right after the Start()
method—just below the bottom curly brace ( } ) but before the Update()
method.
float TemperatureConverter(float fahrenheit) { // our code will go here }
In the code above, we skipped the access level and started with the return type. Since this is expected to be the temperature value in Celsius, we opted for a data type of float. Then we used the name TemperatureConverter
followed by a list of parameters (expected inputs) enclosed in parentheses. We’re only expecting one input variable so we list it with the right data type and name it accordingly—float fahrenheit
.
Right now, our function body is empty. It just contains the comment, “
our code goes here”
. Let's make it do some real work and properly return the Celsius value.
float TemperatureConverter(float fahrenheit) { // we use the fahrenheit-to-celsius conversion equation // to get the celsius value, // then we assign it to a variable float celsius = (fahrenheit − 32) * 5/9; // then we return the celsius value as the output return celsius; }
There.
We have a functional TemperatureConverter
, umm… function. Let’s use it in code.
In the Start()
method lets add some more code that calls the TemperatureConverter
. Modify the method to look like this:
void Start() { float fahrenheit = 36; // 36 degrees Fahrenheit float celsius; // no value yet // now call our converter function to get a celsius value // Note: the data type of the variable passed to the // function must match what the function expects celsius = TemperatureConverter(fahrenheit); Debug.Log($"{fahrenheit} Fahrenheit is {celsius} Celsius"); }
This is great so far.
Right now our function is only useful if we have a Fahrenheit value and want to convert it to Celsius. What about going the other way? Passing a Celsius value to get a Fahrenheit value back? Well, one option is to write another function, just like the one we have, to convert from Celsius to Fahrenheit values. However, that seems like doubling our work for not much gain.
What if we modified our function to accept a second parameter that indicates what type of value we are providing. Then we would know which type of value to return.
For example, if we passed the values 36
and “fahrenheit”
, the function would know it was receiving Fahrenheit readings and therefore return a Celsius value. However, if the second parameter was “celsius”
then it would convert to and return a Fahrenheit value.
Let’s make those changes to TemperatureConverter
.
float TemperatureConverter(float temperature, string scale) { // declare a local variable that will hold our new temperature float convertedTemperature; // we create a set of if-else clauses, to check // the scale parameter if (scale == "fahrenheit") { // convert to celsius convertedTemperature = (temperature − 32) * 5/9; } else if (scale == "celsius") { // convert to fahrenheit convertedTemperature = (temperature * 9/5) + 32; } else{ // print error message if scale is unknown Debug.Log("Error: Unrecognized temperature scale!"); } // then we return the converted value as the output return convertedTemperature; }
Now the code in the Start()
method should be changed to something like this:
void Start() { float temp = 36; // 36 degrees Fahrenheit string tempScale = "fahrenheit"; float convertedTemp; string convertedScale; // Now call our converter function to get a celsius value // Note: the data type of the variable passed to the // function must match what the function expects convertedTemp = TemperatureConverter(temp, tempScale); // show the right convertedScale to use in the print message if (tempScale == "fahrenheit") { convertedScale = "celsius"; } else if (tempScale == "celsius") { convertedScale == "fahrenheit"; } Debug.Log($"{temp} {tempScale} is {convertedTemp} {convertedScale}"); }
Play around with the temp
and tempScale
values to get different results.
Finally, what if we want to create a function that doesn’t return any values. Maybe a function that just prints the current date and time to the console and nothing else. Functions with no return value use the keyword void
as their return type—as in, they return a void.
Here is our function to print the date and time.
// The function uses the void keyword to indicate that it // doesn’t return anything. It's parameter list is also empty // because it doesn’t take any parameters
void PrintTimeNow() { // we print the current time and date // using C#'s built-in DateTime object Debug.Log(DateTime.Now); }
Add a line in the Start()
method to call this PrintTimeNow
function.
PrintTimeNow();
Now the date and time gets printed to the console as expected.
Try it out
Time to practice creating different functions on your own. Here are a few exercises to test your knowledge.
Create a function to calculate the area of a circle. The formula for calculating a circle’s area is
area = pi * (radius * radius)
. Use3.14
for the value ofpi
. The function should accept just one parameter—radius
—and return the area of a circle with the given radius.Write a function that takes an integer between
1
and7
and returns the corresponding day of the week spelled out. So,1
should return“Sunday”,
2
should return“Monday”
,3
should return“Tuesday”
and so on. Do the same for the months of the year (1 - 12
).This last exercise is closer to something you’re likely to write during game development—a function that inflicts health damage on a character. To start with, create a variable to store your character’s
health
and set it to10
(remember to place this just above theStart()
method).
Next, create a function calledDamageHealth
which accepts one integer parameter calleddamage
. This is the amount of “damage” to subtract from the character’s health. Add code to prevent the health score from dropping below zero (0
). Print the health value after each call.
Congratulations!
Give yourself a well-deserved pat on the back.
You now understand the fundamentals of C-sharp. And, believe it or not, you could write a full game with the knowledge you currently possess.
You could branch off from here and start experimenting with creations of your own, or you could continue with the upcoming lessons to gain intermediate knowledge and try your hand at some guided projects.
An Artist's Guide to C# in Unity3D