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

Building UI Systems for Games

Updated: May 16, 2026
25 min read

# CHAPTER 10

Building UI Systems for Games

1. Introduction

A pristine combat system is useless if the player doesn't know their health is at 1%. Building a clean, responsive User Interface (UI) and Heads-Up Display (HUD) is critical to player experience. However, programming UI requires a different architectural mindset than programming a character. UI is event-driven; it sits idle until data changes or a button is clicked. In this chapter, we will master UI Programming. We will learn how to link C# scripts to visual text elements, build a dynamic Health Bar, construct a functioning Pause Menu, and explore the "UI Manager" architectural pattern.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Reference UI components (like Text and Sliders) in C# scripts.
  • Update HUD elements dynamically based on player data.
  • Write C# methods that are triggered by UI Button clicks.
  • Implement a Pause Menu by manipulating Time.timeScale.
  • Design a decoupled UIManager Singleton to handle all canvas updates.

3. Referencing UI Elements in Code

To change what a text box says on the screen, your C# script needs a reference to it.
  • The Import: In Unity, you must add using UnityEngine.UI; (or TMPro for TextMeshPro) at the top of your script.
  • The Variable: You declare a public variable of type Text or Slider.
  • The Action: You assign a new string to its .text property.
csharp
123456789101112
using UnityEngine.UI; // Required for UI code!

class HealthHUD
{
    public Text healthText; // Drag the Text UI object here in the Editor
    
    public void UpdateHealthDisplay(int currentHealth)
    {
        // Convert the int to a string and display it!
        healthText.text = "HP: " + currentHealth.ToString();
    }
}

4. Programming Buttons (Events)

When a player clicks "Start Game", the UI Button needs to run a C# method.
  1. 1. You write a public method in your script (e.g., public void StartGame()). *It must be public, or the Button cannot see it!*
  1. 2. In the visual Editor, you go to the Button's "On Click()" event list.
  1. 3. You link the script and select your StartGame() method from the dropdown.

5. The Pause Menu (TimeScale)

How do you freeze the entire game when the player opens the menu?
  • Engines use a global variable called TimeScale.
  • A TimeScale of 1.0 is normal speed. 0.5 is slow motion. 0.0 completely freezes Update() delta time and Physics!
csharp
1234567891011121314151617
bool isPaused = false;

public void TogglePause()
{
    isPaused = !isPaused; // Flip the boolean

    if (isPaused)
    {
        Time.timeScale = 0f; // Freeze game
        pauseMenuPanel.SetActive(true); // Show UI
    }
    else
    {
        Time.timeScale = 1f; // Resume game
        pauseMenuPanel.SetActive(false); // Hide UI
    }
}

6. The UIManager (Architecture)

Bad Practice: The Player script knows about the healthText, the Enemy script knows about the scoreText, and the Level script knows about the gameOverScreen. This creates tangled "spaghetti code." Best Practice: Create ONE script called UIManager. Make it a Singleton (globally accessible). Any time any script wants to change the UI, they simply tell the UIManager.
csharp
1234567891011
// Example of clean architecture
class Player
{
    void TakeDamage(int dmg)
    {
        health -= dmg;
        // The player just tells the UIManager what happened. 
        // The player doesn't care HOW the UI is drawn.
        UIManager.Instance.UpdateHealthBar(health); 
    }
}

7. Visual Learning: UI Event Flow

txt
12345678910111213
[ The Player ]
   |
   | (Takes 20 Damage)
   |
   v
[ UIManager.Instance.UpdateHealthBar(80) ]
   |
   | (Formats string, calculates percentage)
   |
   v
[ The Canvas ]
  -> Changes Text to "80/100"
  -> Shrinks Red Slider to 80%

8. Best Practices

  • Don't Update UI in Update(): A beginner will put scoreText.text = score; inside the Update() loop, forcing the computer to redraw the text 60 times a second, even if the score hasn't changed! UI is expensive to render. Only update the UI *exactly when the data changes* (Event-Driven programming).

9. Common Mistakes

  • Time.timeScale Freezing Input: A developer pauses the game by setting Time.timeScale = 0. They then try to unpause by checking an input timer that uses Time.deltaTime. Because time is frozen, deltaTime is 0, the timer never counts down, and the game is permanently locked! Use Time.unscaledDeltaTime for UI animations during a pause.

10. Mini Project: Build a Functional Health Bar

Objective: Link a visual slider to a C# variable.
  1. 1. Assume you have a UI Slider on the screen representing health (Value 0 to 1).
csharp
12345678910111213141516171819202122
using UnityEngine.UI;

class UIManager
{
    public Slider healthSlider; // Link in editor
    public Text statusText;

    // Called by the Player script when hit
    public void UpdateHealth(int currentHP, int maxHP)
    {
        // Calculate percentage (e.g., 50 / 100 = 0.5f)
        // We cast to float to prevent integer division resulting in 0!
        float healthPercentage = (float)currentHP / (float)maxHP;
        
        healthSlider.value = healthPercentage;

        if (currentHP <= 0)
        {
            statusText.text = "YOU DIED";
        }
    }
}

11. Practice Exercises

  1. 1. What property of a UI Text element do you modify in C# to change the words displayed on the screen?
  1. 2. What global engine variable do you set to 0 to freeze the entire physics simulation and game loop?

12. MCQs with Answers

Question 1

You want a UI Button to execute a C# method named LoadLevel() when clicked. What access modifier MUST be placed in front of the method definition so the Button can see it in the engine editor?

Question 2

Why is placing healthText.text = playerHealth.ToString(); inside the Update() function considered a severe architectural flaw?

13. Interview Questions

  • Q: Explain the concept of Event-Driven programming in the context of UI development. Why is the Observer Pattern (or C# Events/Delegates) superior to checking data in an Update() loop?
  • Q: Walk me through the implementation of a global UIManager Singleton. How does this decouple the Player logic from the Canvas rendering?
  • Q: You pause the game using Time.timeScale = 0f. You want a UI menu panel to smoothly animate onto the screen using C# math. Why does transform.Translate(speed * Time.deltaTime) fail, and how do you fix it?

14. FAQs

Q: How do I handle different screen resolutions (1080p vs 4K)? A: That is handled by the Engine's UI Canvas scaler (setting "Scale with Screen Size" and using Anchors), not by your C# code. Your C# code simply updates the data; the Canvas handles the dynamic resizing!

15. Summary

In Chapter 10, we built the bridge between the game's internal data and the human player. We learned how to import UI namespaces to manipulate Text and Sliders dynamically. We programmed interactive events by exposing public methods to UI Buttons. We manipulated the flow of time itself using Time.timeScale to create functional Pause Menus. Finally, we learned the professional standard of utilizing a UIManager to keep our codebase decoupled, scalable, and clean.

16. Next Chapter Recommendation

The player can see their health, but nothing is attacking them. We need to build hostile logic. Proceed to Chapter 11: Enemy AI and NPC Programming.

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: ·