Human-written article

C# in Unity 2026: Features Most Developers Still Don’t Use

C# in Unity 2026: Features Most Developers Still Don’t Use

TL;DR: Most Unity C# code still looks like 2009 because Mono held the runtime back for years. I benchmarked properties, tuples, LINQ, and records against old-school patterns in Unity 2026 and documented where modern syntax actually wins, where it costs performance, and where I keep writing code the old way on purpose.

The Unity engine has evolved a lot in modern days, but I noticed a trend where Unity developers are still using "outdated" techniques when writing their C# code. There are multiple reasons for that, such as Unity’s historically limited Mono-based runtime and the influence of legacy tutorials.

If you are guilty of this, you are not alone. I fall into the same category. For years, I wrote code as if it were still 2009 because I never felt the need to change until recently. Now that Unity is gradually adopting modern C# features and moving toward a modern .NET/CoreCLR stack, I decided to share how to write more modern code. I will also include performance testing, because even though some C# features are modern, that doesn’t mean they are always the most performant.

On the positive side, I like to joke that AI is still training on outdated Unity C# code, which means we might be the last to be replaced. That’s not entirely true, but take it as a joke.

Table of Contents

Why do Unity developers write code the old way?

There are several reasons for this, but to understand them, we need to look at the history of Unity.

Why does Unity use the Mono runtime?

In its early days, Unity had strong support for macOS. At the time, Microsoft’s .NET runtime was only available on Windows and was not officially supported on macOS. Building their own runtime would have been extremely time-consuming, so Unity adopted Mono, an open-source implementation of .NET originally developed by Ximian.

Mono made it possible to run C# code across multiple platforms, which aligned perfectly with Unity’s goals. This decision later helped enable broader platform support, including mobile platforms like Android and iOS. Today, this kind of cross-platform support is taken for granted, but at the time, it was a major advantage.

Mono's influence on "bad" Unity code

Older versions of Mono in Unity limited or delayed support for many modern C# features, which had a direct impact on how developers wrote code. Because of these limitations, programmers adopted simpler, more manual patterns that avoided potential issues with performance and compatibility. During that time, most tutorials, guides, and learning resources were created based on those constraints, reinforcing the same coding style across the community. Even though Unity later introduced support for newer C# features, many of those habits persisted, and much of the existing educational content still reflects those older practices.

Influence of Old Unity Tutorials

Since developers at the time were not able to use every C# feature because of Mono limitations, they had to work with what was available. This directly shaped how Unity game development was taught.

What made Unity grow so fast was its community. Thousands of tutorials were created for YouTube and other platforms, covering almost every possible topic. Many of those tutorials are still popular today, which means new developers are often learning from patterns that were written under older technical constraints.

There is now an entire generation of developers whose only background is Unity. When they encounter modern .NET C# code, it can feel like a completely different language. Developers who come from a broader C# background are usually aware of these newer features, but many Unity developers are not. Their foundation comes from tutorials that reflect how Unity was used years ago, and those habits carry forward.

Unity Self-Taught Game Developer culture

This is not something that is formally documented, but something I have observed over the past 10+ years. Game development is usually driven by passion. People start learning it because they love games. Because of that, there are no strict rules on how things should be learned, and no clear path you must follow to become a developer. If you compare it to fields like medicine, architecture, or law, the difference is obvious.

Many beginners come from completely different backgrounds. They enter game development out of curiosity or passion, not through formal education. I am one of them. I started in 2013 when Flappy Bird became a huge hit. At that time, it felt like a gold rush for simple mobile games on iOS.

From what I have seen, there are different waves of indie developers. This is just my observation. The first wave was the Minecraft era, driven by Notch, and the second was the Flappy Bird era. In both cases, many people believed they could build a simple game and become successful overnight. It is similar to what we see today with AI. Every generation has its own gold rush moment.

Because there was no structured or standardized way to learn Unity and C#, there were no clear rules or expectations around how code should be written. Everyone worked with what they knew at the time. The next generation learned from those same sources, and that is how many of these practices continued and became the norm.

Do I need to know modern C# to code in Unity?

No. And there is a simple reason for that. Even if you were not allowed to use any modern C# features, you could still build a complete game in Unity. I have seen .NET developers come into Unity and complain about missing features, but the reality is that most of the time, those features are not required to get the job done.

