GDKB

Classes

Intermediate20 Minutes readUpdated July 21, 20261 related concept

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.

On this page

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 every Enemy object 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 (this in C#/C++), which is how TakeDamage() knows whose health to 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. private fields 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/set pair that looks like a field from the outside but can run logic on read or write — commonly used to expose a private field 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)

text
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)

cpp
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 public removes 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 — Enemy the class has no health; a particular Enemy object does. "The Enemy class'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 Health field defaulting to 0 may 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 private fields; 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 static for state or behavior that is genuinely shared across every instance, not as a shortcut to avoid passing a reference.

Exercises

  1. 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.
  2. Write a class with two constructor overloads representing two valid ways to create the object, and explain what each guarantees.
  3. Explain, in your own words, the difference between "the Enemy class" and "an Enemy object," using the blueprint/house analogy.
  4. Identify a static field in a sample class that should actually be an instance field, and explain the bug that sharing it across instances would cause.
  • Variables and Functions — a class's fields and methods, respectively.
  • Objects — the concrete instances a class describes how to build.

References

Related Concepts