C# & Unity Crash Course

C# & Unity Crash Course

Advanced Features Primer

CSCI 3213 - Game Programming

Spring '26 - Week 1, Class 2

Building your foundation for design patterns

Today's Content

📚 Based on Chapter 3

A Short Primer to Programming in Unity

From: Game Development Patterns with Unity 2021 (2nd Edition)
By David Baron

⚠️ Important Note: This is a crash course - we're covering advanced topics quickly. The goal is awareness and familiarity, not complete mastery. If you need deeper understanding, refer to Baron's book (available at Dulaney Browne Library) and learn.unity.com resources.

Today's Learning Objectives

C# Advanced Features

  • Static members
  • Events & Delegates
  • Generics
  • Serialization

Unity Engine Features

  • Prefabs
  • Unity Events
  • ScriptableObjects
  • Coroutines
For Advanced Students: If you're already comfortable with these concepts, use this time to help your peers or explore Baron's book for deeper examples.

Quick Review: What You Should Know

C# Fundamentals

  • Access modifiers (public, private)
  • Primitive types (int, string, bool, float)
  • Arrays and collections
  • Inheritance (base/derived classes)

Unity Basics

  • MonoBehaviour scripts
  • Creating/manipulating GameObjects
  • Awake(), Start(), Update()
  • Component-based architecture
Not comfortable with these? Please see me after class or during office hours. Check out Unity's "Junior Programmer" pathway at learn.unity.com for a refresher.

C# Advanced Feature: Static

What is Static?

Methods and members with the static keyword can be accessed directly by class name without creating an instance.

Why Use Static?

  • ✓ Globally accessible from anywhere
  • ✓ Shared across all instances
  • ✓ Perfect for managers and utilities
  • ✓ No need to find or reference objects

Static Example: Event Bus

