Learning C# Notes - Part I: Classes

If you work on Game Audio, sooner or later you will need at least some basic knowledge about programming since implementing audio usually needs logic and math. Game engines and specially middleware like Wwise or Fmod does a lot of that that work for us but there are many occasons where you need some extra customization and for that, nothing beats programming.

I would also say that programming is very satisfying… when things work. Well mybe not when things work but when you understand how things work. At least in my case, I like logic and math so builiding a system and seeing that it all works when you push a button is very rewarding but it takes time to get there.

My only previous contact with coding was some high school classes too many years ago and some failed attempts at Code Academy. I never really had the need or drive to learn so I didn’t. Now that I work on the games industry as a sound designer, I found the perfect oportunity to learn so I went all in.

If you want to learn C#, there are great resources in Youtube and documentation online so maybe this post is not the best place to start. But later on, once you have some experience, this could be a good way to learn things from another perspective and at the same time refresh some concepts.

So here are my notes as I have been learning C# with some examples to show syntaxis. When I finish this series of posts I will compile all the information in one long mega-post to have a sort of C# bible.

Classes

  • Most fundamental functional unit in C#.

  • They work as blueprints which define objects.

  • These objects contain data and instructions.

  • C# is an object oriented language: these objects act as self contained functional units that can talk to each other.

  • See example below of a class that describes the notion of a car.

public class MyCar
{
    //Car Attributes
    int doors = 5;
    int year = 1967;
    string model = "Alhambra";

    //Car Actions
    public void Drive()
    {
        //The car drives.
    }

    public void Rust()
    {
        //The car rusts
    }
}

Class Instantiation & Constructors

  • Classes can be instatiated. This is like grabbing the blueprint and building a unit of the thing that the blueprint defines.

  • Once you create an instance, it can contain its own values and be independent from other instances.

  • You can use a class without creating an instance if it is a static class. (More on this later)

  • Constructors are used when we instatiate a class, even if you don’t explicitely have one on your class.

  • Constructors will create the instance using default values unless you create a custom constructor which can take different values and apply them when instatiating (see example below).

public class MyCar
{
    //Car Attributes
    int doors = 5;
    int year = 1967;
    string model = "Alhambra";

    //Constructor
    public MyCar(int carDoors, string carModel)
    {
        year = carDoors;
        model = carModel;
    }
}
  • To create a constructor, we create a method inside the class that has the same name as the class and no return value. (see above)

  • If we want the ability to input certain values when creating the class instance instead of using default values, we add these values as parameters to the method that we created (the constructor).

Class Inheritance & Abstraction

  • Classes can be independent from each other or they can have a parent-child relationship.

  • When a class is child of another, it inheriths all of it functionality and values.

  • It can be very convenient to have base classes to build from. For example, our car class could inherit from a parent “vehicle” class so we get its functionalities and variables.

  • Sometimes, we know that a base class is only going to act as a parent that will never be instantiated and in those cases we can add the modifier “abstract” to it.

  • So an abstract class can never be instantiated and it can have incomplete information (like empty methods) that its children must contain and complete.

  • Inheritance is indicated with a colon after the class name. See example below.

public class MyCar : Vehicle
{
}

Classes & Unity

  • In Unity, usually each class is contained in its own script file but sometimes a single file can contain several classes.

  • When we create a new c# script in Unity, you will see that we are always creating a new class which inherits from Monobehaviour.

  • Monobehaviour is a parent class that gives you access to functions related to runtime like Start() or Update(). So these scripts usually deal with things that work in real time.

  • Scripts inheriting from Monobehaviour also allow you to add them to any game object as component. So you can think about them as "custom unity components". See how the scripts below, which inherit from Monobehaviour, act as components.


  • Another Unity parent class that is usually used is ScriptableObject. In contrast with Monobehaviour, scripts that inherit from ScriptableObject don't have runtime related functions and can't be added as components.

  • Nevertheless, these scripts can be created as custom data objects to contain information about any concept in a game like enemies, weapons, sounds or shaders and reside on our project as an asset file.

  • Scriptable objects can be instatiated via code but the most usual and useful way to create them is on Unity as a project asset. To do this, we use the "CreateAssetMenu" keyword when we define them. See example:

[CreateAssetMenu(menuName = "YourGame/Audio/Data/Weapon")]
public class WeaponAudioData : AudioData
{
}
  • If you think about it, scriptable objects are a way to separate funcionality and data. The first will stay on Monobehaviour scripts running in real time while the data will reside on scriptable objects and access when needed. This makes code much more scalable and easy to debug.

  • Lastly, keep in mind that Unity can also contain classes that just don’t inherit from either Monobehaviour or ScriptableObject.