Programming 6. Classes and methods

6. Classes and methods

Classes & objects

  • Classes are blueprints for specific kinds of collections of data & functionality
    • E.g., an Enemy class tells what shared properties do all enemies have
  • Instance of a class is an implementation of that class
    • “This certain enemy right here”
  • Classes can be very useful in game development
  • C# is an object-oriented language (almost everything is a class)
    • All Unity scripts contain a class by default!
    • Usually, the term object also refers to an instance of a class

Unity class example

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class WrapAround : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

Creating a class from scratch

  • This is all you need for creating a class:
      class Enemy
      {
    
      }
    
  • The curly braces {} define a new scope
    • Everything inside them is inside the class

Fields

  • Fields are variables inside the class
      class Enemy
      {
          int HP; // this is a field
          string name = "Sanae"; // this is also a field
      }
    
  • Fields can have initial values

Methods

  • Methods are functions inside the class
      class Enemy
      {
          int HP;
          string name;
    
          void SetHP() // this is a method
          {
              HP = 4;
          }
      }
    
  • This function returns nothing, so its type is void

About functionality

  • You can declare fields and methods, but cannot have any functionality outside class methods
      class Enemy
      {
          int HP;
          string name = "Sanae"; // this works (initial value)
    
          name = "Reimu"; // this doesn't work
          HP += 4; // and neither does this. It needs to go inside a method
    
          void AddHP() 
          {
              HP += 1; // see, like this
          }
      }
    

Instantiating a class

  • Instances of a class can be created with the new keyword
  • Instances have the same fields and methods, but their values can be different
      class Enemy
      {
          int HP;
      }
    
      class Program
      {
          static void CreateThreeEnemies()
          {
              Enemy enemy1 = new Enemy();
              Enemy enemy2 = new Enemy();
              Enemy enemy3 = new Enemy();
          }
      }
    
  • NOTE: When creating new GameObjects, use Instantiate()

Object variables

  • The variables inside of an object can be accessed with the dot . operator
      static void CreateThreeEnemies()
      {
          Enemy enemy1 = new Enemy();
          Enemy enemy2 = new Enemy();
          Enemy enemy3 = new Enemy();
            
          enemy2.HP = 4; // wait, this does not work!?
      }
    

Own variables of the class

  • “dot scoping” is not needed when we’re inside the class
      class Enemy
      {
          int HP;
          void SetHP()
          {
              HP = 4; // this works!
          }
      }
    
  • We can also use the keyword this to refer to the class we’re in
    class Enemy
    {
        int HP;
        void SetHP()
        {
            this.HP = 4; // "this" also works! :D
        }
    }
    

Object variables: fix the previous example

  • We could not change the Enemy HP from outside the class, because class members are private by default
  • This can be changed with the public access modifier
      class Enemy
      {
          public int HP;
          string name;
      }
    
      enemy2.HP = 4;
    

Access modifiers

  • C# Docs: Access modifiers
  • Access modifiers can be used to give additional level of protection inside classes
    • public
      • Accessible and editable from anywhere
      • In Unity, public properties are also editable in the Inspector
    • private
      • Accessible only from within the class. it’s the default, but we can make it more explicit by writing it out
    • protected
      • Like private, but accessible also by the inheritors of the class
    • virtual
      • Accessible and overridable by inheritors

Conventions

  • C# Docs: Capitalization conventions
  • C# Docs: Coding conventions
  • Conventions are not mandatory, but following them makes for more readable code
    • You can also have your own conventions: it’s more important to write consistently than to write in a way Microsoft wants you to
    • In a team project, it’s better if everyone writes with the same conventions
  • My convention: Type and method names with PascalCase and variable names with camelCase
    • type naming: MyType
    • public variable naming: myVariable
    • private variable naming: _myPrivateVariable
    • function naming: MyFunction()

Class methods

  • As seen in the Unity class example, classes can contain methods
  • If the method is public, it can be called from outside of the class
  • Here, a public method makes changes to a private property
      class Enemy
      {
          private int HP;
          public int maxHP;
          public void Heal()
          {
              HP = maxHP;
          }
      }
    
      Enemy enemy1 = new Enemy();
      enemy.maxHP = 2;
      enemy1.Heal();
    

Static methods

  • static methods can be called without instantiating the class
    • These methods can’t change properties of a specific instance
    • They’re especially useful for implementing helper functions

      class MathFunctions
      {
          public static float VectorLength(Vector3 vector)
          {
              return Mathf.Sqrt(vector.x^2 + vector.y^2 + vector.z^2);
          }
      }
      
      Debug.Log(MathFunctions.VectorLength(transform.position));
      
  • See how we’re calling the method by referencing the class itself, never having to use the new keyword

