Lesson 9: UI Design & Menus

Welcome to the essential UI design lesson! You'll learn how to create intuitive, professional game interfaces that enhance player experience. By the end of this lesson, you'll have a complete UI system with main menus, pause screens, and in-game HUD.

What You'll Learn

In this lesson, you'll master:

  • UI/UX Design Principles for 2D games
  • Menu Navigation Systems with smooth transitions
  • HUD Design for gameplay information
  • Responsive UI that works on different screen sizes
  • Audio Integration for UI feedback

Why UI Design Matters

Great UI design is the difference between a good game and a great game. It's the first thing players see and the last thing they remember. Professional UI design:

  • Guides players through your game experience
  • Provides clear feedback for all interactions
  • Enhances immersion without distracting from gameplay
  • Builds trust with polished, professional appearance

Step 1: UI Design Planning

Before diving into Unity, let's plan your UI system:

UI Hierarchy Design

Create a clear hierarchy for your game's interface:

Main Menu
├── Play Button
├── Settings Button
├── Credits Button
└── Quit Button

Settings Menu
├── Audio Settings
├── Graphics Settings
└── Controls Settings

In-Game HUD
├── Health Bar
├── Score Display
├── Lives Counter
└── Pause Button

Pause Menu
├── Resume Button
├── Settings Button
├── Main Menu Button
└── Quit Button

Design Principles

Follow these key principles for professional UI:

  1. Clarity: Every element should have a clear purpose
  2. Consistency: Use consistent colors, fonts, and spacing
  3. Hierarchy: Important information should be most prominent
  4. Accessibility: Ensure UI works for all players
  5. Performance: Keep UI lightweight and responsive

Step 2: Unity UI System Setup

Let's set up Unity's UI system for your 2D platformer:

Canvas Configuration

  1. Create Canvas: Right-click in Hierarchy → UI → Canvas
  2. Set Canvas Scaler:
    • UI Scale Mode: Scale With Screen Size
    • Reference Resolution: 1920x1080
    • Screen Match Mode: Match Width Or Height
    • Match: 0.5 (balanced scaling)

UI Layer Structure

Create this hierarchy in your Canvas:

Canvas
├── MainMenu (Panel)
├── GameHUD (Panel)
├── PauseMenu (Panel)
├── SettingsMenu (Panel)
└── LoadingScreen (Panel)

Step 3: Main Menu Design

Create an engaging main menu that sets the tone for your game:

Main Menu Layout

  1. Background: Use your game's art style
  2. Logo: Prominent game title
  3. Navigation: Clear, large buttons
  4. Visual Effects: Subtle animations

Button Design Best Practices

  • Size: Minimum 44x44 pixels for touch targets
  • Spacing: 20-30 pixels between buttons
  • Colors: High contrast for readability
  • States: Normal, Hover, Pressed, Disabled

Implementation Steps

  1. Create Main Menu Panel:

    // MainMenuController.cs
    public class MainMenuController : MonoBehaviour
    {
       [SerializeField] private Button playButton;
       [SerializeField] private Button settingsButton;
       [SerializeField] private Button quitButton;
    
       void Start()
       {
           playButton.onClick.AddListener(StartGame);
           settingsButton.onClick.AddListener(OpenSettings);
           quitButton.onClick.AddListener(QuitGame);
       }
    
       void StartGame()
       {
           SceneManager.LoadScene("GameScene");
       }
    
       void OpenSettings()
       {
           // Switch to settings menu
       }
    
       void QuitGame()
       {
           Application.Quit();
       }
    }
  2. Add Button Animations:

    // ButtonAnimation.cs
    public class ButtonAnimation : MonoBehaviour
    {
       [SerializeField] private float hoverScale = 1.1f;
       [SerializeField] private float animationSpeed = 5f;
    
       private Vector3 originalScale;
    
       void Start()
       {
           originalScale = transform.localScale;
       }
    
       public void OnPointerEnter()
       {
           StartCoroutine(ScaleTo(originalScale * hoverScale));
       }
    
       public void OnPointerExit()
       {
           StartCoroutine(ScaleTo(originalScale));
       }
    
       IEnumerator ScaleTo(Vector3 targetScale)
       {
           while (Vector3.Distance(transform.localScale, targetScale) > 0.01f)
           {
               transform.localScale = Vector3.Lerp(transform.localScale, targetScale, animationSpeed * Time.deltaTime);
               yield return null;
           }
       }
    }