That said, since we are already working in C#, it makes sense to be aware of what the language offers today. Many modern features exist to reduce boilerplate, improve readability, and, in some cases, improve performance. You do not need them to build a game, but they can make your code easier to write, easier to maintain, and sometimes more efficient.

Also, if you ever move away from Unity and transition into .NET enterprise development, those skills will transfer.

Are you using C# Properties?

Properties are something I still don’t use as often, and it’s not because I think they are wrong. It’s just a habit I developed over time. If it is something simple like this:

public string SomeProperty {get; private set;}

I use it. But beyond that, I usually don’t go further.

There is a historical reason for that. In Unity, if you want to edit a value in the Inspector, you typically use the [SerializeField] attribute. The problem is that Unity does not serialize properties, so they do not appear in the Inspector. If I wanted to assign a value to SomeProperty through the Inspector, that would not be possible. The field simply would not show up at all.

Because of this, developers often relied on fields instead of properties, which shaped how a lot of Unity code is written even today.

I even wrote an entire tutorial about it here Why You Should Stop Using Public Fields, which goes through that problem.

Newer versions of Unity (2019.x and later), with updated C# support, allow you to use attribute targets like [field: SerializeField], which applies serialization to the backing field of a property. This makes it possible to expose properties in the Inspector, something that was not possible in earlier versions.

[field:SerializeField] public string SomeProperty {get; private set;} = "Darko"

This line will expose SomeProperty in the editor, and you can set the value as you want.

Why do C# Properties exist at all?

The truth is, you can live your life without ever using a C# property. And another thing is that properties are not something we can consider modern. They were introduced by Microsoft in 2000. But they are still not used as often by Unity developers, so I am going to explain their intent and advantages.

They help you reduce boilerplate, but that is not the main reason they exist.

public class Player{
    private int _health = 10;
}

We set health, but it is inaccessible from the outside. Imagine this is your player's health. How would an enemy damage the player? It would not be able to access this field directly.

So the logical solution is to expose it:

public class Player : MonoBehaviour{
    public int Health = 10;
}

Now other objects can access the health. But we introduced a new problem. Any part of the code can now be changed freely.

From the outside, it looks like this:

player.Health = 20;

At first, this might seem fine. The enemy damages the player, something updates the value, everything works. But the problem appears later. What if you need to prevent health from going below 0? How would you enforce that?

Let me show you a simple example:

public class Enemy : MonoBehaviour{
    private void OnAttack(int damage){

        // Only update player health if above 0
        if(player.Health > 0){
             player.Health -= damage;
        }
    }
}

public class ExplosiveBarrel : MonoBehaviour{
    private void OnExplosion(int damage){

        // Only update player health if above 0
        if(player.Health > 0){
             player.Health -= damage;
        }
    }
}

Now we have a problem. Every system that modifies Health has to remember to add this check. If you forget it in just one place, your health can go negative. This leads to bugs, inconsistent behavior, and duplicated logic across your codebase. So instead of relying on every external system to behave correctly, we move that responsibility inside the class itself.

I’ve personally run into this more than once. You set everything up correctly, it works fine, and then later you add a new system or feature and forget that one small check. Suddenly, your health drops to -30, and now your UI starts behaving in weird ways. Your health bar might go in the wrong direction or break completely if it wasn’t designed to handle negative values. It’s one of those bugs that doesn’t happen often, but when it does, it’s annoying to track down.

Do I use Methods or Properties in C# Unity?

Before properties existed, this was solved using methods:

private int _health = 10;

public int GetHealth(){
    return Mathf.Clamp(_health, 0, 100);
}

public void SetHealth(int health){
     if(health > 0){
          _health = health;
     }
}

Now the logic is centralized. No matter who changes the value, it always stays within valid bounds. This is already a much better solution than using public fields. But it comes with a downside: it is verbose and not very natural to use.

player.SetHealth(20);
int health = player.GetHealth();

This is exactly the problem properties were designed to solve. Properties give you the same level of control as methods, but with field-like syntax:

private int _health = 10;

public int Health
{
    get => _health;
    set => _health = Mathf.Clamp(value, 0, 100);
}

Now usage becomes simple again:

player.Health = 20;
int health = player.Health;

But the real advantage is not just reduced boilerplate. The real advantage is control. You can change how Health behaves internally without changing how the rest of your code uses it. From the outside, it still looks like a simple field, but internally, you can enforce rules, validation, or even trigger side effects.

