// Copyright (c) Pixel Crushers. All rights reserved. using UnityEngine; using System; using System.Collections; using System.Collections.Generic; namespace PixelCrushers { /// /// Implements a Level of Detail (LOD) system according to distance from the player. /// Add this component to any GameObject with script(s) that implement a method /// named `OnLOD(int)`. It's the script's responsibility to handle the message /// accordingly. For example, an AI script could reduce the frequency of perception /// checks as the LOD number increases. /// [AddComponentMenu("")] // Use wrapper instead. public class LODManager : MonoBehaviour { [Serializable] public class LOD { [Tooltip("The minimum distance for this LOD.")] [SerializeField] private float m_minDistance = 0; [Tooltip("The max distance for this LOD.")] [SerializeField] private float m_maxDistance = Mathf.Infinity; /// /// The minimum distance for this LOD. /// public float minDistance { get { return m_minDistance; } set { m_minDistance = value; } } /// /// The max distance for this LOD. /// public float maxDistance { get { return m_maxDistance; } set { m_maxDistance = value; } } public bool Contains(float distance) { return (minDistance <= distance && distance <= maxDistance); } } [Tooltip("The LODs (levels of detail).")] [SerializeField] private LOD[] m_levels; [Tooltip("The frequency at which to check distance from the player and update the current LOD if necessary.")] [SerializeField] private float m_monitorFrequency = 5f; /// /// The LODs. /// public LOD[] levels { get { return m_levels; } set { m_levels = value; } } /// /// The frequency at which to check distance from the player and update /// the current LOD if necessary. /// public float monitorFrequency { get { return m_monitorFrequency; } set { m_monitorFrequency = value; } } /// /// Gets or sets the player's transform. The Start method assigns the /// GameObject tagged "Player" to this property. /// /// The player. public Transform player { get; set; } private int m_currentLevel = 0; private WaitForSeconds m_currentWaitForSeconds = new WaitForSeconds(60); // Cache to reduce garbage collection. private float m_currentWaitForSecondsValue = 0; private void Start() { FindPlayer(); StartCoroutine(MonitorLOD()); } /// /// Assigns the GameObject tagged "Player" to the player property. /// public void FindPlayer() { var go = GameObject.FindWithTag("Player"); player = (go != null) ? go.transform : null; } private IEnumerator MonitorLOD() { yield return new WaitForSeconds(UnityEngine.Random.value); // Stagger GameObjects. m_currentWaitForSecondsValue = -1; while (true) { CheckLOD(); if (monitorFrequency != m_currentWaitForSecondsValue) { m_currentWaitForSecondsValue = monitorFrequency; m_currentWaitForSeconds = new WaitForSeconds(monitorFrequency); } yield return m_currentWaitForSeconds; } } /// /// Updates the current level of detail based on distance from the player and /// the component's LOD ranges. /// public void CheckLOD() { if (player == null || levels == null || levels.Length == 0) return; float distance = Vector3.Distance(transform.position, player.position); if (levels[m_currentLevel].Contains(distance)) return; for (int level = 0; level < levels.Length; level++) { if (levels[level].Contains(distance)) { m_currentLevel = level; BroadcastMessage("OnLOD", level, SendMessageOptions.DontRequireReceiver); return; } } } /// /// For optional UtopiaWorx Zone Controller integration. /// /// The properties that Zone Controller can control. public static List ZonePluginActivator() { List controllable = new List(); controllable.Add("monitorFrequency|System.Single|0|99999|1|The frequency at which to check distance from the player and update the current LOD if necessary."); return controllable; } } }