Unity’s Programming Language
C# (pronounced C sharp) is a widely used object-oriented programming language developed by Microsoft, first introduced in 2000 as part of the .NET framework. C# provides a powerful and flexible platform for building a wide range of applications, including the game engine Unity.
C# is based on object-oriented programming (OOP). Objects are instances of classes that encapsulate data and behavior. C# allows you to define classes, create objects from those classes, and interact with them to build complex systems.
If you have taken Java, you will find that it is very similar to C#. An example is that both language require that variable types must be explicitly declared and enforced during compilation. They also follow similar syntax and have comparable features like classes, inheritance, and polymorphism. This means that transitioning to C# is relatively smooth. However, it's worth noting that C# has its own unique features and libraries that differentiate it from Java.
If you haven’t taken Java that is fine. You do not need to be completely fluent in C# to develop a game. We will also be presenting C# code every step of this course. This course focuses on C# for only a few days.
Now let’s review some of the topics that one may need when programming their first game.
Objects
Inheritance
If-Statements
In programming, the If-statement is a fundamental control structure that allows you to execute code based on a certain condition. It enables your program to make decisions and perform different actions depending on whether a condition is true or false.
The syntax of an If-statement in C# is as follows:
if (condition) { //Code to execute if the condition is true }
Here's a breakdown of the components:
if
: This is the keyword that indicates the start of an If-statement
condition
: This is an expression or a condition that evaluates to either true or false. It can involve comparison operators (e.g.,==
,>
,<
,>=
,<=
,!=
), logical operators (e.g.,&&
,||
,!
), or any valid boolean expression.
{}
: This is a pair of curly braces that encloses the block of code to be executed if the condition is true. It is optional if there is only one line of code to execute. However, it's good practice to always include curly braces to clearly define the scope of the code block.
Let's see an example to understand If-statements better:
int age = 25; if (age >= 18) { Console.WriteLine("You are an adult."); }
In this example, we have an integer variable
age
assigned a value of 25. The If-statement checks if the age
is greater than or equal to 18. If the condition is true, it executes the code inside the curly braces, which is to print "You are an adult" to the console.Optionally, you can also include an
else
statement after the If-statement to define a block of code that executes when the condition is false:int age = 15; if (age >= 18) { Console.WriteLine("You are an adult."); } else { Console.WriteLine("You are a minor."); }
In this updated example, if the
age
is less than 18, the code inside the else
block will be executed, resulting in the output "You are a minor."Additionally, you can use multiple
if
statements in succession or combine them with else if
to create more complex decision-making structures.If-statements are incredibly useful for controlling the flow of your program based on specific conditions. They allow you to create dynamic and responsive behavior in your code.
Loops
Methods
A method in C# is basically just a set of instructions with a name that we can call whenever we want. Think of it as a mini-program inside a bigger program!
using System; class Test { static void Main(String[] args) { //do thing1 //do thing2 //do thing3 Console.WriteLine("Hello!"); //do thing1 //do thing2 //do thing3 Console.WriteLine("Test"); //do thing1 //do thing2 //do thing3 } }
That sounds like a lot of repetition, doesn’t it? We have to call the lines to do thing1, thing2, AND thing3 in that exact order again and again. What if there was a way to simplify it? Enter: methods.
using System; class Test { static void Main(String[] args) { doThings(); Console.WriteLine("Hello!"); doThings(); Console.WriteLine("Test"); doThings(); } private static void doThings() { //do thing1 //do thing2 //do thing3 } }
There we go! Now it is easier to call all of that code, and the code itself is easier to read.
If we use the analogy of a mini-program inside a regular program again, it makes sense that we can do anything that we can do in the regular program in the method. Therefore, we can set variables, if statements, loops, etc. The property of scope still holds here though, so the variables declared inside the function only exist inside the function and are lost outside of it.
We can also pass in and pass out parameters and values, to make our method more dynamic.
using System; class Test { static void Main(String[] args) { doThings(); Console.WriteLine("Hello!"); doThings(); Console.WriteLine("Test"); doThings(); } void doThings(String ) { //do thing1 //do thing2 //do thing3 } }
Let’s break down the components of this method:
private
: access permissionThere are three types of access prmissions,
public
, private
, and protected
.public
: This method can be accessed from anywhere.private
: This method can only be accessed from the class itself.protected
: This method can only be accessed from the class and any classes that inherit it.static
: type of method (static/non-static)A static method is one that changes all instances of objects created from a class, while a non-static method is one that only changes the instance that it acts upon.
As an example, let’s consider the
Car
class. A non-static method could be used to change one specific car’s attributes, and a static method could be used to change all of the cars that have been created and will be created. Let’s make an example car, with
Car car1 = new Car();
If we wanted to change the color of one specific car, we could use a method like
car1.setColor("red");
See how the
setColor()
method is only called on car1
, because only car1
is getting its color changed. If we made another car, car2
, before setting the color of car1
, car2
wouldn’t have its color changed because the setColor()
method was not static.However, what if we wanted to, for some reason, change the brand of the car (for the sake of the example, from Honda to Toyota)? Well, if we did that, we’d want EVERY single car made by the
Car
class to change their brand from Honda to Toyota. If we used a non-static method,Car car1 = new Car(); Car car2 = new Car(); car1.setBrand("toyota"); car2.setBrand("toyota");
It would theoretically work, but then we would have to set the brand of every single pre-existing car, as well as any new car created by the code. That sounds like a lot of work! A common theme with many of these concepts is that, if something requires a lot of repetition, there is probably an easier way to do it (for loops, methods, now this)
What we could do instead is make the
setBrand()
method static, changing it from public void setBrand(String s) { //don't worry about the string inside the parentheses, we'll get to that later //set brand }
to
public static void setBrand(String s) { //don't worry about the string inside the parentheses, we'll get to that later //set brand }
Now, in the main method, we could just write
Car car1 = new Car(); Car car2 = new Car(); Car.setBrand("toyota");
Notice how now, rather than calling the
setBrand()
on a specific object of the Car
class, we call it on the class itself. This makes sense when you think about the purpose, because rather than changing a specific object’s attributes, we are changing the class as a whole so that every new object and every pre-existing object has that value modified. void
: return typeThis is basically the type of variable that you want the method to return. Suppose if you wanted the program to return a
String
, you would replace the void
with a String
. This attribute holds for any return type, as long as you make sure to actually return the value. If we wanted to return a String
in a method, we would write public String doThings() { String s = new String(); //do things return s; }
In this case, a
String
, s
, was returned by the method. If we called this doThings()
method,doThings();//returns a String String s = doThings(); //the returned String is stored in the variable s
A couple things to keep in mind here are that you can only return one instance of the type. If you wanted to return multiple types or multiple instances of a type, a tuple or ArrayList would work better.
(String s)
: parameter(s) passed inIf we want to make our method dynamic, we need some sort of indicator as to what we need to act upon and return dynamically. That’s where parameters come in. When calling a method, you can pass in any number of parameters to give the method knowledge about why you are calling it, as long as those parameters are explicitly stated in the method declaration. For example,
public String doThings(String parameter) { String s = parameter; //do things return s; }
In this case, we pass in a
String
called parameter
, that holds information that the method can then act upon. Judging by what the function does with that variable, we can assume that it is modifying the String by setting it to a temporary variable s
. If we wanted to call the method, we would do
doThings("test");
We included a string in the parentheses, because the method directed us to. If we wanted, we could use more parameters, like
public String doThings(String parameter, int value) { String s = parameter; //do things return s; } ... doThings("test", 9);
The parameters have to be in the order and of the type specified to work properly, and anything otherwise will throw an error.
If we use the analogy of a mini-program inside a regular program again, it makes sense that we can do anything that we can do in the regular program in the method. Therefore, we can set variables, if statements, loops, etc. The property of scope still holds here though, so the variables declared inside the function only exist inside the function and are lost outside of it.