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:
- Clarity: Every element should have a clear purpose
- Consistency: Use consistent colors, fonts, and spacing
- Hierarchy: Important information should be most prominent
- Accessibility: Ensure UI works for all players
- 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
- Create Canvas: Right-click in Hierarchy → UI → Canvas
- 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
- Background: Use your game's art style
- Logo: Prominent game title
- Navigation: Clear, large buttons
- 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
-
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(); } }
-
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:
- Health Bar: Visual representation of player health
- Score Display: Current score with animations
- Lives Counter: Remaining lives with icons
- Mini-map: Optional navigation aid
- 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
- Audio Settings: Master volume, SFX, music
- Graphics Settings: Quality, resolution, fullscreen
- Controls Settings: Key bindings, sensitivity
- 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
- Anchor Points: Use proper anchor positioning
- Canvas Scaler: Configure for different aspect ratios
- Safe Areas: Account for notches and bezels
- 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:
- Design Main Menu: Create an engaging main menu with logo and navigation
- Build Game HUD: Implement health bar, score display, and lives counter
- Add Pause Menu: Create pause menu with resume, settings, and quit options
- Implement Settings: Add audio, graphics, and control settings
- 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!