That is something a public field can never give you. So while it might feel like properties are just a shortcut for getters and setters, they are actually a way to write safer, more maintainable code without sacrificing simplicity.

A better Property example

A better real world example is reacting to changes without tightly coupling systems together.

Let’s say we still have gold:

public int Gold = 0;

Now multiple systems might care about this value:

  • UI
  • sound effects
  • achievements
  • analytics

With a field, every system has to manually react:

player.Gold += 10;

// somewhere else
ui.UpdateGold(player.Gold);
audio.PlayCoinSound();
achievementTracker.CheckGold(player.Gold);

This quickly becomes messy and easy to forget.

With a property, we can centralize the moment when the value changes, without coupling it to specific systems:

private int _gold;

public event Action<int> OnGoldChanged;

public int Gold
{
    get => _gold;
    set
    {
        if (_gold == value) return;

        _gold = value;
        OnGoldChanged?.Invoke(_gold);
    }
}

Now other systems can subscribe:

player.OnGoldChanged += ui.UpdateGold;
player.OnGoldChanged += audio.PlayCoinSound;

And usage stays clean:

player.Gold += 10;

This is where properties actually become useful. Not because they save you a few lines of code, but because they give you a single place where change happens. Everything else can react to it without needing to know how or why it changed.

The OnGoldChanged += ui.UpdateGold line above is a C# event, and most Unity devs write events in a way that silently breaks. If you are unsure whether to reach for UnityEvent, Action, or the event keyword here, I covered it in Most Unity devs still don't understand events.

What are C# Tuples and how do I use them in C#?

Same as properties, tuples are not something modern. They are a C# language feature that existed much earlier, and Unity support depends on the Unity/C# runtime version you are using. Most learning materials don't teach them, so I don't see them used as often, but I want to cover them because they come to be useful in specific use cases.

Before we can understand C# Tuple Type, we have to understand why somebody decided to implement them in C#. My biggest problem with Unity Tutorials is that they will shove something at you just because. When I am learning something, I try to understand the history and intent behind it. Because some dude somewhere sat down at his computer, and he concluded that Tuples make sense.

So, as a Unity teacher, I always make sure I understand the problem that he had so that Tuples solved it for him. And then when I am explaining to my students, it makes sense to them as well. If you implement this way of thinking when learning programming, you will become excellent at logical thinking. For a topic to make sense, try to search what programmers had to do before Tuples were introduced into C#. Just that information alone is going to reveal the topical meaning.

What problem do C# Tuples solve?

To understand this, we have to understand the problem developers had to deal with before Tuples were introduced to C#. Let me give you an example.

public Vector3 GetSpawnPoint(Vector3 position){
    return new Vector3(position.x, 0, position.z);
}

The example is just fine, and it will work. But, we got the position, what if we also need the rotation as well? Imagine we want to spawn a gameObject on the spawn point, but the gameObject is not facing the direction we want.

public Vector3 GetSpawnPoint(Vector3 position, Quaternion rotation){
    position = new Vector3(position.x, 0, position.z);
    rotation = Quaternion.Euler(0, 180, 0);

    return position; 
}

We inserted rotation, but we can't return it. In C#, a method can return only a single value. Even though we modify the rotation inside the method, that change is lost because it was passed by value. The method works on a copy, not the original variable.

When do I use the Out keyword in C#?

What if I told you that you can actually return multiple values from a single method? Let me introduce you to the out keyword. For years, this was a perfect solution for such problems, and it is still an okay solution.

This is how it looks in action:

public Vector3 GetSpawnPoint(Vector3 position, out Quaternion rotation){
    position = new Vector3(position.x, 0, position.z);
    rotation = Quaternion.Euler(0, 180, 0);

    return position; 
}

// Call GetSpawnPoint
Quaternion rotation;

Vector3 spawnPosition = GetSpawnPoint(_player.transform.position, out rotation);

When we use the out keyword, the variable is passed by reference instead of by value. This means the method assigns the caller's variable directly rather than working only on a copy. That is why rotation is available after the method call.

Probably you have already used the out keyword before

If you have ever dealt with physics and raycasts before, you probably dealt with the out keyword as well. In those tutorials where you are shown how to detect raycast from the camera, you were told to type out keyword, but you had no idea why.

bool isGroundHit = Physics.Raycast(transform.position, transform.forward, out RaycastHit hit, 100f);

