Writing, Instantiating and Initializing Pure C# Classes (POCOs)

Objective: Create an instance of a Pure C# Class with a Constructor or a custom method.

Natalia DaLomba
Women in Technology
4 min readJun 30, 2023

--

Pure C# Classes (POCOs/Plain Old C# Objects) don’t inherit MonoBehaviour. Think of a POCO as a blueprint to a program.

A huge benefit to creating a POCO is you can create a Constructor (or multiple Constructors) in how you want your POCO to be initialized. When you inherit from MonoBehaviour, you can but unpredictable things can happen when you do that and it’s not recommended to.

Also, say you have over 100 weapons in your game. All the weapons all have shared variables that define a weapon. Instead of writing 100 C# scripts to define what each weapon is, it makes much more sense to write one POCO that you can copy as many times as you’d like to make as many weapons as you’d like.

Here’s an example of a POCO:

public class Weapon {
public string name;
public float fireRate;
public int ammoCount;
}

Then in another class, we can create a handle of the Weapon POCO, instantiate it and initialize the values for machineGun:

What if we have several weapons that we’ll have to initialize just like machineGun? This becomes a very tedious process. One benefit of POCOs is they have something called a Constructor and it’s how we can initialize objects by passing in the information. The Constructor belongs in the POCO itself and is named the same exact name as the POCO:

public class Weapon {
public string name;
public float fireRate;
public int ammoCount;

public Weapon(string name, float fireRate, int ammoCount) {
//initialize here
}
}

Note that you generally don’t want to use a Constructor with a MonoBehaviour Class. Now we can do here is initialize an instance of this Weapon to the data that was passed in:

public class Weapon {
public string name;
public float fireRate;
public int ammoCount;

public Weapon(string name, float fireRate, int ammoCount) {
//initialize here
this.name = name;
this.fireRate = fireRate;
this.ammoCount = ammoCount;
}
}

So for example, public string name that is at the top of the Weapon Class will be set to name that is passed into the Constructor. The same is done for all of the variables in Weapon.

Having these parameters you can pass in is much more modular and easier to create an instance of the Weapon POCO whenever we want.

Keep in mind, we can do anything inside the constructor so every time Weapon is created, something happens. Like we can add 10 to the ammo count if it made sense for what we’re doing.

Also, we can create multiple Constructors to pass in no parameters or different combinations of parameters to pass in.

Now going back into our Player Class, we change how we initialize machineGun:

public class Player : MonoBehaviour {
//other code

private Weapon machineGun;

void Start() {
/* OLD CODE
machineGun = new Weapon;
machineGun.name = "Machine Gun";
machineGun.fireRate = 0.1f;
machineGun.ammoCount = 100;
*/

machineGun = new Weapon("Machine Gun", 0.1f, 100);
}

We now instantiate and initialize machineGun all in one line and pass in parameters through our Constructor.

Another way to simplify instantiating and initializing a POCO is to create a method inside the Class you’re trying to initialize the Custom Class like so. This is if you don’t want a Constructor in your POCO. It works practically the same:

public class Player : MonoBehaviour {
//other code

private Weapon machineGun;

void Start() {
//machineGun = new Weapon("Machine Gun", 0.1f, 100);

machineGun = CreateWeapon("Machine Gun", 0.1f, 100);
}

private Weapon CreateWeapon(string name, float fireRate, int ammoCount) {
Weapon weapon = new Weapon(name, fireRate, ammoCount);
return weapon;
}

Personally, I would instead create a Constructor in the POCO to keep everything relating to the POCO, in that Class.

Know that because we are not inheriting MonoBehaviour, even if we have the variables in our POCO be public, the variables will not show in the inspector.

Serializing data allows Unity to read your object in the inspector. To serialize an object or a POCO, we can go to that Class and above the Class we can add an attribute like below:

[System.Serializable] //attribute
public class Weapon {
public string name;
public float fireRate;
public int ammoCount;

public Weapon(string name, float fireRate, int ammoCount) {
//initialize here
this.name = name;
this.fireRate = fireRate;
this.ammoCount = ammoCount;
}
}

[Serializable] marks classes, structs, and other types. [SerializeField] marks instance fields.

MonoBehaviours are instantiated either in the Unity editor when you’re adding them as components, or by gameObject.AddComponent<T>() where T is your MonoBehaviour type. Regular classes are not shown in the inspector unless they’re grouped under a MonoBehaviour.

Regular classes (POCOs) do use [Serializable] with (using System;). That just tells Unity that this data type can be shown in the inspector, or saved/loaded with JSON (JsonUtility uses the SAME serialization rules as the inspector). MonoBehaviours have a C++ backing side to them, which needs to be created in the engine. By Awake and OnEnable, this is all setup and ready to go.

But the Constructor happens too quickly on the C# for it to be ready.
Bad things would happen if our C# MonoBehaviour scripts started running Unity code during their object Construction (aka creating a Constructor in a MonoBehaviour Class), so it’s advised against using them for MonoBehaviours (MBs). POCOs need Constructors, otherwise you can’t create them.

--

--

Natalia DaLomba
Women in Technology

A Unity C# developer inspired by game design logic used to create digital adventures. https://www.starforce.games/devlog/