LightningBoltPathScript.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. //
  2. // Procedural Lightning for Unity
  3. // (c) 2015 Digital Ruby, LLC
  4. // Source code may be used for personal or commercial projects.
  5. // Source code may NOT be redistributed or sold.
  6. //
  7. // comment out to disable lightning path lines
  8. #define SHOW_LIGHTNING_PATH
  9. using UnityEngine;
  10. using System.Collections;
  11. using System.Collections.Generic;
  12. namespace DigitalRuby.ThunderAndLightning
  13. {
  14. /// <summary>
  15. /// Base lghtning bolt path script
  16. /// </summary>
  17. public abstract class LightningBoltPathScriptBase : LightningBoltPrefabScriptBase
  18. {
  19. /// <summary>The game objects to follow for the lightning path</summary>
  20. [Header("Lightning Path Properties")]
  21. [Tooltip("The game objects to follow for the lightning path")]
  22. public List<GameObject> LightningPath;
  23. private readonly List<GameObject> currentPathObjects = new List<GameObject>();
  24. #if UNITY_EDITOR
  25. [System.NonSerialized]
  26. private bool gizmosCleanedUp;
  27. [System.NonSerialized]
  28. private readonly List<GameObject> lastGizmos = new List<GameObject>();
  29. private void DoGizmoCleanup()
  30. {
  31. if (gizmosCleanedUp)
  32. {
  33. return;
  34. }
  35. gizmosCleanedUp = true;
  36. foreach (var obj in Resources.FindObjectsOfTypeAll(typeof(LightningGizmoScript)))
  37. {
  38. LightningGizmoScript s;
  39. s = obj as LightningGizmoScript;
  40. if (s == null)
  41. {
  42. GameObject gObj = obj as GameObject;
  43. if (gObj != null)
  44. {
  45. s = gObj.GetComponent<LightningGizmoScript>();
  46. }
  47. }
  48. if (s != null)
  49. {
  50. GameObject.DestroyImmediate(s, true);
  51. }
  52. }
  53. }
  54. /// <summary>
  55. /// OnDrawGizmos
  56. /// </summary>
  57. protected override void OnDrawGizmos()
  58. {
  59. base.OnDrawGizmos();
  60. DoGizmoCleanup();
  61. if (HideGizmos)
  62. {
  63. return;
  64. }
  65. #if SHOW_LIGHTNING_PATH
  66. bool noLightningPath = (LightningPath == null || LightningPath.Count == 0);
  67. #else
  68. bool noLightningPath = true;
  69. #endif
  70. // remove any objects that were taken out of the list and cleanup the gizmo script
  71. for (int i = lastGizmos.Count - 1; i >= 0; i--)
  72. {
  73. if (noLightningPath || !LightningPath.Contains(lastGizmos[i]))
  74. {
  75. if (lastGizmos[i] != null)
  76. {
  77. LightningGizmoScript s = lastGizmos[i].GetComponent<LightningGizmoScript>();
  78. if (s != null)
  79. {
  80. GameObject.DestroyImmediate(s, true);
  81. }
  82. }
  83. lastGizmos.RemoveAt(i);
  84. }
  85. }
  86. // no objects, we are done
  87. if (noLightningPath)
  88. {
  89. return;
  90. }
  91. // add gizmo scripts and draw lines as needed
  92. Vector3 gizmoPosition;
  93. HashSet<GameObject> gizmos = new HashSet<GameObject>();
  94. Vector3? previousPoint = null;
  95. LightningGizmoScript gizmoScript;
  96. lastGizmos.Clear();
  97. for (int index = 0; index < LightningPath.Count; index++)
  98. {
  99. GameObject o = LightningPath[index];
  100. if (o == null || !o.activeInHierarchy)
  101. {
  102. continue;
  103. }
  104. else if ((gizmoScript = o.GetComponent<LightningGizmoScript>()) == null)
  105. {
  106. // we need to add the gizmo script so that this object can be selectable by tapping on the lightning bolt in the scene view
  107. gizmoScript = o.AddComponent<LightningGizmoScript>();
  108. }
  109. gizmoScript.hideFlags = HideFlags.HideInInspector;
  110. // setup label based on whether we've seen this one before
  111. if (gizmos.Add(o))
  112. {
  113. gizmoScript.Label = index.ToString();
  114. }
  115. else
  116. {
  117. gizmoScript.Label += ", " + index.ToString();
  118. }
  119. gizmoScript.LightningBoltScript = this;
  120. gizmoPosition = o.transform.position;
  121. if (previousPoint != null && previousPoint.Value != gizmoPosition)
  122. {
  123. // draw a line and arrow in the proper direction
  124. Gizmos.DrawLine(previousPoint.Value, gizmoPosition);
  125. Vector3 direction = (gizmoPosition - previousPoint.Value);
  126. Vector3 center = (previousPoint.Value + gizmoPosition) * 0.5f;
  127. float arrowSize = Mathf.Min(1.0f, direction.magnitude) * 2.0f;
  128. UnityEditor.Handles.ArrowHandleCap(0, center, Quaternion.LookRotation(direction), arrowSize, EventType.Repaint);
  129. }
  130. previousPoint = gizmoPosition;
  131. lastGizmos.Add(o);
  132. }
  133. }
  134. #endif
  135. /// <summary>
  136. /// Get the game objects in the path currently - null or inactive objects are not returned
  137. /// </summary>
  138. /// <returns>List of game objects in the path</returns>
  139. protected List<GameObject> GetCurrentPathObjects()
  140. {
  141. currentPathObjects.Clear();
  142. if (LightningPath != null)
  143. {
  144. foreach (GameObject obj in LightningPath)
  145. {
  146. if (obj != null && obj.activeInHierarchy)
  147. {
  148. currentPathObjects.Add(obj);
  149. }
  150. }
  151. }
  152. return currentPathObjects;
  153. }
  154. /// <summary>
  155. /// Create lightning bolt path parameters
  156. /// </summary>
  157. /// <returns>Lightning bolt path parameters</returns>
  158. protected override LightningBoltParameters OnCreateParameters()
  159. {
  160. LightningBoltParameters p = base.OnCreateParameters();
  161. p.Generator = LightningGeneratorPath.GeneratorInstance;
  162. return p;
  163. }
  164. }
  165. /// <summary>
  166. /// Lightning bolt path script implementation
  167. /// </summary>
  168. public class LightningBoltPathScript : LightningBoltPathScriptBase
  169. {
  170. /// <summary>How fast the lightning moves through the points or objects. 1 is normal speed, 0.01 is slower, so the lightning will move slowly between the points or objects.</summary>
  171. [Tooltip("How fast the lightning moves through the points or objects. 1 is normal speed, " +
  172. "0.01 is slower, so the lightning will move slowly between the points or objects.")]
  173. [Range(0.01f, 1.0f)]
  174. public float Speed = 1.0f;
  175. /// <summary>When each new point is moved to, this can provide a random value to make the movement to the next point appear more staggered or random. Leave as 1 and 1 to have constant speed. Use a higher maximum to create more randomness.</summary>
  176. [Tooltip("Repeat when the path completes?")]
  177. [SingleLineClamp("When each new point is moved to, this can provide a random value to make the movement to " +
  178. "the next point appear more staggered or random. Leave as 1 and 1 to have constant speed. Use a higher " +
  179. "maximum to create more randomness.", 1.0, 500.0)]
  180. public RangeOfFloats SpeedIntervalRange = new RangeOfFloats { Minimum = 1.0f, Maximum = 1.0f };
  181. /// <summary>Repeat when the path completes?</summary>
  182. [Tooltip("Repeat when the path completes?")]
  183. public bool Repeat = true;
  184. private float nextInterval = 1.0f;
  185. private int nextIndex;
  186. private Vector3? lastPoint;
  187. /// <summary>
  188. /// Create a lightning bolt
  189. /// </summary>
  190. /// <param name="parameters">Parameters</param>
  191. public override void CreateLightningBolt(LightningBoltParameters parameters)
  192. {
  193. Vector3? currentPoint = null;
  194. List<GameObject> lightningPath = GetCurrentPathObjects();
  195. if (lightningPath.Count < 2)
  196. {
  197. return;
  198. }
  199. else if (nextIndex >= lightningPath.Count)
  200. {
  201. if (!Repeat)
  202. {
  203. return;
  204. }
  205. else if (lightningPath[lightningPath.Count - 1] == lightningPath[0])
  206. {
  207. nextIndex = 1;
  208. }
  209. else
  210. {
  211. nextIndex = 0;
  212. lastPoint = null;
  213. }
  214. }
  215. try
  216. {
  217. if (lastPoint == null)
  218. {
  219. lastPoint = lightningPath[nextIndex++].transform.position;
  220. }
  221. currentPoint = lightningPath[nextIndex].transform.position;
  222. if (lastPoint != null && currentPoint != null)
  223. {
  224. parameters.Start = lastPoint.Value;
  225. parameters.End = currentPoint.Value;
  226. base.CreateLightningBolt(parameters);
  227. if ((nextInterval -= Speed) <= 0.0f)
  228. {
  229. float speedValue = UnityEngine.Random.Range(SpeedIntervalRange.Minimum, SpeedIntervalRange.Maximum);
  230. nextInterval = speedValue + nextInterval;
  231. lastPoint = currentPoint;
  232. nextIndex++;
  233. }
  234. }
  235. }
  236. catch (System.NullReferenceException)
  237. {
  238. // null reference exception can happen here, in which case we bail as the list is broken until the null object gets taken out
  239. }
  240. }
  241. /// <summary>
  242. /// Reset everything
  243. /// </summary>
  244. public void Reset()
  245. {
  246. lastPoint = null;
  247. nextIndex = 0;
  248. nextInterval = 1.0f;
  249. }
  250. }
  251. }