What's new

Optimization Building Scalable Unity Games: Controllers and Managers Explained

Under

Administrator
Unity King Developer

Introduction​

The architecture of a game is vital to creating a smooth and engaging user experience. Central to this architecture are controllers and managers, which handle player interaction and game state, respectively. Well-structured controllers ensure precise input handling, while managers maintain the integrity of core game mechanics.

This guide will cover advanced strategies, code examples, and best practices to optimize these components in Unity, ensuring efficient and maintainable game development.

1. Controllers: Interpreting and Responding to Player Input​

Definition and Importance​

Controllers handle player input and translate it into in-game actions. They ensure responsiveness and a seamless user experience. Well-structured controllers contribute to fluid gameplay and better code maintainability.

1.1 Basic Movement Controller​

The following example demonstrates a simple movement controller using Unity’s `InputManager`.

C#:
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public float speed = 5f;

    private void Update()
    {
        float moveHorizontal = Input.GetAxis("Horizontal");
        float moveVertical = Input.GetAxis("Vertical");

        Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);

        transform.position += movement * speed * Time.deltaTime;
    }
}


Key Takeaways:
  • Uses Input.GetAxis() for smooth movement.
  • Adjusts position based on frame time (Time.deltaTime).
  • Basic implementation suitable for simple games.

1.2 Enhanced Movement with Rigidbody​

For physics-based movement, using a Rigidbody ensures proper collision and force application.

C#:
using UnityEngine;

public class PlayerRigidbodyController : MonoBehaviour
{
    public float speed = 5f;
    private Rigidbody rb;

    private void Start()
    {
        rb = GetComponent<Rigidbody>();
    }

    private void FixedUpdate()
    {
        float moveHorizontal = Input.GetAxis("Horizontal");
        float moveVertical = Input.GetAxis("Vertical");

        Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical) * speed;
        rb.velocity = movement;
    }
}


Key Takeaways:
  • Uses Rigidbody.velocity for physics-driven movement.
  • Implemented in FixedUpdate() to ensure consistent physics calculations.
  • Ensures smooth movement with proper collision detection.

1.3 Handling Jump and Dash Mechanics​

To improve character movement, let’s add a jump and a dash mechanic.

C#:
public class PlayerAdvancedController : MonoBehaviour
{
    public float speed = 5f;
    public float jumpForce = 7f;
    public float dashSpeed = 10f;
    private Rigidbody rb;
    private bool isGrounded = true;

    private void Start()
    {
        rb = GetComponent<Rigidbody>();
    }

    private void Update()
    {
        float moveHorizontal = Input.GetAxis("Horizontal");
        float moveVertical = Input.GetAxis("Vertical");

        Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical) * speed;
        rb.velocity = new Vector3(movement.x, rb.velocity.y, movement.z);

        if (Input.GetKeyDown(KeyCode.Space) && isGrounded)
        {
            rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
            isGrounded = false;
        }

        if (Input.GetKeyDown(KeyCode.LeftShift))
        {
            rb.velocity += movement.normalized * dashSpeed;
        }
    }

    private void OnCollisionEnter(Collision collision)
    {
        isGrounded = true;
    }
}


Key Takeaways:
  • Jumping implemented with ForceMode.Impulse.
  • Dashing provides a burst of speed in the current direction.
  • Ground check ensures controlled jumping.

2. Managers: Maintaining Game State and Logic​

Definition and Importance​

Managers handle core game logic, such as inventory management, UI state, enemy AI, and level progression. They centralize logic, preventing redundancy and making debugging easier.

2.1 Inventory Manager​

An inventory system is essential for many games. This manager handles item storage and retrieval.

C#:
using System.Collections.Generic;
using UnityEngine;

public class InventoryManager : MonoBehaviour
{
    private List<Item> inventoryItems = new List<Item>();

    public void AddItem(Item item)
    {
        inventoryItems.Add(item);
        Debug.Log($"Added {item.name} to inventory.");
    }

    public bool RemoveItem(Item item)
    {
        if (inventoryItems.Contains(item))
        {
            inventoryItems.Remove(item);
            Debug.Log($"{item.name} removed from inventory.");
            return true;
        }
        return false;
    }

    public void ListItems()
    {
        foreach (Item item in inventoryItems)
        {
            Debug.Log($"Inventory Item: {item.name}");
        }
    }
}


Key Takeaways:
  • Implements a list to store items.
  • Logs actions for debugging.
  • Provides methods to add, remove, and list items.

2.2 Game State Manager​

Managing game states (e.g., playing, paused, game over) ensures smooth transitions.

C#:
using UnityEngine;

public class GameManager : MonoBehaviour
{
    public static GameManager Instance { get; private set; }

    public enum GameState { Playing, Paused, GameOver }
    public GameState CurrentState { get; private set; }

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }

    public void SetGameState(GameState state)
    {
        CurrentState = state;
        Debug.Log($"Game state changed to: {state}");
    }
}


Key Takeaways:
  • Uses Singleton pattern for global access.
  • Manages different game states.
  • Implements DontDestroyOnLoad() to persist across scenes.

3. Integration and Communication Between Controllers and Managers​

Importance of Cohesion​

Controllers and managers must communicate efficiently. This ensures proper event handling and state management.

3.1 Event-Driven Architecture for Interaction​

Using Unity’s C# Events improves modularity and scalability.

C#:
using System;
using UnityEngine;

public class PlayerEvents : MonoBehaviour
{
    public static event Action<int> OnHealthChanged;

    public static void ChangeHealth(int newHealth)
    {
        OnHealthChanged?.Invoke(newHealth);
    }
}

Subscribing to the event:

C#:
public class UIManager : MonoBehaviour
{
    private void OnEnable()
    {
        PlayerEvents.OnHealthChanged += UpdateHealthUI;
    }

    private void OnDisable()
    {
        PlayerEvents.OnHealthChanged -= UpdateHealthUI;
    }

    private void UpdateHealthUI(int health)
    {
        Debug.Log($"Player health updated to: {health}");
    }
}


Key Takeaways:
  • Uses Action<int> for flexible event handling.
  • Prevents tight coupling between UI and player scripts.
  • Ensures modularity in game architecture.

Conclusion​

By implementing well-structured controllers and managers, developers can create efficient, scalable, and maintainable Unity games. These best practices ensure smooth gameplay and better debugging, ultimately improving the player experience.

📢 What are your strategies for managing controllers and managers in Unity? Share your thoughts below!
 

Top