Introduction
Until now, we have lived in a world where we used tools built by other people—like Console, Random, or String. But in game development, the “standard tools” aren’t enough. C# doesn’t come with a built-in “Player” or “Enemy” or “Inventory.” You have to build them. Creating your own class is like moving from playing with a finished LEGO set to having a 3D printer. You are no longer restricted by what C# gives you; you can now define exactly what a “Player” is, what they remember (Data), and what they can do (Actions).
Think of creating a class as drawing a Blueprint
- The class is the paper drawing of the house, It doesn’t take up space in the real world yet.
- The Object is the actual house built from that drawing. Once you have the blueprint, you can build 1,000 identical houses (Players) just by using the
newkeyword.
Our First Project: The Player Class
In this tutorial, we are going to build a Player class from scratch. This isn’t just a coding exercise—this is the foundation of every game you will ever make. Whether you are building a simple text adventure today or a massive 3D world in MonoGame tomorrow, the logic is the same:
- Define the Data: What does a player have? (Health, Name, Score).
- Define the Actions: What can a player do? (Jump, TakeDamage, LevelUp).
In C#, a Class is a blueprint. An Object (or Instance) is the actual house built from that blueprint. To build a complete class, we need three things: Fields, Methods, and Constructors.
1. Defining the Barebone class
first, we define the “Neighborhood” and the “House.” Remember: Class definitions go at the bottom of your file, outside of the Main method.
// Top of file: Using statements
using System;
// Layer 1: The Namespace
namespace GameProject
{
// Layer 2: The Logic (Main)
class Program
{
static void Main(string[] args)
{
// This is where we will use our Player later
}
}
// Layer 3: THE CUSTOM CLASS (The Blueprint)
class Player
{
// This is currently an empty blueprint.
}
}
2. Defining the Barebone class Fields are variables that live inside the class. They represent what the object remembers.
- Public: This means other parts of your program are allowed to see these variables.
class Player
{
public string name; // Automatically starts as null (nothing)
public int points; // Automatically starts as 0
public int livesLeft; // Automatically starts as 0
}
3. Adding Methods ( The Actions)
Methods represent what the object can do. Notice there is no static here. This is because these actions belong to one specific player, not the whole class.
public void AddPoints(int amount)
{
// Logic: If points cross a 1000 threshold, gain a life
int previousThousand = points / 1000;
points += amount;
int newThousand = points / 1000;
if (newThousand > previousThousand)
{
livesLeft++;
}
}
public void Kill()
{
livesLeft--;
if (livesLeft == 0) { points = 0; }
}
4. The Constructor
A Constructor is a special method that runs only once, the very moment you use the new keyword. Its job is to put the object into a “Good Starting State
Rules for Constructors:
- It must have the exact same name as the Class.
- It has no return type (not even
void).
// This constructor REQUIRES a name when you create a player
public Player(string name_)
{
name = name_; // don'w worry i'll teach you why i put underscore there.
points = 0;
livesLeft = 5;
}
As soon as you write any custom constructor (like public Player(string name)), C# assumes you want to take full control over how your objects are born. It says, “Okay, since you wrote your own rules, I will stop providing my free default one.”
class Player { }
// In Main:
Player p = new Player(); // Works! (Uses the hidden default)
class Player
{
public string Name { get; set; }
public Player(string name) { Name = name; }
}
// In Main:
Player p = new Player("Hero"); // Works!
Player p2 = new Player(); // ERROR! The default no longer exists.
how to bring back default constructor
class Player
{
public string Name { get; set; }
// 1. We manually add the default one back
public Player() { }
// 2. We keep our custom one
public Player(string name)
{
Name = name;
}
}
**Putting it all together ( The Final Code)
static void Main(string[] args)
{
// 1. Create instances (The Constructor runs here!)
Player player1 = new Player("HAL 9000");
Player player2 = new Player("Tron");
// 2. Use the Methods (Actions)
player1.AddPoints(1500); // HAL earns 1500 points and gets an extra life!
player2.Kill(); // Tron loses a life
// 3. Check the Fields (Data)
Console.WriteLine($"{player1._name} has {player1._livesLeft} lives.");
Console.WriteLine($"{player2._name} has {player2._livesLeft} lives.");
}
Access Modifier Public vs private
In C#, access modifiers control who is allowed to see or touch the code inside a class. Think of it as the security clearance for your software.
The Definitions
public: Anyone can see it. Anyone can change it. (Like a park bench).private: Only code inside the same class can see or change it. (Like your toothbrush).
The Danger of “Public Everything”
If you fields (data) are public the outside world can bypass your rules
The Problem:
Imagine our AddPoints method has a rule: “Every 1000 points = +1 Life”
if the field points is public, a student could write this in Main:
player1.points += 5000; // direct chagne!
The Result: The player gets 5000 points, but zero extra lives. The “logic” we wrote in the method was completely ignored because someone “reached inside” the player and changed the numbers manually.
Encapsulation the vending machine strategy We want our objects to be like vending machines
- You can’t reach inside and grab a soda (private data).
- You must press a button or insert coins ( Public methods).
- The machine then decides if you followed the rules before giving you the soda.
Hide The Data
Change your fields to private.
class Player
{
private string name;
private int points;
private int livesLeft;
}
Provide “Getter”
since the outside world can no longer see name, we provide a public method to “get” the value. This is called a Getter.
public string GetName()
{
return name;
}
public int GetPoints()
{
return points;
}
The downside is that before we could do this:
Console.WriteLine(player1._name + " has " + player1._points + " total points.");
Now, we need to do this:
Console.WriteLine(player1.GetName() + " has " + player1.GetPoints() + " total points.");
Provide “Setters”
Now that our fields are private, the outside world can’t change them directly. We provide a Setter method to allow changes only if they follow our rules.
class Player
{
private int points; // Private (Hidden)
// The Setter: Allows the outside world to change _points
public void SetPoints(int newAmount)
{
points = newAmount;
}
}
class Program
{
static void Main()
{
Player player1 = new Player();
player1.SetPoints(100);
Console.WriteLine("Player Score: " + player1.GetPoints());
}
}
Static Methods, Fields, and Classes
It’s totally normal for this to feel like “Code Overload.” We just learned that objects are independent, and now I’m telling you they can also share things!
Think of Static as the “Global Knowledge” of the class. It’s the difference between what one player knows (their own health) and what the entire game world knows (the gravity or the rules).
The static Keyword: Shared Knowledge
By default, every player you create with new Player() gets their own separate “backpack” of data. But sometimes, it’s wasteful for everyone to carry the same thing.
Static Fields (Shared Data)
Imagine every player needs to know that it takes 1000 points to get an extra life.
- Without Static: 1,000 players each carry the number “1000” in their memory. (Wasteful!)
- With Static: There is only one copy of that number, kept at the “Main Office” (the Class)
using System;
namespace StaticTest
{
class Program
{
static void Main(string[] args)
{
// 1. Create the first player
Player p1 = new Player("Master Chief");
Console.WriteLine($"Created {p1.GetName()}. Total Players: {Player.Count}");
// 2. Create a second player
Player p2 = new Player("Arbiter");
Console.WriteLine($"Created {p2.GetName()}. Total Players: {Player.Count}");
// 3. The "Aha!" Moment:
// Even though we never touched 'p1' again, 'p1' sees the change!
Console.WriteLine($"Does p1 know there are 2 players? Yes, Count is: {Player.Count}");
}
}
class Player
{
private string name;
// STATIC: This lives in the "Class Office," not in the player's backpack.
public static int Count = 0;
public Player(string name)
{
name = name;
// Every time 'new' is called, we increment the SHARED count.
Count++;
}
public string GetName() { return name; }
}
}
Static Methods (Tools, not Actions)
A static method is like a Calculator sitting on a desk. You don’t need to “build a human” to use a calculator. You just walk up and use it.
The Math class in C# is a Static Class. You never use new Math(). You simply ask the “Class” to perform a calculation for you.
using System;
class Program
{
static void Main()
{
double radius = 5.0;
// We call the static method 'Pow' and the static field 'PI'
// directly from the Math class.
double area = Math.PI * Math.Pow(radius, 2);
Console.WriteLine($"Area of the circle: {area}");
}
}
Why this is static: You don’t need a “Physical Math Object” to calculate a power or a square root. The rules of math are universal—they are the same for everyone, so they are static.
User-Defined Static Method ( The “Unit converter”)
Let’s build a custom class called Converter. This will show how we can change a value in one call, and it doesn’t require us to create an object.
using System;
namespace GameProject
{
class Program
{
static void Main()
{
float fahrenheit = 98.6f;
// We call our custom static method WITHOUT 'new Converter()'
float celsius = Converter.ToCelsius(fahrenheit);
Console.WriteLine($"{fahrenheit}F is {celsius}C");
// Proof of Shared Knowledge:
Console.WriteLine($"Total conversions done: {Converter.ConversionCount}");
}
}
class Converter
{
// Static Field to track usage across the whole app
public static int ConversionCount = 0;
// Static Method: It takes an input, does work, and returns a result.
// It doesn't care about "Instance Data."
public static float ToCelsius(float fahrenheit)
{
ConversionCount++; // Update the shared counter
return (fahrenheit - 32) * 5 / 9;
}
}
}
Static Classes
Sometimes, you don’t want to create objects at all. You just want a container for a bunch of tools. This is a Static Class.
- You cannot use the
newkeyword with a static class. - Everything inside it must be static.
using System;
namespace SimpleStatic
{
class Program
{
static void Main()
{
// 1. Change a shared rule directly
Alert.CompanyName = "Cherrybox Studios";
// 2. Use a static tool (No 'new' keyword!)
Alert.Show("Game Started!");
Alert.Show("Player Joined.");
// 3. Check the counter
Console.WriteLine("Messages sent: " + Alert.MessageCount);
}
}
// --- THE STATIC CLASS (The Toolbox) ---
static class Alert
{
public static string CompanyName;
public static int MessageCount = 0;
public static void Show(string text)
{
MessageCount++;
Console.WriteLine($"[{CompanyName}] {text}");
}
}
}
This Keyword
When a method parameter has the exact same name as a class field, the computer gets “tunnel vision.” It only sees the parameter and ignores the field.
Broken Code
class Player
{
private int health; // The FIELD (The object's actual health)
public void SetHealth(int health) // The PARAMETER (The new value)
{
// ISSUE: Both sides of the '=' refer to the PARAMETER.
// It's like saying "5 = 5".
// The actual field 'health' upstairs is never updated!
health = health;
}
}
The Result: If you call player1.SetHealth(80);, the player’s health will still be 0. The data never made it into the object’s memory.
The Fix: Using this
We use this to break the tie. It tells the computer: “Look outside this method and find the variable that belongs to me (the object).”
class Player
{
private int health; // The FIELD
public void SetHealth(int health) // The PARAMETER
{
// FIX: 'this.health' points to the FIELD.
// 'health' points to the PARAMETER.
this.health = health;
Console.WriteLine("My health is now updated to: " + this.health);
}
}
-
thisis an Address: It points to the specific “House” (Object) currently in use. -
thisis for Clarity: You only need it when the computer might get confused between a “Field” and a “Parameter.” -
thisis Internal: You only use it inside the{ }of the class. You never use it inMain.
Object-Oriented Programming is a massive city, and we have only explored the first few blocks. While we’ve mastered how objects are born and how they live in memory, there is still much more to discover ” i’ll teach it later”.