Warning about static variables in Unity

  • Note: Static variables do not reset on SceneManager.LoadScene("sceneWeAreAlreadyIn");

Static classes

  • C# Docs: Static classes and structs
  • Even whole classes can be static, too
  • Then you can call all methods and properties from the class without instantiating it
  • Actually, you CAN’T instantiate a static class!

Exercise 1. Class for helper functions

Create a new Script class for math helper functions and properties (do not add it to any GameObject).

Add this Remap function there

Remap(float iMin, float iMax, float oMin, float oMax, float v)
{
    float t = Mathf.InverseLerp(iMin, iMax, v);
    return Mathf.Lerp(oMin, oMax, t);
}

Try to call the method from a GameObject!

Constructors

  • Constructor is an optional function that is called when a class is instantiated
  • In C#, constructor has the same name as the class itself
      class Enemy
      {
          private int HP;
          public int maxHP;
          public Enemy()
          {
              maxHP = 4;
              HP = maxHP;
          }
      }
    

Constructors with parameters

  • You can pass in parameters to the constructor at initialization

      class Enemy
      {
          private int HP;
          public int maxHP;
          public string name;
          public Enemy(int enemyMaxHP, string enemyName)
          {
              name = enemyName;
              maxHP = enemyMaxHP;
              HP = maxHP;
          }
      }
    
      Enemy grunt1 = new Enemy(4, "Grunt 1");
      Enemy grunt2 = new Enemy(4, "Grunt 2");
      Enemy boss = new Enemy(16, "Big boss");
    

Exercise 2: Hello class

  1. Create a console application with a class Animal, that contains
    • two strings: name and sound, and
    • a method Greet() that prints [name] says [sound]!
  2. Create a few animals with different names and sounds
  3. Call their greet methods from the main program

Inheritance

  • Classes can be made to inherit functionality of some other class
  • If class B inherits class A, all of the class A public functionality is available in class B
  • A is called the base class (or parent class)
  • B is called the derived class (or child class)

  • Use the : symbol to make a class inherit from another
    class Grunt : Enemy
    {
        ...
    }
    

Importing classes

Reference type vs value type

  • Value types actually store the value
  • Reference types store the memory address to where the value is actually stored
  • Value types
  • Reference types

Reference vs value example 1

int firstValueType = 15;
int secondValueType = firstValueType;
secondValueType++;
Debug.Log($"firstValueType: {firstValueType}, secondValueType: {secondValueType}");

var firstRefType = new Enemy(15, "FirstEnemy");
var secondRefType = firstRefType;
secondRefType.SetHP(16);
Debug.Log($"firstRefType.HP: {firstRefType.GetHP()}, secondRefType.HP: {secondRefType.GetHP()}");
  • When modifying secondValueType, firstValueType stays intact
  • When modifying secondRefType, firstRefType gets modified as well!

Reference vs value example 2

//Value type variable
Vector3 pos = transform.position;
pos = new Vector3(0, 2, 0); // TRANSFORM'S POSITION IS UNAFFECTED!!

//Reference type variable
Transform tran = transform;
tran.position = new Vector3(0, 2, 0);

Extra: Properties with getters & setters

public class PlayerClass
{
    private int exp; // private field, not accessible from outside

    public int Experience // public property, accessible from outside
    {
        get
        {
            // do stuff...
            return exp;
        }
        set
        {
            // do stuff...
            exp = value;
        }
    }
}

Properties vs fields

  • C# Docs: Using Properties
  • Properties are used to expose fields to the outside world
  • get is called when the property is retrieved somewhere
    • It can execute some code, and then give us some internal data we don’t want to directly expose
      Debug.Log(PlayerClass.Experience);
      
  • set is called when a new value is given
      PlayerClass.Experience = 10;
    

Property example

// Level is a property that converts experience points into the level of a player automatically
public int Level
{
    get
    {
        return experience / 1000;
    }
    set
    {
        experience = value * 1000;
    }
}

public int Health{ get; set;} // This is an example of an auto-implemented property

Example: Implementing the Vector3 class in C#

using System;

class Vector3
{
	public float x; public float y; public float z;
	
	public Vector3(float x0, float y0, float z0)
	{
		x = x0; y = y0; z = z0;
	}
	public void Print()
	{
		Console.WriteLine($"x: {x}, y: {y}, z: {z}");
	}
	public static Vector3 forward
	{
		get
		{
			return new Vector3(0, 0, 1);
		}
	}
}

public class Program
{
	public static void Main()
	{
		Vector3 vec = new Vector3(0, 4, 2);
		vec.Print();
		Vector3 forw = Vector3.forward;
		forw.Print();
	}
}