Skip to main content
C# for Games – Complete Beginner to Advanced Guide
CHAPTER 18 Beginner

Working with Unity and C#

Updated: May 16, 2026
25 min read

# CHAPTER 18

Working with Unity and C#

1. Introduction

Throughout this course, we have used C# to learn universal gameplay programming logic. We built variables, loops, classes, and physics concepts that apply to any engine. However, in the professional industry, C# is synonymous with the Unity Game Engine. Unity provides a massive library of pre-written C# code—called an API (Application Programming Interface)—that allows you to control graphics, physics, and audio without writing the low-level C++ math yourself. In this chapter, we will master the Unity C# integration. We will explore the MonoBehaviour lifecycle, learn how to expose variables to the Unity Inspector, and utilize the UnityEngine namespace.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Understand the MonoBehaviour base class and its requirement in Unity.
  • Memorize the execution order of Unity's event functions (Awake, Start, Update).
  • Use [SerializeField] to expose private C# variables to the visual Editor.
  • Communicate between multiple C# scripts attached to GameObjects.
  • Utilize the UnityEngine namespace for math and debugging.

3. The MonoBehaviour Class

If you create a standard C# script, Unity doesn't know what to do with it. To attach a script to a 3D character or a 2D sprite, the script *must* inherit from Unity's base class: MonoBehaviour.
csharp
1234567
using UnityEngine; // This imports the Unity API

// By inheriting from MonoBehaviour, this script becomes a "Component"
public class PlayerController : MonoBehaviour
{
    // Script logic goes here
}

4. The Lifecycle (Execution Order)

When you press "Play" in Unity, the engine executes specific methods in a strict order.
  1. 1. Awake(): Runs instantly the moment the object is created. Use this to grab components (GetComponent).
  1. 2. Start(): Runs right before the first frame update. Use this to initialize variables or find other objects in the scene.
  1. 3. Update(): Runs once per frame (Graphics and Input).
  1. 4. FixedUpdate(): Runs on a fixed timer (Physics).
  1. 5. OnDestroy(): Runs the moment the object is deleted.

5. Exposing Variables to the Inspector

Game designers don't want to open Visual Studio just to change the player's jump height. We must expose variables to Unity's visual Inspector.
  • The Bad Way: Making the variable public. This exposes it to the Inspector, but it also ruins Encapsulation (any other script can change it).
  • The Professional Way: Keep the variable private, but add the [SerializeField] attribute above it.
csharp
1234567
public class Enemy : MonoBehaviour
{
    // Appears in the Unity Editor for designers to tweak,
    // but remains completely hidden from other C# scripts!
    [SerializeField] private float speed = 5.0f; 
    [SerializeField] private GameObject lootPrefab;
}

6. Communicating Between Scripts (GetComponent)

How does the Player script tell the Health script to subtract 10 HP? Because both scripts are Components attached to the same GameObject, they can find each other using GetComponent<T>().
csharp
12345678910111213141516
public class Player : MonoBehaviour
{
    private Health myHealthScript;

    void Awake()
    {
        // Search this GameObject for a script named 'Health'
        myHealthScript = GetComponent<Health>();
    }

    void OnCollisionEnter(Collision hit)
    {
        // Call the public method on the other script!
        myHealthScript.TakeDamage(10);
    }
}

7. Visual Learning: The Unity Script Connection

txt
123456789
[ Unity Editor ]
  GameObject: "Hero"
   |- Transform (Position/Rotation)
   |- Rigidbody (Physics)
   |- Player.cs (Your Script)  <-- [SerializeField] speed = 5.0
   |- Health.cs (Your Script)

*When Play is pressed:*
Unity reads Player.cs -> Calls Awake() -> Calls Start() -> Calls Update() 60x a sec

8. Best Practices

  • RequireComponent: If your PlayerMotor.cs script uses GetComponent<Rigidbody>(), the game will crash if you forget to add a Rigidbody in the editor. Add [RequireComponent(typeof(Rigidbody))] at the very top of your script. This forces Unity to automatically add a Rigidbody the moment you attach the script!

9. Common Mistakes

  • Naming Conflicts: Do not name your C# script Camera or Collider. Unity already has internal classes with those exact names. If you do this, the compiler will get confused, throwing hundreds of errors. Name your scripts descriptively: PlayerCamera.cs or DamageCollider.cs.

10. Mini Project: Build a Complete Unity Component

Objective: Write a fully formatted, Unity-ready script utilizing attributes and the lifecycle.
csharp
123456789101112131415161718192021222324252627282930313233343536
using UnityEngine;

// Force the engine to add an AudioSource so we don't forget!
[RequireComponent(typeof(AudioSource))]
public class CoinPickup : MonoBehaviour
{
    [Header("Settings")]
    [SerializeField] private int coinValue = 10;
    [SerializeField] private AudioClip pingSound;

    private AudioSource audioSource;

    // 1. Grab references
    void Awake()
    {
        audioSource = GetComponent<AudioSource>();
    }

    // 2. Rotate the coin every frame
    void Update()
    {
        transform.Rotate(0, 90 * Time.deltaTime, 0);
    }

    // 3. Detect player collision
    void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("Player"))
        {
            // Update score, play sound, and destroy
            GameManager.Instance.AddScore(coinValue);
            audioSource.PlayOneShot(pingSound);
            Destroy(gameObject);
        }
    }
}

11. Practice Exercises

  1. 1. What is the difference between Awake() and Start() in the Unity lifecycle?
  1. 2. What C# attribute allows a private variable to be modified inside the Unity visual Inspector?

12. MCQs with Answers

Question 1

You want to write a custom C# script and attach it to a 3D Cube in Unity. What base class MUST your script inherit from for Unity to recognize it as a component?

Question 2

You need to get a reference to the BoxCollider attached to the same GameObject as your script. Which Unity method should you use?

13. Interview Questions

  • Q: Explain the purpose of the [SerializeField] attribute. Why is using this attribute vastly superior to making all your variables public just so game designers can see them in the Unity Inspector?
  • Q: Walk me through the Unity MonoBehaviour execution order for the initialization phase. If you have two scripts interacting, why should you grab component references in Awake() but run setup logic in Start()?
  • Q: What is the performance impact of calling GetComponent<T>() inside the Update() loop, and how is it properly mitigated?

14. FAQs

Q: Do I have to use Visual Studio with Unity? A: No! While Visual Studio is the default, many professional developers prefer JetBrains Rider (which is explicitly built for Unity C#) or Visual Studio Code (which is lightweight and highly customizable).

15. Summary

In Chapter 18, we integrated our raw C# knowledge into the Unity Game Engine. We learned that to exist in the game world, a script must inherit from MonoBehaviour. We mapped the engine's execution lifecycle, understanding exactly when Awake, Start, and Update are called by the C++ core. We maintained strict encapsulation by utilizing [SerializeField] to expose variables to designers without breaking our architecture. We are now writing true Unity gameplay code.

16. Next Chapter Recommendation

You have the knowledge. Now you must prove it. Proceed to Chapter 19: C# Game Development Interview Questions and Challenges.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·