Step 4: In-Game HUD Design

Create a functional HUD that provides essential gameplay information:

HUD Elements

Design these key HUD components:

  1. Health Bar: Visual representation of player health
  2. Score Display: Current score with animations
  3. Lives Counter: Remaining lives with icons
  4. Mini-map: Optional navigation aid
  5. Pause Button: Easy access to pause menu

Health Bar Implementation

// HealthBar.cs
public class HealthBar : MonoBehaviour
{
    [SerializeField] private Slider healthSlider;
    [SerializeField] private Image fillImage;
    [SerializeField] private Color healthyColor = Color.green;
    [SerializeField] private Color damagedColor = Color.red;

    private float maxHealth = 100f;
    private float currentHealth;

    void Start()
    {
        currentHealth = maxHealth;
        UpdateHealthBar();
    }

    public void TakeDamage(float damage)
    {
        currentHealth -= damage;
        currentHealth = Mathf.Clamp(currentHealth, 0, maxHealth);
        UpdateHealthBar();
    }

    void UpdateHealthBar()
    {
        float healthPercentage = currentHealth / maxHealth;
        healthSlider.value = healthPercentage;

        // Change color based on health
        fillImage.color = Color.Lerp(damagedColor, healthyColor, healthPercentage);
    }
}

Score Display with Animations

// ScoreDisplay.cs
public class ScoreDisplay : MonoBehaviour
{
    [SerializeField] private Text scoreText;
    [SerializeField] private float animationDuration = 0.5f;

    private int currentScore;
    private int targetScore;

    public void UpdateScore(int newScore)
    {
        targetScore = newScore;
        StartCoroutine(AnimateScore());
    }

    IEnumerator AnimateScore()
    {
        float elapsed = 0f;
        int startScore = currentScore;

        while (elapsed < animationDuration)
        {
            elapsed += Time.deltaTime;
            float progress = elapsed / animationDuration;
            currentScore = Mathf.RoundToInt(Mathf.Lerp(startScore, targetScore, progress));
            scoreText.text = "Score: " + currentScore.ToString();
            yield return null;
        }

        currentScore = targetScore;
        scoreText.text = "Score: " + currentScore.ToString();
    }
}

Step 5: Pause Menu System

Implement a comprehensive pause menu system:

Pause Menu Features

  • Resume Game: Return to gameplay
  • Settings Access: Audio, graphics, controls
  • Save/Load: Game state management
  • Main Menu: Return to main menu
  • Quit Game: Exit application

Pause System Implementation

// PauseManager.cs
public class PauseManager : MonoBehaviour
{
    [SerializeField] private GameObject pauseMenu;
    [SerializeField] private GameObject gameHUD;

    private bool isPaused = false;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            TogglePause();
        }
    }

    public void TogglePause()
    {
        isPaused = !isPaused;

        if (isPaused)
        {
            Time.timeScale = 0f;
            pauseMenu.SetActive(true);
            gameHUD.SetActive(false);
            Cursor.lockState = CursorLockMode.None;
        }
        else
        {
            Time.timeScale = 1f;
            pauseMenu.SetActive(false);
            gameHUD.SetActive(true);
            Cursor.lockState = CursorLockMode.Locked;
        }
    }

    public void ResumeGame()
    {
        TogglePause();
    }

    public void ReturnToMainMenu()
    {
        Time.timeScale = 1f;
        SceneManager.LoadScene("MainMenu");
    }
}

Step 6: Settings Menu Implementation

Create a comprehensive settings menu for player preferences:

Settings Categories

  1. Audio Settings: Master volume, SFX, music
  2. Graphics Settings: Quality, resolution, fullscreen
  3. Controls Settings: Key bindings, sensitivity
  4. Gameplay Settings: Difficulty, accessibility options

