Updating floor visibility only with events and with no update

It is a HUGE optimization problem calling all this logic on update. Think that literally every object on the map if checking if it has to hide or not EVERY frame. When the user will not zoom more than 1 or 2 times per second. And the units will only change floors when the do move. So, events is the correct solution here.

Let me show you how I did this:

  1. Changed FloorVisibility script to look like so (Subscribing to CameraController.OnCameraZoomed and LevelGrid.OnAnyUnitChangedFloor on start). Make sure this subscriptions happens AFTER checking if you should destroy the object. If you subscribe before, you will have problems with events being called on destroyed objects.
using System.Collections.Generic;
using UnityEngine;

public class FloorVisibility : MonoBehaviour
{
    [SerializeField] private bool dymanicFloorPosition;
    [SerializeField] private List<Renderer> ignoredRenderers;
    
    private Renderer[] renderers;
    private int floor;
    
    private void Awake()
    {
        renderers = GetComponentsInChildren<Renderer>(true);
    }

    private void Start()
    {
        floor = LevelGrid.Instance.GetFloor(transform.position);

        if (floor == 0 && !dymanicFloorPosition)
        {
            // Ground floor does not need to be hidden
            Destroy(this);
            return;
        }
        
        // Always update floor visibility on cameza zoom
        CameraController.OnCameraZoomed += OnCameraZoomed;
        // Only update floor if it is dynamic
        if (dymanicFloorPosition)
            LevelGrid.OnAnyUnitChangedFloor += OnAnyUnitChangedFloor;
    }

    private void OnAnyUnitChangedFloor()
    {
        UpdateUnitFloor();
        UpdateFloorVisiblity();
    }

    private void OnCameraZoomed()
    {
        UpdateFloorVisiblity();
    }
    
    private void UpdateUnitFloor()
    { 
        floor = LevelGrid.Instance.GetFloor(transform.position);
    }

    private void UpdateFloorVisiblity()
    {
        float cameraHeight = CameraController.Instance.GetCameraHeight();
        const float floorHeightOffset = 6.5f;
        
        bool showObject = cameraHeight > LevelGrid.FLOOR_HEIGHT * floor + floorHeightOffset;
        
        SetShow(showObject || floor == 0);
    }

    private void SetShow(bool show)
    {
        foreach (Renderer _renderer in renderers)
        {
            if (ignoredRenderers.Contains(_renderer))
                continue;

            _renderer.enabled = show;
        }
    }
}

  1. Create the STATIC OnCameraZoomed event on CameraController:
    public static event Action OnCameraZoomed;

  2. Call the event in HandleCameraZoom like so:

private void HandleCameraZoom()
    {
        float newOffsetY = targetFollowOffset.y + InputManager.Instance.GetCameraZoomAmount();
        newOffsetY = Mathf.Clamp(newOffsetY, MIN_FOLLOW_Y_OFFSET, MAX_FOLLOW_Y_OFFSET);
        
        // This is checking if the value changed by more than 0.001f, so to not call it every frame
        // Rememer this code IS called every frame, but the event is not, only when the player zooms in or out
        if (Math.Abs(newOffsetY - targetFollowOffset.y) > 0.001f)
        {
            OnCameraZoomed?.Invoke();
        }
        
        targetFollowOffset.y = newOffsetY;

        const float zoomSpeed = 10f;
        cinemachineTransposer.m_FollowOffset = Vector3.Slerp(cinemachineTransposer.m_FollowOffset, targetFollowOffset,
            Time.deltaTime * zoomSpeed);
    }
  1. Create the STATIC OnAnyUnitChangedFloor event on LevelGrid:
    public static event Action OnAnyUnitChangedFloor;

  2. Invoke the event on the MoveUnitGridPos method (sorry if I changed this name from the course one, should be easy one to find)

   public void MoveUnitGridPos(Unit unit, GridPosition fromGridPosition, GridPosition toGridPosition)
    {
        RemoveUnitAtGridPos(fromGridPosition, unit);
        AddUnitAtGridPos(toGridPosition, unit);

        OnAnyUnitMovedGridPosition?.Invoke();
        
        // Only call this event if the unit changed floor
        if (fromGridPosition.floor != toGridPosition.floor)
        {
            OnAnyUnitChangedFloor?.Invoke();
        }
    }

You are done!! You optimized you game hugely.

Privacy & Terms