CullingHandler.cs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. // --------------------------------------------------------------------------------------------------------------------
  2. // <copyright file="CullingHandler.cs" company="Exit Games GmbH">
  3. // Part of: Photon Unity Utilities,
  4. // </copyright>
  5. // <summary>
  6. // Handles the network culling.
  7. // </summary>
  8. // <author>developer@exitgames.com</author>
  9. // --------------------------------------------------------------------------------------------------------------------
  10. using System.Collections.Generic;
  11. using UnityEngine;
  12. using Photon.Pun;
  13. namespace Photon.Pun.UtilityScripts
  14. {
  15. using ExitGames.Client.Photon;
  16. /// <summary>
  17. /// Handles the network culling.
  18. /// </summary>
  19. [RequireComponent(typeof(PhotonView))]
  20. public class CullingHandler : MonoBehaviour, IPunObservable
  21. {
  22. #region VARIABLES
  23. private int orderIndex;
  24. private CullArea cullArea;
  25. private List<byte> previousActiveCells, activeCells;
  26. private PhotonView pView;
  27. private Vector3 lastPosition, currentPosition;
  28. // used to limit the number of UpdateInterestGroups calls per second (there is no use to change groups more than a few times per second, even if the Culling algorithm makes it look like that)
  29. private float timeSinceUpdate;
  30. // see timeSinceUpdate
  31. private float timeBetweenUpdatesMin = 0.33f;
  32. #endregion
  33. #region UNITY_FUNCTIONS
  34. /// <summary>
  35. /// Gets references to the PhotonView component and the cull area game object.
  36. /// </summary>
  37. private void OnEnable()
  38. {
  39. if (this.pView == null)
  40. {
  41. this.pView = GetComponent<PhotonView>();
  42. if (!this.pView.IsMine)
  43. {
  44. return;
  45. }
  46. }
  47. if (this.cullArea == null)
  48. {
  49. this.cullArea = FindObjectOfType<CullArea>();
  50. }
  51. this.previousActiveCells = new List<byte>(0);
  52. this.activeCells = new List<byte>(0);
  53. this.currentPosition = this.lastPosition = transform.position;
  54. }
  55. /// <summary>
  56. /// Initializes the right interest group or prepares the permanent change of the interest Group of the PhotonView component.
  57. /// </summary>
  58. private void Start()
  59. {
  60. if (!this.pView.IsMine)
  61. {
  62. return;
  63. }
  64. if (PhotonNetwork.InRoom)
  65. {
  66. if (this.cullArea.NumberOfSubdivisions == 0)
  67. {
  68. this.pView.Group = this.cullArea.FIRST_GROUP_ID;
  69. PhotonNetwork.SetInterestGroups(this.cullArea.FIRST_GROUP_ID, true);
  70. }
  71. else
  72. {
  73. // This is used to continuously update the active group.
  74. this.pView.ObservedComponents.Add(this);
  75. }
  76. }
  77. }
  78. /// <summary>
  79. /// Checks if the player has moved previously and updates the interest groups if necessary.
  80. /// </summary>
  81. private void Update()
  82. {
  83. if (!this.pView.IsMine)
  84. {
  85. return;
  86. }
  87. // we'll limit how often this update may run at all (to avoid too frequent changes and flooding the server with SetInterestGroups calls)
  88. this.timeSinceUpdate += Time.deltaTime;
  89. if (this.timeSinceUpdate < this.timeBetweenUpdatesMin)
  90. {
  91. return;
  92. }
  93. this.lastPosition = this.currentPosition;
  94. this.currentPosition = transform.position;
  95. // This is a simple position comparison of the current and the previous position.
  96. // When using Network Culling in a bigger project keep in mind that there might
  97. // be more transform-related options, e.g. the rotation, or other options to check.
  98. if (this.currentPosition != this.lastPosition)
  99. {
  100. if (this.HaveActiveCellsChanged())
  101. {
  102. this.UpdateInterestGroups();
  103. this.timeSinceUpdate = 0;
  104. }
  105. }
  106. }
  107. /// <summary>
  108. /// Drawing informations.
  109. /// </summary>
  110. private void OnGUI()
  111. {
  112. if (!this.pView.IsMine)
  113. {
  114. return;
  115. }
  116. string subscribedAndActiveCells = "Inside cells:\n";
  117. string subscribedCells = "Subscribed cells:\n";
  118. for (int index = 0; index < this.activeCells.Count; ++index)
  119. {
  120. if (index <= this.cullArea.NumberOfSubdivisions)
  121. {
  122. subscribedAndActiveCells += this.activeCells[index] + " | ";
  123. }
  124. subscribedCells += this.activeCells[index] + " | ";
  125. }
  126. GUI.Label(new Rect(20.0f, Screen.height - 120.0f, 200.0f, 40.0f), "<color=white>PhotonView Group: " + this.pView.Group + "</color>", new GUIStyle() { alignment = TextAnchor.UpperLeft, fontSize = 16 });
  127. GUI.Label(new Rect(20.0f, Screen.height - 100.0f, 200.0f, 40.0f), "<color=white>" + subscribedAndActiveCells + "</color>", new GUIStyle() { alignment = TextAnchor.UpperLeft, fontSize = 16 });
  128. GUI.Label(new Rect(20.0f, Screen.height - 60.0f, 200.0f, 40.0f), "<color=white>" + subscribedCells + "</color>", new GUIStyle() { alignment = TextAnchor.UpperLeft, fontSize = 16 });
  129. }
  130. #endregion
  131. /// <summary>
  132. /// Checks if the previously active cells have changed.
  133. /// </summary>
  134. /// <returns>True if the previously active cells have changed and false otherwise.</returns>
  135. private bool HaveActiveCellsChanged()
  136. {
  137. if (this.cullArea.NumberOfSubdivisions == 0)
  138. {
  139. return false;
  140. }
  141. this.previousActiveCells = new List<byte>(this.activeCells);
  142. this.activeCells = this.cullArea.GetActiveCells(transform.position);
  143. // If the player leaves the area we insert the whole area itself as an active cell.
  144. // This can be removed if it is sure that the player is not able to leave the area.
  145. while (this.activeCells.Count <= this.cullArea.NumberOfSubdivisions)
  146. {
  147. this.activeCells.Add(this.cullArea.FIRST_GROUP_ID);
  148. }
  149. if (this.activeCells.Count != this.previousActiveCells.Count)
  150. {
  151. return true;
  152. }
  153. if (this.activeCells[this.cullArea.NumberOfSubdivisions] != this.previousActiveCells[this.cullArea.NumberOfSubdivisions])
  154. {
  155. return true;
  156. }
  157. return false;
  158. }
  159. /// <summary>
  160. /// Unsubscribes from old and subscribes to new interest groups.
  161. /// </summary>
  162. private void UpdateInterestGroups()
  163. {
  164. List<byte> disable = new List<byte>(0);
  165. foreach (byte groupId in this.previousActiveCells)
  166. {
  167. if (!this.activeCells.Contains(groupId))
  168. {
  169. disable.Add(groupId);
  170. }
  171. }
  172. PhotonNetwork.SetInterestGroups(disable.ToArray(), this.activeCells.ToArray());
  173. }
  174. #region IPunObservable implementation
  175. /// <summary>
  176. /// This time OnPhotonSerializeView is not used to send or receive any kind of data.
  177. /// It is used to change the currently active group of the PhotonView component, making it work together with PUN more directly.
  178. /// Keep in mind that this function is only executed, when there is at least one more player in the room.
  179. /// </summary>
  180. public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
  181. {
  182. // If the player leaves the area we insert the whole area itself as an active cell.
  183. // This can be removed if it is sure that the player is not able to leave the area.
  184. while (this.activeCells.Count <= this.cullArea.NumberOfSubdivisions)
  185. {
  186. this.activeCells.Add(this.cullArea.FIRST_GROUP_ID);
  187. }
  188. if (this.cullArea.NumberOfSubdivisions == 1)
  189. {
  190. this.orderIndex = (++this.orderIndex % this.cullArea.SUBDIVISION_FIRST_LEVEL_ORDER.Length);
  191. this.pView.Group = this.activeCells[this.cullArea.SUBDIVISION_FIRST_LEVEL_ORDER[this.orderIndex]];
  192. }
  193. else if (this.cullArea.NumberOfSubdivisions == 2)
  194. {
  195. this.orderIndex = (++this.orderIndex % this.cullArea.SUBDIVISION_SECOND_LEVEL_ORDER.Length);
  196. this.pView.Group = this.activeCells[this.cullArea.SUBDIVISION_SECOND_LEVEL_ORDER[this.orderIndex]];
  197. }
  198. else if (this.cullArea.NumberOfSubdivisions == 3)
  199. {
  200. this.orderIndex = (++this.orderIndex % this.cullArea.SUBDIVISION_THIRD_LEVEL_ORDER.Length);
  201. this.pView.Group = this.activeCells[this.cullArea.SUBDIVISION_THIRD_LEVEL_ORDER[this.orderIndex]];
  202. }
  203. }
  204. #endregion
  205. }
  206. }