isGroundHit returns a boolean that registers whether raycast has hit the ground or not. But also using the out keyword, you are able to extract the exact location where the hit intersected with the target.

Without the out keyword, there would be no way for the method to give us the RaycastHit data back in this form. The method would only be able to return a single value.

Can I use KeyValuePair<T, T> to return multiple values?

If you have ever worked with Dictionary<TKey, TValue>, you have probably encountered the KeyValuePair type. It allows you to store two values: a key and a value.

KeyValuePair<Vector3, Quaternion> GetSpawnPoint(Vector3 position, Quaternion rotation){
    position = new Vector3(position.x, 0, position.z);
    rotation = Quaternion.Euler(0, 180, 0);
    
    return new KeyValuePair<Vector3, Quaternion>(position, rotation);
}

var spawnPoint = GetSpawnPoint(_player.transform.position, _player.transform.rotation);

While this will technically work, I strongly recommend against using it in this context. KeyValuePair is designed to represent a relationship between a key and a value, typically when working with dictionaries. In our case, we are simply returning two related values, not a key-value relationship, so using KeyValuePair does not make much sense.

What about using Class to return multiple values?

Compared to KeyValuePair, this is a much better solution. In many cases, it is actually the correct and most maintainable approach.

public class SpawnPositionData
{
    public Vector3 position;
    public Quaternion rotation;

    public SpawnPositionData(Vector3 position, Quaternion rotation)
    {
        this.position = new Vector3(position.x, 0, position.z);
        this.rotation = Quaternion.Euler(0, 180, 0);
    }
}

public SpawnPositionData GetSpawnPoint(Vector3 position)
{
    return new SpawnPositionData(position, Quaternion.identity);
}

This is close to "perfect," but I still don’t recommend it for simple cases like this, and there are a few reasons why.

A class is a reference type, which means it is allocated on the heap. This introduces additional memory allocations and puts pressure on the garbage collector if used frequently (for example, in update loops or gameplay systems that run every frame).

However, it is important to be precise here: objects on the heap are not inherently “slow to find,” and using classes is not wrong. In fact, classes are the preferred solution when you are modeling more complex data or behavior.

The real downside in this context is overkill. For something as simple as returning two values, creating a full class adds extra structure, extra allocation, and more code than necessary.

How about Struct type?

Structs look very similar to classes in how they are written, but they behave differently under the hood.

public struct SpawnPositionData
{
    public Vector3 position;
    public Quaternion rotation;

    public SpawnPositionData(Vector3 position, Quaternion rotation)
    {
        this.position = new Vector3(position.x, 0, position.z);
        this.rotation = Quaternion.Euler(0, 180, 0);
    }
}

private SpawnPositionData GetSpawnPoint(){
    return new SpawnPositionData(_player.transform.position, _player.transform.rotation);
}

The main difference is that structs are value types, while classes are reference types. This means structs are typically allocated inline (often on the stack or inside other objects), rather than on the heap like classes.

Because of this, structs can reduce heap allocations and garbage collection pressure, which can be beneficial in performance critical code.

However, it is not entirely accurate to say that structs are always “faster.” They can actually become less efficient if they are large or frequently copied, since every assignment creates a full copy of the data.

For this use case, I would still vote against using a struct. The reason is simple: it is overkill. You have to define a separate type, construct it, and maintain it, just to return two values.

This adds extra steps and increases maintenance overhead for something that is conceptually very simple.

Structs make more sense when you are modeling clear, reusable data structures, for example, something like EnemyData, DamageInfo, or any data that represents a well defined concept in your game.

Why would I use Tuples in C# and Unity?

I demonstrated multiple ways to do it and they are not really the best solutions and here is the reason. For tuples to make sense we need to figure out other ways to do the same thing and only then it will be clear why Tuples make sense.

private (Vector3 position, Quaternion rotation) GetSpawnPoint(Vector3 inputPosition)
{
    Vector3 position = new Vector3(inputPosition.x, 0, inputPosition.z);
    Quaternion rotation = Quaternion.Euler(0, 180, 0);

    return (position, rotation);
}

At first glance, the syntax looks strange, but you get used to it quickly. What tuples give you is the ability to return multiple values without creating new types like we did with KeyValuePair, struct, or class.

private (Vector3 position, Quaternion rotation) 

This part is just the return type. Under the hood, this is a ValueTuple<Vector3, Quaternion>, not the old Tuple<T1, T2> reference type, which means it is lightweight and does not allocate like a class would.

