Classes
Summary
A class is a blueprint that bundles data (fields) and the functions that
operate on that data (methods) into one named unit. It describes what
shape something has and what it can do — Enemy might have health
and position fields and a TakeDamage method — without yet being any
particular enemy. Classes are how object-oriented code organizes state
and behavior together, instead of leaving related data and functions
scattered and only loosely connected.
Mental Model
A class is an architect's blueprint for a house: it specifies "every house built from this plan has a front door, N bedrooms, and a kitchen," but a blueprint is not itself a house you can live in. Building an actual house from the blueprint is instantiation — creating an object (see Objects), a concrete instance with its own front door and its own kitchen, distinct from any other house built from the same blueprint.
Every field the class declares becomes a variable that exists once per
object — two Enemy objects each have their own health, independent
of each other, even though both were built from the identical Enemy
blueprint. Every method the class declares is shared code, written once,
that any object built from the class can call — but each call operates on
one particular object's own fields.
Core Explanation
- Fields are the class's member variables (see Variables) — the data
each object carries.
public int maxHealth;declares a field everyEnemyobject will have its own copy of. - Methods are the class's member functions (see Functions) — the
operations defined on that data. A method has implicit access to the
particular object it's called on (
thisin C#/C++), which is howTakeDamage()knows whosehealthto reduce without taking the object as an explicit parameter. - Constructors are special methods that run when an object is first created, responsible for putting the new object into a valid starting state — assigning initial values to fields, given whatever the caller supplied. A class can declare more than one constructor (overloading, see Functions) to support different ways of creating a valid object.
- Access modifiers (
public,private,protected) control which code outside the class can see a field or method.privatefields are visible only inside the class's own methods — the standard way to protect an invariant (see Practical Example) by forcing all changes through methods the class controls, rather than letting arbitrary outside code set fields directly to any value. - Properties (C#) wrap a field behind a
get/setpair that looks like a field from the outside but can run logic on read or write — commonly used to expose aprivatefield for reading while keeping writes restricted or validated. - Static members belong to the class itself, not to any one object —
one shared copy exists regardless of how many objects the class has.
A static field is a genuinely global piece of state scoped to the class
(with all the trade-offs global state carries); a static method can be
called without any object at all (
Mathf.Clamp(...)).
Classes are, formally, a user-defined data type (see Data Types) — declaring a class introduces a new type the rest of the program can then declare variables, parameters, and return values of.
Syntax / Examples
C# (Unity)
public class Enemy
{
public int MaxHealth = 100;
private int currentHealth;
// Constructor
public Enemy(int maxHealth)
{
MaxHealth = maxHealth;
currentHealth = maxHealth;
}
// Property: public read, private write
public int CurrentHealth
{
get => currentHealth;
private set => currentHealth = Mathf.Clamp(value, 0, MaxHealth);
}
public void TakeDamage(int amount)
{
CurrentHealth -= amount; // goes through the property, so it stays clamped
}
}
C++ (Unreal)
UCLASS()
class AEnemy : public AActor
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere)
int32 MaxHealth = 100;
void TakeDamage(int32 Amount)
{
CurrentHealth = FMath::Clamp(CurrentHealth - Amount, 0, MaxHealth);
}
int32 GetCurrentHealth() const { return CurrentHealth; }
private:
int32 CurrentHealth = 100;
};
Unreal Blueprint
A Blueprint is a class — created as an asset (e.g. BP_Enemy), its
variables are fields and its custom Functions/Event Graph logic are
methods. Editable variables map to public fields exposed for
per-instance configuration; variables left non-editable behave like
private fields, changeable only from the Blueprint's own graph.
Practical Example
CurrentHealth in the C# example above is private, only changeable
through TakeDamage() (or the property's own clamped setter) —
enemy.CurrentHealth = 99999; from outside the class is not possible.
This is the class enforcing its own invariant ("health is always between
0 and MaxHealth") structurally, rather than trusting every piece of
calling code to remember to clamp it correctly every time.
Common Mistakes
- Making every field
publicremoves the class's ability to protect its own invariants — any outside code can set a field to an invalid value, and every one of those call sites becomes a place the bug could originate. - Classes that do too much ("God classes") — a class whose fields and methods span several unrelated responsibilities is hard to reuse and hard to reason about; the same "one responsibility" pressure from Functions applies at the class level too.
- Confusing the class with an object —
Enemythe class has nohealth; a particularEnemyobject does. "TheEnemyclass's health" is a category error the same way "the blueprint's front door" is. - Forgetting a constructor leaves fields at their type's default, not
necessarily a valid value for the object — a
Healthfield defaulting to0may need an explicit constructor to set it to something usable. - Static fields used for state that should be per-object — a static
field is shared by every instance, so using one for something that
should differ per object (like
health) means every object silently shares the same value.
Best Practices
- Default to
privatefields; expose only what outside code genuinely needs, and prefer a method or property over a raw public field when an invariant needs protecting. - Keep a class's responsibility narrow enough to describe in one sentence without "and."
- Give every class a constructor that leaves new objects in a valid state — never rely on a caller remembering to call a separate "setup" method.
- Reserve
staticfor state or behavior that is genuinely shared across every instance, not as a shortcut to avoid passing a reference.
Exercises
- Take a class with all-public fields and refactor it so the fields
enforcing an invariant (e.g. a clamped range) become
private, exposed through a method or property. - Write a class with two constructor overloads representing two valid ways to create the object, and explain what each guarantees.
- Explain, in your own words, the difference between "the
Enemyclass" and "anEnemyobject," using the blueprint/house analogy. - Identify a
staticfield in a sample class that should actually be an instance field, and explain the bug that sharing it across instances would cause.
Related Concepts
- Variables and Functions — a class's fields and methods, respectively.
- Objects — the concrete instances a class describes how to build.
References
KNOWLEDGE_SCHEMA.md— GDKB's concept maturity ladder and cross-linking rules.- C# Language Reference — Classes: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/classes
- Unreal Engine — Gameplay Classes: https://dev.epicgames.com/documentation/en-us/unreal-engine/gameplay-classes-in-unreal-engine