Audio Settings Implementation

// AudioSettings.cs
public class AudioSettings : MonoBehaviour
{
    [SerializeField] private Slider masterVolumeSlider;
    [SerializeField] private Slider musicVolumeSlider;
    [SerializeField] private Slider sfxVolumeSlider;

    void Start()
    {
        LoadSettings();
        SetupSliders();
    }

    void SetupSliders()
    {
        masterVolumeSlider.onValueChanged.AddListener(SetMasterVolume);
        musicVolumeSlider.onValueChanged.AddListener(SetMusicVolume);
        sfxVolumeSlider.onValueChanged.AddListener(SetSFXVolume);
    }

    void SetMasterVolume(float volume)
    {
        AudioListener.volume = volume;
        PlayerPrefs.SetFloat("MasterVolume", volume);
    }

    void SetMusicVolume(float volume)
    {
        // Set music volume
        PlayerPrefs.SetFloat("MusicVolume", volume);
    }

    void SetSFXVolume(float volume)
    {
        // Set SFX volume
        PlayerPrefs.SetFloat("SFXVolume", volume);
    }

    void LoadSettings()
    {
        masterVolumeSlider.value = PlayerPrefs.GetFloat("MasterVolume", 1f);
        musicVolumeSlider.value = PlayerPrefs.GetFloat("MusicVolume", 1f);
        sfxVolumeSlider.value = PlayerPrefs.GetFloat("SFXVolume", 1f);
    }
}

Step 7: Responsive UI Design

Ensure your UI works on different screen sizes and resolutions:

Responsive Design Principles

  1. Anchor Points: Use proper anchor positioning
  2. Canvas Scaler: Configure for different aspect ratios
  3. Safe Areas: Account for notches and bezels
  4. Scalable Elements: Use vector graphics when possible

Screen Adaptation

// ScreenAdapter.cs
public class ScreenAdapter : MonoBehaviour
{
    [SerializeField] private Canvas canvas;
    [SerializeField] private CanvasScaler canvasScaler;

    void Start()
    {
        AdaptToScreen();
    }

    void AdaptToScreen()
    {
        float screenRatio = (float)Screen.width / Screen.height;
        float targetRatio = 16f / 9f;

        if (screenRatio > targetRatio)
        {
            // Wide screen - match height
            canvasScaler.matchWidthOrHeight = 1f;
        }
        else
        {
            // Tall screen - match width
            canvasScaler.matchWidthOrHeight = 0f;
        }
    }
}

Step 8: UI Polish and Effects

Add professional polish to your UI with effects and animations:

Button Hover Effects

// ButtonEffects.cs
public class ButtonEffects : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
    [SerializeField] private float hoverScale = 1.05f;
    [SerializeField] private float clickScale = 0.95f;
    [SerializeField] private float animationSpeed = 10f;

    private Vector3 originalScale;
    private bool isHovering = false;

    void Start()
    {
        originalScale = transform.localScale;
    }

    public void OnPointerEnter(PointerEventData eventData)
    {
        isHovering = true;
        StartCoroutine(ScaleTo(originalScale * hoverScale));
    }

    public void OnPointerExit(PointerEventData eventData)
    {
        isHovering = false;
        StartCoroutine(ScaleTo(originalScale));
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        StartCoroutine(ScaleTo(originalScale * clickScale));
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        if (isHovering)
        {
            StartCoroutine(ScaleTo(originalScale * hoverScale));
        }
        else
        {
            StartCoroutine(ScaleTo(originalScale));
        }
    }

    IEnumerator ScaleTo(Vector3 targetScale)
    {
        while (Vector3.Distance(transform.localScale, targetScale) > 0.01f)
        {
            transform.localScale = Vector3.Lerp(transform.localScale, targetScale, animationSpeed * Time.deltaTime);
            yield return null;
        }
    }
}

Menu Transitions

// MenuTransition.cs
public class MenuTransition : MonoBehaviour
{
    [SerializeField] private float transitionDuration = 0.5f;
    [SerializeField] private AnimationCurve transitionCurve;

