Skip to main content

5.1 - Introduction to Object-Oriented Programming (OOP)

Overview

Object-Oriented Programming (OOP) is a programming paradigm that organizes code around "objects" rather than functions and logic. It's one of the most powerful and widely used approaches in modern software development, and it forms the foundation of Unity's architecture. Understanding OOP is crucial for becoming an effective Unity developer.

What is Object-Oriented Programming?

Object-Oriented Programming is a way of designing and structuring code that models real-world entities as "objects." These objects contain both:

  1. Data (attributes or properties)
  2. Behaviors (methods or functions)

For example, in a game, you might have a Player object that contains:

  • Data: health, position, inventory
  • Behaviors: move, jump, attack, take damage

This approach makes code more intuitive, modular, and easier to maintain as your projects grow in complexity.

The Four Pillars of OOP

Object-Oriented Programming is built on four fundamental principles:

1. Encapsulation

Encapsulation is the bundling of data and methods that operate on that data within a single unit (the class), and restricting access to some of the object's components.

Key benefits:

  • Protects data from accidental modification
  • Hides implementation details
  • Creates a clear, controlled interface for interacting with objects

Example:

public class Player
{
// Private field (encapsulated data)
private int health;

// Public property (controlled access)
public int Health
{
get { return health; }
set
{
// Validate before changing
if (value >= 0 && value <= 100)
health = value;
}
}
}

In this example, the health field is protected from direct access, and the Health property provides controlled access with validation.

2. Abstraction

Abstraction means hiding complex implementation details and showing only the necessary features of an object.

Key benefits:

  • Simplifies usage
  • Reduces complexity
  • Focuses on what an object does rather than how it does it

Example:

public class Car
{
// Complex internal systems are hidden
private Engine engine;
private Transmission transmission;

// Simple interface for users
public void Start()
{
engine.Ignite();
transmission.SetGear(1);
}

public void Accelerate()
{
engine.IncreasePower();
if (engine.RPM > 3000)
transmission.ShiftUp();
}
}

Users of the Car class don't need to understand how the engine or transmission works; they just need to know they can call Start() and Accelerate().

3. Inheritance

Inheritance allows a class to inherit properties and methods from another class, establishing an "is-a" relationship.

Key benefits:

  • Promotes code reuse
  • Creates hierarchical relationships
  • Allows for specialized implementations

Example:

// Base class
public class Enemy
{
public int Health { get; set; }
public int Damage { get; set; }

public virtual void Attack()
{
Console.WriteLine("Enemy attacks for " + Damage + " damage!");
}
}

// Derived class
public class Goblin : Enemy
{
public Goblin()
{
Health = 50;
Damage = 5;
}

public override void Attack()
{
Console.WriteLine("Goblin slashes for " + Damage + " damage!");
}
}

// Another derived class
public class Dragon : Enemy
{
public Dragon()
{
Health = 500;
Damage = 50;
}

public override void Attack()
{
Console.WriteLine("Dragon breathes fire for " + Damage + " damage!");
}

public void Fly()
{
Console.WriteLine("Dragon soars through the air!");
}
}

Both Goblin and Dragon inherit from Enemy, gaining its properties and methods, while also adding their own specialized behaviors.

4. Polymorphism

Polymorphism means "many forms" and allows objects of different classes to be treated as objects of a common base class.

Key benefits:

  • Enables flexible and generic programming
  • Allows for method overriding
  • Supports dynamic behavior at runtime

Example:

public class GameManager
{
public void ProcessEnemyTurn(Enemy enemy)
{
// This works for ANY type of enemy!
enemy.Attack();
}
}

// Usage
GameManager gameManager = new GameManager();
Enemy goblin = new Goblin();
Enemy dragon = new Dragon();

gameManager.ProcessEnemyTurn(goblin); // Outputs: "Goblin slashes for 5 damage!"
gameManager.ProcessEnemyTurn(dragon); // Outputs: "Dragon breathes fire for 50 damage!"

The ProcessEnemyTurn method can work with any type of Enemy, and the correct version of Attack() is called based on the actual object type.

Classes and Objects: The Building Blocks of OOP

Classes