using UnityEngine.Events; using System.Collections.Generic; namespace Chapter.EventBus { public class RaceEventBus { // Static dictionary - shared globally private static readonly IDictionary<RaceEventType, UnityEvent> Events = new Dictionary<RaceEventType, UnityEvent>(); // Static method - call without instance public static void Subscribe( RaceEventType eventType, UnityAction listener) { UnityEvent thisEvent; if (Events.TryGetValue(eventType, out thisEvent)) { thisEvent.AddListener(listener); } else { thisEvent = new UnityEvent(); thisEvent.AddListener(listener); Events.Add(eventType, thisEvent); } } } }

Using Static Members

// Call static method directly - no instance needed! RaceEventBus.Subscribe(RaceEventType.START, OnRaceStart); // vs. Non-static (would need instance) RaceEventBus bus = new RaceEventBus(); bus.Subscribe(RaceEventType.START, OnRaceStart);

✅ Good Use Cases

  • Game managers
  • Utility functions
  • Global event systems
  • Configuration data

❌ Avoid For

  • Instance-specific data
  • Multiple object types
  • Testability concerns
  • Everything (don't overuse!)

C# Advanced Feature: Events

What are Events?

Events allow a publisher object to send signals that subscriber objects can receive. Perfect for decoupled, event-driven architectures.

Publisher

Declares and invokes the event

Subscriber

Listens for and responds to events

Why Events? Decoupling! Publishers don't need to know who's listening.

Events Example: Publisher

using UnityEngine; using System.Collections; public class CountdownTimer : MonoBehaviour { // Declare delegate types public delegate void TimerStarted(); public delegate void TimerEnded(); // Declare events using those delegates public static event TimerStarted OnTimerStarted; public static event TimerEnded OnTimerEnded; [SerializeField] private float duration = 5.0f; void Start() { StartCoroutine(StartCountdown()); } private IEnumerator StartCountdown() { // Invoke event (notify subscribers) if (OnTimerStarted != null) OnTimerStarted(); while (duration > 0) { yield return new WaitForSeconds(1f); duration--; } if (OnTimerEnded != null) OnTimerEnded(); } }

C# Advanced Feature: Delegates

What are Delegates?

Delegates hold references to functions. They're function pointers that store memory addresses of methods. Think of them as an address book of function locations.

Key Concepts

  • Function pointers: Store memory addresses to methods
  • Multicast: Can hold multiple function references
  • Invoke all: Call all subscribed functions at once
  • Type-safe: Enforces matching signatures

Delegates Example: Subscriber

using UnityEngine; public class Buzzer : MonoBehaviour { void OnEnable() { // Subscribe: Assign local functions to delegates CountdownTimer.OnTimerStarted += PlayStartBuzzer; CountdownTimer.OnTimerEnded += PlayEndBuzzer; } void OnDisable() { // Unsubscribe: Always clean up! CountdownTimer.OnTimerStarted -= PlayStartBuzzer; CountdownTimer.OnTimerEnded -= PlayEndBuzzer; } void PlayStartBuzzer() { Debug.Log("[BUZZER] : Play start buzzer!"); } void PlayEndBuzzer() { Debug.Log("[BUZZER] : Play end buzzer!"); } }
Critical: Always unsubscribe in OnDisable() to prevent memory leaks!

Events + Delegates: How They Work Together

The Relationship

Delegate = Type definition (what signature functions must have)
Event = Special delegate with restricted access
Together = Safe, decoupled communication system

// Step 1: Define delegate type public delegate void MyDelegate(); // Step 2: Create event using that delegate public static event MyDelegate OnSomethingHappened; // Step 3: Subscribe a method OnSomethingHappened += MyMethod; // Step 4: Invoke (call all subscribed methods) if (OnSomethingHappened != null) OnSomethingHappened();

Before We Code: Editor Setup ⚙️

Unity + Code Editor Configuration

I'll be demonstrating with Rider, but VS Code works great too!

Setting Your Editor in Unity

  1. Edit → Preferences (Windows) or Unity → Settings (Mac)
  2. External Tools → External Script Editor
  3. Select your preferred editor (Rider or VS Code)
  4. Click "Regenerate project files" if needed

Opening Scripts

  • Double-click any .cs file in Unity
  • Your configured editor launches automatically
  • Right-click → Open C# Project opens entire solution

Why Rider?

  • ✓ Free student license (apply with your OCU email)
  • ✓ Excellent Unity integration and autocomplete
  • ✓ Advanced refactoring tools
  • ✓ Built-in debugger for Unity

VS Code is also excellent - use what works best for you!

Practice: Events & Delegates 💻

15-Minute Challenge: Player Damage System

Setup Instructions (First Time)

  1. Create C# Script in Unity:
    • Right-click in Project window → Create → C# Script
    • Name it Player (Unity will add .cs automatically)
  2. Open in Rider:
    • Double-click the Player.cs file in Unity
    • Rider will launch and open the script
    • Note: VS Code users - same process, double-click opens your configured editor
  3. Repeat for UIHealthBar: Create another script named UIHealthBar

Challenge Tasks:

  1. In Player.cs: Add a health variable (int or float)
  2. Declare a delegate type and event for OnPlayerDamaged
  3. Create a TakeDamage() method that reduces health and invokes the event
  4. In UIHealthBar.cs: Subscribe to the event in OnEnable()
  5. Unsubscribe in OnDisable() (always clean up!)
  6. Log the new health value when the event fires
Bonus: Add an OnPlayerDied event when health reaches zero!
Rider Tip: Use Alt+Enter for quick fixes and code generation!

C# Advanced Feature: Generics

What are Generics?

Generics permit deferred type specification until the class is instantiated. Create type-safe, reusable code templates.

// Generic Singleton - works with ANY component type! public class Singleton<T> : MonoBehaviour where T : Component { private static T _instance; public static T Instance { get { if (_instance == null) { _instance = FindObjectOfType<T>(); } return _instance; } } }

Using Generics

// Use the generic Singleton for different managers public class GameManager : Singleton<GameManager> { // GameManager-specific code } public class AudioManager : Singleton<AudioManager> { // AudioManager-specific code } // Access them anywhere GameManager.Instance.StartGame(); AudioManager.Instance.PlaySound();

Benefits of Generics

  • ✓ Write once, use for many types
  • ✓ Type safety at compile time
  • ✓ No type casting needed
  • ✓ Cleaner, more maintainable code

C# Advanced Feature: Serialization

What is Serialization?

Converting an object instance into binary or text format. Allows you to save and load object state to/from files.

using System.IO; using System.Runtime.Serialization.Formatters.Binary; private void SerializePlayerData(PlayerData playerData) { // Create binary formatter BinaryFormatter bf = new BinaryFormatter(); // Create file at persistent data path FileStream file = File.Create( Application.persistentDataPath + "/playerData.dat"); // Serialize object to file bf.Serialize(file, playerData); file.Close(); }
Use Cases: Save games, player preferences, game state persistence

Unity Engine Feature: Prefabs

What are Prefabs?

Prefabricated containers of assembled GameObjects and components. Reusable building blocks for your game entities.

Why Prefabs?

  • Reusability across scenes
  • Consistent object templates
  • Easy mass updates
  • Runtime instantiation

Examples

  • Enemy types
  • Pickup items
  • Vehicle variants
  • UI elements

In Blade Racer: Each motorcycle type, obstacle, and pickup will be a prefab!

Unity Engine Feature: Unity Events

What are Unity Events?

Unity's native event system - similar to C# events but with Inspector integration for visual configuration.

Unity Events

  • Visible in Inspector
  • Designer-friendly
  • No code required
  • Drag-and-drop wiring

C# Events

  • Code-only
  • Programmer-focused
  • Type-safe delegates
  • Better performance
Best Practice: Use Unity Events for designer-exposed functionality, C# events for internal systems.

Unity Engine Feature: ScriptableObjects

What are ScriptableObjects?

Data containers that live as assets. Separate data from behavior. Use MonoBehaviour for logic, ScriptableObject for data.

using UnityEngine; [CreateAssetMenu(fileName = "NewSword", menuName = "Weaponry/Sword")] public class Sword : ScriptableObject { public string swordName; public string swordPrefab; public int damage; public float attackSpeed; }
Power: Non-programmers can create new sword types through the Unity Editor without touching code!

ScriptableObjects in Action

Workflow Benefits

  1. Right-click in Project → Create → Weaponry → Sword
  2. Name it "FireSword"
  3. Fill in values in Inspector
  4. Reference it in your code or prefabs
  5. Data is now reusable across scenes!

Perfect For

  • Weapon stats
  • Enemy configurations
  • Level data
  • Game settings

Benefits

  • Designer authoring
  • Memory efficient
  • Version control friendly
  • Easy balancing

Unity Engine Feature: Coroutines

What are Coroutines?

Functions with the ability to wait, pause, and time their execution. Provide concurrency (not parallelism).

Normal Function vs Coroutine

Normal Function

Executes start to finish in one frame

Coroutine

Can pause and resume across frames

Coroutines Example

using UnityEngine; using System.Collections; public class CountdownTimer : MonoBehaviour { private float _duration = 10.0f; // IEnumerator return type = coroutine IEnumerator Start() { Debug.Log("Timer Started!"); yield return StartCoroutine(WaitAndPrint(1.0f)); Debug.Log("Timer Ended!"); } IEnumerator WaitAndPrint(float waitTime) { while (Time.time < _duration) { // Wait 1 second between iterations yield return new WaitForSeconds(waitTime); Debug.Log("Seconds: " + Mathf.Round(Time.time)); } } }
Key: yield return pauses execution and resumes next frame (or after wait)

When to Use Coroutines

✅ Perfect For

  • Timers and countdowns
  • Smooth animations
  • Delayed actions
  • Sequences over time
  • Loading operations

❌ Avoid For

  • Every-frame updates
  • Heavy computations
  • Instant results needed
  • True parallelism
// Common coroutine yields: yield return null; // Wait one frame yield return new WaitForSeconds(2f); // Wait 2 seconds yield return new WaitForEndOfFrame(); // Wait until frame end yield return new WaitUntil(() => condition); // Wait for condition

Practice: Coroutines 💻

20-Minute Challenge: Power-Up System

Setup Instructions

  1. Create PowerUp Script:
    • In Unity: Right-click Project → Create → C# Script
    • Name: PowerUp
    • Double-click to open in Rider
  2. Create GameObject:
    • Hierarchy → Right-click → Create Empty
    • Name: "PowerUpManager"
    • Drag PowerUp.cs onto PowerUpManager
  3. Add Test Trigger:
    • In Update(): Check for Input.GetKeyDown(KeyCode.Space)
    • This lets you test by pressing spacebar

Challenge Tasks:

  1. Create coroutine method: IEnumerator PowerUpDuration()
  2. Log "Power-Up Activated!" at start
  3. Use yield return new WaitForSeconds(5f) to wait 5 seconds
  4. Log "Power-Up Ended!" after wait
  5. Call it with StartCoroutine(PowerUpDuration()) when spacebar pressed
  6. Bonus: Add countdown (4, 3, 2, 1...) using a loop with 1-second waits
Rider Tips: Rider auto-completes Unity API methods like StartCoroutine!
Testing: Press Play in Unity, then spacebar to trigger the power-up and watch the Console!

How These Features Work Together

Example: Blade Racer Turbo System

  • ScriptableObject: TurboBoostData (duration, speed multiplier)
  • Prefab: TurboPickup with collider and visual effect
  • Event: OnTurboActivated (notify UI to show boost meter)
  • Coroutine: Temporary speed boost over time
  • Static: GameManager.Instance to track boost status
  • Generics: Singleton<GameManager> for global access
See the power? Each feature solves a specific problem. Combined, they create elegant, maintainable systems!

Important Disclaimer

⚠️ About the Code Examples

All code shown today (and throughout this course) is for educational purposes only. The examples have not been optimized and are not meant for production use.

What This Means

  • Focus is on learning concepts, not performance
  • Production code requires error handling, optimization, edge cases
  • These are simplified examples to illustrate patterns
  • Always test and optimize for real projects

Further Reading & Resources

📚 Primary Text

Game Development Patterns with Unity 2021

By David Baron

Available at Dulaney Browne Library

Recommended Reading: Baron's Chapter 3 goes into more depth on each of these topics. Read it this week if you want deeper understanding!

Moving Forward

You're Ready!

With awareness of these C# and Unity features, you're prepared to tackle design patterns. Don't worry about mastery - we'll practice as we go!

Next Class: Singleton Pattern

We'll implement our first pattern - the Singleton - to create a Game Manager for Blade Racer. You'll see these concepts in action!

If anything covered today was confusing, now is the time to ask questions or schedule office hours before we start implementing!

Questions & Discussion 💬

Open Floor

Questions about:

  • Static members and when to use them?
  • Events vs delegates - what's the difference?
  • Generics and type constraints?
  • Prefabs vs ScriptableObjects?
  • Coroutines and when to use them?
  • Anything else?

Crash Course Complete!

Concepts Covered Today:

C# Features

  • ✓ Static members
  • ✓ Events
  • ✓ Delegates
  • ✓ Generics
  • ✓ Serialization

Unity Features

  • ✓ Prefabs
  • ✓ Unity Events
  • ✓ ScriptableObjects
  • ✓ Coroutines

Next Class: Singleton Pattern - Implementing a Game Manager

Homework: Review Baron's Chapter 3, experiment with code examples in Unity

You're ready to start building! 🚀