Tuple vs Class vs Struct. Which one is better?

None of them is universally better, it depends on what you are solving. If I just need to return a couple of values that are used immediately, I use a tuple because it is the simplest and cleanest option. If the data actually represents something meaningful in the game, like EnemyData, DamageInfo, or PlayerStats, then a class or struct makes more sense because the data has identity and will likely be reused.

Structs sit somewhere in between. They are useful for small data containers, but in a case like this they still introduce extra setup for something that does not really need it.

If I just need to return a few values quickly, I use a tuple. If the data has meaning beyond that method, I use a class or a struct.

Tuples are not more powerful than other solutions. They just remove friction. They let you return multiple values without introducing extra structure, and that is why they fit so well in cases like this.

Do I use C# LINQ in Unity?

Same as properties and tuples, LINQ is not a modern feature. It was introduced in C# 3.0 back in 2007, and it has been available in Unity for a long time. The reason many Unity developers avoided it is because it used to cause GC spikes, especially in older versions of Unity that relied on an outdated Mono runtime. Because of that, a lot of developers built a habit of avoiding LINQ entirely, and that habit still exists today.

In Unity versions around 2018.x and 2019.x, things improved. Unity introduced the .NET 4.x Equivalent runtime and upgraded its Mono backend. This brought better JIT compilation and improved performance of the standard libraries, including LINQ. Garbage collection also became less punishing, so the small allocations LINQ creates became less noticeable in many cases.

It is important to understand that LINQ itself did not suddenly become faster. What changed was the environment around it. The runtime and garbage collector improved, so LINQ became more usable.

I still do not use LINQ in performance critical code like Update loops or systems that run every frame because it introduces overhead and hides what is happening under the hood. Outside of that, it is completely fine to use. If I need control, I write loops. If I need speed of writing, I use LINQ.

Let's leave talk for later and let's do some benchmarking

I am going to do two types of tests, One with C# loops and other with using LINQ. So we want to compare their performance and see why Unity developers are not using LINQ.

I created a text file with 646 American names and I am going to test LINQ and Loops against them. In the first test I will run a loop that filters and counts names in the list. Also, I did tests in Unity version 2022.3 and 6.3. The goal is to compare if LINQ has improved through Unity updates.

Let's begin.
I first ran a starts-with count benchmark and then three sorting benchmarks: a manual insertion sort, LINQ OrderBy, and built-in List.Sort.

How fast is counting names that start with a letter? (LINQ vs loop)

public static int LoopStartsWithCount(List<string> data, char c)
{
    int count = 0;
    for (int i = 0; i < data.Count; i++)
    {
        if (data[i][0] == c)
        {
            count++;
        }
    }
    return count;
}
  • Unity 2022: 3.617 ms / 1000 runs (0.003617 ms each)
  • Unity 6.3: 3.534 ms / 1000 runs (0.003534 ms each)
public static int LinqStartsWithCount(List<string> data, char c)
{
    return data.Where(n => n[0] == c).Count();
}
  • Unity 2022: 7.884 ms / 1000 runs (0.007884 ms each)
  • Unity 6.3: 7.776 ms / 1000 runs (0.007776 ms each)

The loop is roughly twice as fast. This is expected. The loop is direct and does exactly one thing. LINQ introduces a predicate, creates an iterator, and then evaluates it, which adds extra work even for a simple operation.

Which is faster for sorting names: LINQ OrderBy or a pure loop sort?


public static List<string> LoopInsertionSort(List<string> data)
{
    var result = new List<string>(data);
    var comparer = StringComparer.Ordinal;
    for (int i = 1; i < result.Count; i++)
    {
        string current = result[i];
        int j = i - 1;
        while (j >= 0 && comparer.Compare(result[j], current) > 0)
        {
            result[j + 1] = result[j];
            j--;
        }
        result[j + 1] = current;
    }
    return result;
}
  • Unity 2022: 2060.221 ms / 1000 runs (2.060221 ms each)
  • Unity 6.3: 2088.157 ms / 1000 runs (2.088157 ms each)
public static List<string> LinqSort(List<string> data)
{
    return data.OrderBy(n => n, StringComparer.Ordinal).ToList();
}
  • Unity 2022: 244.639 ms / 1000 runs (0.244639 ms each)
  • Unity 6.3: 233.270 ms / 1000 runs (0.233270 ms each)