    public void TransitionToMenu(GameObject targetMenu)
    {
        StartCoroutine(AnimateTransition(targetMenu));
    }

    IEnumerator AnimateTransition(GameObject targetMenu)
    {
        float elapsed = 0f;
        Vector3 startScale = transform.localScale;
        Vector3 targetScale = Vector3.zero;

        while (elapsed < transitionDuration)
        {
            elapsed += Time.deltaTime;
            float progress = elapsed / transitionDuration;
            float curveValue = transitionCurve.Evaluate(progress);

            transform.localScale = Vector3.Lerp(startScale, targetScale, curveValue);
            yield return null;
        }

        gameObject.SetActive(false);
        targetMenu.SetActive(true);
        targetMenu.transform.localScale = Vector3.zero;
        StartCoroutine(AnimateIn(targetMenu));
    }

    IEnumerator AnimateIn(GameObject menu)
    {
        float elapsed = 0f;
        Vector3 startScale = Vector3.zero;
        Vector3 targetScale = Vector3.one;

        while (elapsed < transitionDuration)
        {
            elapsed += Time.deltaTime;
            float progress = elapsed / transitionDuration;
            float curveValue = transitionCurve.Evaluate(progress);

            menu.transform.localScale = Vector3.Lerp(startScale, targetScale, curveValue);
            yield return null;
        }
    }
}

Pro Tips for UI Design

Visual Hierarchy

  • Size: Larger elements draw more attention
  • Color: Use color to guide player attention
  • Contrast: Ensure text is readable against backgrounds
  • Spacing: Use consistent spacing for professional look

User Experience

  • Feedback: Provide immediate feedback for all interactions
  • Consistency: Use consistent patterns throughout
  • Accessibility: Consider colorblind players and different abilities
  • Performance: Keep UI lightweight for smooth gameplay

Common Mistakes

  • Cluttered UI: Too many elements competing for attention
  • Poor Contrast: Text that's hard to read
  • Inconsistent Styling: Mixed design languages
  • No Feedback: Buttons that don't respond to clicks

Troubleshooting Common Issues

UI Not Scaling Properly

Problem: UI elements appear too small or large on different screens Solution: Check Canvas Scaler settings and anchor points

Buttons Not Responding

Problem: Buttons don't trigger when clicked Solution: Verify EventSystem is present and buttons have proper colliders

Text Not Displaying

Problem: Text elements show as blank Solution: Check font import settings and text component configuration

Performance Issues

Problem: UI causes frame rate drops Solution: Optimize images, use object pooling for dynamic elements

Mini Challenge: Complete UI System

Create a complete UI system for your 2D platformer:

  1. Design Main Menu: Create an engaging main menu with logo and navigation
  2. Build Game HUD: Implement health bar, score display, and lives counter
  3. Add Pause Menu: Create pause menu with resume, settings, and quit options
  4. Implement Settings: Add audio, graphics, and control settings
  5. Test Responsiveness: Ensure UI works on different screen sizes

Success Criteria:

  • All menus are functional and responsive
  • UI provides clear feedback for interactions
  • Settings are saved and loaded properly
  • Transitions are smooth and professional

What's Next?

In the next lesson, you'll learn about Level Progression & Save System. You'll implement:

  • Level unlocking mechanics
  • Save/load functionality
  • Progress tracking
  • Player data persistence

Community & Support

Share your UI designs in our Discord community:

  • Get feedback on your menu designs
  • Share screenshots of your progress
  • Ask questions about UI implementation
  • Connect with other developers

Key Takeaways

  • UI Design is Critical: First impression matters for player engagement
  • Consistency is Key: Use consistent styling throughout your game
  • Feedback is Essential: Players need to know their actions are registered
  • Responsive Design: Ensure UI works on all target devices
  • Performance Matters: Keep UI lightweight for smooth gameplay

Ready to create professional game interfaces? Your UI system is the foundation of player experience - make it count!


Ready to level up your 2D platformer development skills? Join our Discord community to share your UI designs and get feedback from fellow developers!