A class is a blueprint or template that defines the structure and behavior of objects. It specifies:

  • What data the object will contain (fields and properties)
  • What operations the object can perform (methods)
  • How the object will interact with other objects

Think of a class as a cookie cutter, and objects as the cookies it creates.

Objects

An object is an instance of a class—a concrete entity created from the class blueprint. When you create an object, you're allocating memory for a specific instance of that class.

Example:

// Class definition (blueprint)
public class Weapon
{
public string Name { get; set; }
public int Damage { get; set; }

public void Attack()
{
Console.WriteLine($"Attacking with {Name} for {Damage} damage!");
}
}

// Creating objects (instances)
Weapon sword = new Weapon();
sword.Name = "Steel Sword";
sword.Damage = 10;

Weapon bow = new Weapon();
bow.Name = "Longbow";
bow.Damage = 8;

// Using the objects
sword.Attack(); // Outputs: "Attacking with Steel Sword for 10 damage!"
bow.Attack(); // Outputs: "Attacking with Longbow for 8 damage!"

OOP in Unity

Unity's entire architecture is built on Object-Oriented Programming principles:

  1. GameObject-Component System: Unity uses a component-based architecture where GameObjects are containers that can have multiple Components attached to them.

  2. MonoBehaviour: This is the base class for all Unity scripts. When you create a script in Unity, you're creating a new class that inherits from MonoBehaviour.

  3. Inheritance Hierarchy: Unity has a rich inheritance hierarchy. For example:

    • Object is the base class for all Unity objects
    • Component inherits from Object
    • Behaviour inherits from Component
    • MonoBehaviour inherits from Behaviour
  4. Polymorphism in Action: Unity uses polymorphism extensively. For example, the GetComponent<T>() method can return any component type.

Example of a Unity script:

using UnityEngine;

// This class inherits from MonoBehaviour
public class PlayerController : MonoBehaviour
{
// Data (fields and properties)
public float moveSpeed = 5f;
private int health = 100;

// Behaviors (methods)
void Update()
{
// Move the player based on input
float horizontalInput = Input.GetAxis("Horizontal");
float verticalInput = Input.GetAxis("Vertical");

Vector3 movement = new Vector3(horizontalInput, 0f, verticalInput);
transform.Translate(movement * moveSpeed * Time.deltaTime);
}

public void TakeDamage(int amount)
{
health -= amount;

if (health <= 0)
{
Die();
}
}

private void Die()
{
Debug.Log("Player has died!");
// Disable the player GameObject
gameObject.SetActive(false);
}
}

In this Unity script:

  • Encapsulation: The health field is private, and TakeDamage() provides controlled access to modify it.
  • Abstraction: The script provides a simple interface (TakeDamage()) without exposing implementation details.
  • Inheritance: The class inherits from MonoBehaviour, gaining all its functionality.
  • Polymorphism: Unity's event methods like Update() are overridden to provide custom behavior.

Why OOP Matters for Game Development

Object-Oriented Programming is particularly well-suited for game development because:

  1. Games are naturally object-oriented: Games contain many distinct entities (players, enemies, items, etc.) that have both state and behavior.

  2. Modularity: OOP allows you to create reusable components that can be combined in different ways.

  3. Maintainability: As games grow in complexity, OOP helps manage that complexity by organizing code into logical units.

  4. Collaboration: OOP makes it easier for teams to work together, as different programmers can work on different classes.

  5. Extensibility: New features can be added by creating new classes or extending existing ones, without modifying working code.

Conclusion

Object-Oriented Programming is a powerful paradigm that forms the foundation of modern game development in Unity. By understanding and applying the four pillars of OOP—encapsulation, abstraction, inheritance, and polymorphism—you'll be able to create more organized, maintainable, and scalable code.

In the next sections, we'll dive deeper into each aspect of OOP in C#, starting with classes and objects.

Practice Exercise

Exercise: Think about a simple game with a player character and different types of collectible items (coins, health packs, power-ups). How would you structure these using OOP principles? Try to identify:

  1. What classes would you create?
  2. What properties and methods would each class have?
  3. How would inheritance and polymorphism be useful in this scenario?

Write down your ideas, and we'll explore similar concepts in more detail in the upcoming sections.