At first glance, LINQ looks much faster, but this is not a fair comparison. The loop version uses insertion sort, which is not an efficient algorithm for this kind of data. LINQ uses a much more optimized sorting algorithm internally, which is why it wins here.

But there is also an optimized built-in option, List.Sort, which avoids LINQ and uses a highly optimized library implementation.

public static List<string> LoopBuiltInSort(List<string> data)
{
    var result = new List<string>(data);
    result.Sort(StringComparer.Ordinal);
    return result;
}
  • Unity 2022: 128.232 ms / 1000 runs (0.128232 ms each)
  • Unity 6.3: 121.891 ms / 1000 runs (0.121891 ms each)

Once again, loop sorting beats LINQ. But this needs context. List.Sort works in place and avoids extra allocations, while LINQ’s OrderBy creates intermediate structures and then materializes the result with ToList(). That adds overhead.

What do these results actually mean?

This is not just LINQ vs loops. You are comparing different levels of abstraction.

  • Manual loops give you full control and minimal overhead
  • Built-in methods are highly optimized and usually the fastest option
  • LINQ adds abstraction, which makes code cleaner but slightly less efficient

The counting test shows the raw cost of LINQ. The sorting test shows that algorithm choice matters more than syntax.

Another important observation is that there is almost no difference between Unity 2022 and 6.3. That confirms that LINQ itself did not significantly improve in performance. The runtime improvements made it more stable and predictable, but not fundamentally faster.

What are C# Records and when do I use them in Unity?

Records were introduced in C# 9 in 2020. Unity support came later and depends on the Unity version, runtime profile, and C# language support in that version. In practice, records started becoming much more realistic for day to day Unity code as Unity moved further into its .NET modernization path.

A record is a data focused type with built in value based equality. The default record is a reference type, and record struct exists when you want value type behavior. Compared to a classic class, records reduce boilerplate for models where the data shape matters more than mutable behavior.

What problem do C# records solve?

In my game Skeletons AR, I had an EnemyData type for skeleton enemies. At first it looked simple, health, speed, damage, reward gold. Then I needed small variations for each wave. One version for basic skeletons, one for fast skeletons, one for tank skeletons.

With a normal class, this became repetitive fast. I kept writing constructors and copy logic just to make small changes safely.

public class EnemyData
{
    public int Health;
    public float Speed;
    public int Damage;
    public int RewardGold;

    public EnemyData(int health, float speed, int damage, int rewardGold)
    {
        Health = health;
        Speed = speed;
        Damage = damage;
        RewardGold = rewardGold;
    }
}

var basic = new EnemyData(100, 2.5f, 10, 5);
var fast = new EnemyData(basic.Health, 3.4f, basic.Damage, basic.RewardGold);

Then I switched to a record. The same setup became cleaner and easier to scale.

public record EnemyData(int Health, float Speed, int Damage, int RewardGold);

var basic = new EnemyData(100, 2.5f, 10, 5);
var fast = basic with { Speed = 3.4f };
var tank = basic with { Health = 180, Speed = 1.9f, RewardGold = 8 };

That is the real problem records solved for me in Skeletons AR. I stopped writing repetitive setup code for enemy variants and focused on tuning gameplay.

Records vs Class vs Struct

None of these is universally better, it depends on what your data is doing in the project. I use classes when an object has identity and mutable behavior over time, like a live enemy object that keeps changing during gameplay. I use structs for small value data where copying is expected and useful, but I avoid large structs because frequent copies can get expensive. I use records when a type is mostly data and I want clean equality plus quick variation creation. In Skeletons AR, EnemyData fit that pattern, so records gave me the cleanest workflow for creating and comparing skeleton variants without extra boilerplate.

Should you use Records in Unity?

Yes, you should use records in Unity when they match the job. If your type is mostly data and you keep creating variations from a base setup, records are a strong option because they keep the code clean and predictable. I still would not force them everywhere. For hot gameplay objects with identity and a lot of mutable state, classes are usually the better tool. My practical rule is simple, records for data models you copy and compare often, classes for objects that live and mutate through gameplay.


Read next
Most Unity devs still don't understand events
10 Mistakes I Made Learning Unity Alone (Cost Me Years)
Is It Worth Learning Unity in 2026?
What is Unity DOTS? Is Unity DOTS worth learning in 2026?