LightningBoltPrefabScript.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  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. #define SHOW_MANUAL_WARNING
  8. using UnityEngine;
  9. using System.Collections;
  10. using System.Collections.Generic;
  11. namespace DigitalRuby.ThunderAndLightning
  12. {
  13. /// <summary>
  14. /// Lightning bolt prefab base script
  15. /// </summary>
  16. public abstract class LightningBoltPrefabScriptBase : LightningBoltScript
  17. {
  18. #if DEBUG && SHOW_MANUAL_WARNING
  19. private static bool showedManualWarning;
  20. #endif
  21. private readonly List<LightningBoltParameters> batchParameters = new List<LightningBoltParameters>();
  22. private readonly System.Random random = new System.Random();
  23. /// <summary>Reduces the probability that additional bolts from CountRange will actually happen (0 - 1).</summary>
  24. [Header("Lightning Spawn Properties")]
  25. [SingleLineClamp("How long to wait before creating another round of lightning bolts in seconds", 0.001, double.MaxValue)]
  26. public RangeOfFloats IntervalRange = new RangeOfFloats { Minimum = 0.05f, Maximum = 0.1f };
  27. /// <summary>How many lightning bolts to emit for each interval</summary>
  28. [SingleLineClamp("How many lightning bolts to emit for each interval", 0.0, 100.0)]
  29. public RangeOfIntegers CountRange = new RangeOfIntegers { Minimum = 1, Maximum = 1 };
  30. /// <summary>Reduces the probability that additional bolts from CountRange will actually happen (0 - 1).</summary>
  31. [Tooltip("Reduces the probability that additional bolts from CountRange will actually happen (0 - 1).")]
  32. [Range(0.0f, 1.0f)]
  33. public float CountProbabilityModifier = 1.0f;
  34. /// <summary>Delay in seconds (range) before each additional lightning bolt in count range is emitted</summary>
  35. public RangeOfFloats DelayRange = new RangeOfFloats { Minimum = 0.0f, Maximum = 0.0f };
  36. /// <summary>For each bolt emitted, how long should it stay in seconds</summary>
  37. [SingleLineClamp("For each bolt emitted, how long should it stay in seconds", 0.01, 10.0)]
  38. public RangeOfFloats DurationRange = new RangeOfFloats { Minimum = 0.06f, Maximum = 0.12f };
  39. /// <summary>How long (in seconds) this game object should live before destroying itself. Leave as 0 for infinite.</summary>
  40. [Header("Lightning Appearance Properties")]
  41. [SingleLineClamp("The trunk width range in unity units (x = min, y = max)", 0.0001, 100.0)]
  42. public RangeOfFloats TrunkWidthRange = new RangeOfFloats { Minimum = 0.1f, Maximum = 0.2f };
  43. /// <summary>How long (in seconds) this game object should live before destroying itself. Leave as 0 for infinite.</summary>
  44. [Tooltip("How long (in seconds) this game object should live before destroying itself. Leave as 0 for infinite.")]
  45. [Range(0.0f, 1000.0f)]
  46. public float LifeTime = 0.0f;
  47. /// <summary>Generations (1 - 8, higher makes more detailed but more expensive lightning)</summary>
  48. [Tooltip("Generations (1 - 8, higher makes more detailed but more expensive lightning)")]
  49. [Range(1, 8)]
  50. public int Generations = 6;
  51. /// <summary>The chaos factor that determines how far the lightning main trunk can spread out, higher numbers spread out more. 0 - 1.</summary>
  52. [Tooltip("The chaos factor that determines how far the lightning main trunk can spread out, higher numbers spread out more. 0 - 1.")]
  53. [Range(0.0f, 1.0f)]
  54. public float ChaosFactor = 0.075f;
  55. /// <summary>The chaos factor that determines how far the forks of the lightning can spread out, higher numbers spread out more. 0 - 1.</summary>
  56. [Tooltip("The chaos factor that determines how far the forks of the lightning can spread out, higher numbers spread out more. 0 - 1.")]
  57. [Range(0.0f, 1.0f)]
  58. public float ChaosFactorForks = 0.095f;
  59. /// <summary>Intensity of the lightning</summary>
  60. [Tooltip("Intensity of the lightning")]
  61. [Range(0.0f, 10.0f)]
  62. public float Intensity = 1.0f;
  63. /// <summary>The intensity of the glow</summary>
  64. [Tooltip("The intensity of the glow")]
  65. [Range(0.0f, 10.0f)]
  66. public float GlowIntensity = 0.1f;
  67. /// <summary>The width multiplier for the glow, 0 - 64</summary>
  68. [Tooltip("The width multiplier for the glow, 0 - 64")]
  69. [Range(0.0f, 64.0f)]
  70. public float GlowWidthMultiplier = 4.0f;
  71. /// <summary>What percent of time the lightning should fade in and out. For example, 0.15 fades in 15% of the time and fades out 15% of the time, with full visibility 70% of the time.</summary>
  72. [Tooltip("What percent of time the lightning should fade in and out. For example, 0.15 fades in 15% of the time and fades out 15% of the time, with full visibility 70% of the time.")]
  73. [Range(0.0f, 0.5f)]
  74. public float FadePercent = 0.15f;
  75. /// <summary>Modify the duration of lightning fade in.</summary>
  76. [Tooltip("Modify the duration of lightning fade in.")]
  77. [Range(0.0f, 1.0f)]
  78. public float FadeInMultiplier = 1.0f;
  79. /// <summary>Modify the duration of fully lit lightning.</summary>
  80. [Tooltip("Modify the duration of fully lit lightning.")]
  81. [Range(0.0f, 1.0f)]
  82. public float FadeFullyLitMultiplier = 1.0f;
  83. /// <summary>Modify the duration of lightning fade out.</summary>
  84. [Tooltip("Modify the duration of lightning fade out.")]
  85. [Range(0.0f, 1.0f)]
  86. public float FadeOutMultiplier = 1.0f;
  87. /// <summary>0 - 1, how slowly the lightning should grow. 0 for instant, 1 for slow.</summary>
  88. [Tooltip("0 - 1, how slowly the lightning should grow. 0 for instant, 1 for slow.")]
  89. [Range(0.0f, 1.0f)]
  90. public float GrowthMultiplier;
  91. /// <summary>How much smaller the lightning should get as it goes towards the end of the bolt. For example, 0.5 will make the end 50% the width of the start.</summary>
  92. [Tooltip("How much smaller the lightning should get as it goes towards the end of the bolt. For example, 0.5 will make the end 50% the width of the start.")]
  93. [Range(0.0f, 10.0f)]
  94. public float EndWidthMultiplier = 0.5f;
  95. /// <summary>How forked should the lightning be? (0 - 1, 0 for none, 1 for lots of forks)</summary>
  96. [Tooltip("How forked should the lightning be? (0 - 1, 0 for none, 1 for lots of forks)")]
  97. [Range(0.0f, 1.0f)]
  98. public float Forkedness = 0.25f;
  99. /// <summary>Minimum distance multiplier for forks</summary>
  100. [Range(0.0f, 10.0f)]
  101. [Tooltip("Minimum distance multiplier for forks")]
  102. public float ForkLengthMultiplier = 0.6f;
  103. /// <summary>Fork distance multiplier variance. Random range of 0 to n that is added to Fork Length Multiplier.</summary>
  104. [Range(0.0f, 10.0f)]
  105. [Tooltip("Fork distance multiplier variance. Random range of 0 to n that is added to Fork Length Multiplier.")]
  106. public float ForkLengthVariance = 0.2f;
  107. /// <summary>Forks have their EndWidthMultiplier multiplied by this value</summary>
  108. [Tooltip("Forks have their EndWidthMultiplier multiplied by this value")]
  109. [Range(0.0f, 10.0f)]
  110. public float ForkEndWidthMultiplier = 1.0f;
  111. /// <summary>Light parameters</summary>
  112. [Header("Lightning Light Properties")]
  113. [Tooltip("Light parameters")]
  114. public LightningLightParameters LightParameters;
  115. /// <summary>Maximum number of lights that can be created per batch of lightning</summary>
  116. [Tooltip("Maximum number of lights that can be created per batch of lightning")]
  117. [Range(0, 64)]
  118. public int MaximumLightsPerBatch = 8;
  119. /// <summary>Manual or automatic mode. Manual requires that you call the Trigger method in script. Automatic uses the interval to create lightning continuously.</summary>
  120. [Header("Lightning Trigger Type")]
  121. [Tooltip("Manual or automatic mode. Manual requires that you call the Trigger method in script. Automatic uses the interval to create lightning continuously.")]
  122. public bool ManualMode;
  123. /// <summary>Turns lightning into automatic mode for this number of seconds, then puts it into manual mode.</summary>
  124. [Tooltip("Turns lightning into automatic mode for this number of seconds, then puts it into manual mode.")]
  125. [Range(0.0f, 120.0f)]
  126. public float AutomaticModeSeconds;
  127. /// <summary>Custom handler to modify the transform of each lightning bolt, useful if it will be alive longer than a few frames and needs to scale and rotate based on the position of other objects.</summary>
  128. [Header("Lightning custom transform handler")]
  129. [Tooltip("Custom handler to modify the transform of each lightning bolt, useful if it will be alive longer than a few frames and needs to scale and rotate based " +
  130. "on the position of other objects.")]
  131. public LightningCustomTransformDelegate CustomTransformHandler;
  132. /// <summary>
  133. /// Override the random generator for the bolts
  134. /// </summary>
  135. public System.Random RandomOverride { get; set; }
  136. private float nextLightningTimestamp;
  137. private float lifeTimeRemaining;
  138. private void CalculateNextLightningTimestamp(float offset)
  139. {
  140. nextLightningTimestamp = (IntervalRange.Minimum == IntervalRange.Maximum ? IntervalRange.Minimum : offset + IntervalRange.Random());
  141. }
  142. private void CustomTransform(LightningCustomTransformStateInfo state)
  143. {
  144. if (CustomTransformHandler != null)
  145. {
  146. CustomTransformHandler.Invoke(state);
  147. }
  148. }
  149. private void CallLightning()
  150. {
  151. CallLightning(null, null);
  152. }
  153. private void CallLightning(Vector3? start, Vector3? end)
  154. {
  155. System.Random r = (RandomOverride ?? random);
  156. int count = CountRange.Random(r);
  157. for (int i = 0; i < count; i++)
  158. {
  159. LightningBoltParameters p = CreateParameters();
  160. if (CountProbabilityModifier >= 0.9999f || i == 0 || (float)p.Random.NextDouble() <= CountProbabilityModifier)
  161. {
  162. p.CustomTransform = (CustomTransformHandler == null ? (System.Action<LightningCustomTransformStateInfo>)null : CustomTransform);
  163. CreateLightningBolt(p);
  164. if (start != null)
  165. {
  166. p.Start = start.Value;
  167. }
  168. if (end != null)
  169. {
  170. p.End = end.Value;
  171. }
  172. }
  173. else
  174. {
  175. LightningBoltParameters.ReturnParametersToCache(p);
  176. }
  177. }
  178. CreateLightningBoltsNow();
  179. }
  180. /// <summary>
  181. /// Create lightning bolts immediately
  182. /// </summary>
  183. protected void CreateLightningBoltsNow()
  184. {
  185. int tmp = LightningBolt.MaximumLightsPerBatch;
  186. LightningBolt.MaximumLightsPerBatch = MaximumLightsPerBatch;
  187. CreateLightningBolts(batchParameters);
  188. LightningBolt.MaximumLightsPerBatch = tmp;
  189. batchParameters.Clear();
  190. }
  191. /// <summary>
  192. /// Populate lightning bolt parameters from script
  193. /// </summary>
  194. /// <param name="parameters">Parameters to populate</param>
  195. protected override void PopulateParameters(LightningBoltParameters parameters)
  196. {
  197. base.PopulateParameters(parameters);
  198. parameters.RandomOverride = RandomOverride;
  199. float duration = DurationRange.Random(parameters.Random);
  200. float trunkWidth = TrunkWidthRange.Random(parameters.Random);
  201. parameters.Generations = Generations;
  202. parameters.LifeTime = duration;
  203. parameters.ChaosFactor = ChaosFactor;
  204. parameters.ChaosFactorForks = ChaosFactorForks;
  205. parameters.TrunkWidth = trunkWidth;
  206. parameters.Intensity = Intensity;
  207. parameters.GlowIntensity = GlowIntensity;
  208. parameters.GlowWidthMultiplier = GlowWidthMultiplier;
  209. parameters.Forkedness = Forkedness;
  210. parameters.ForkLengthMultiplier = ForkLengthMultiplier;
  211. parameters.ForkLengthVariance = ForkLengthVariance;
  212. parameters.FadePercent = FadePercent;
  213. parameters.FadeInMultiplier = FadeInMultiplier;
  214. parameters.FadeOutMultiplier = FadeOutMultiplier;
  215. parameters.FadeFullyLitMultiplier = FadeFullyLitMultiplier;
  216. parameters.GrowthMultiplier = GrowthMultiplier;
  217. parameters.EndWidthMultiplier = EndWidthMultiplier;
  218. parameters.ForkEndWidthMultiplier = ForkEndWidthMultiplier;
  219. parameters.DelayRange = DelayRange;
  220. parameters.LightParameters = LightParameters;
  221. }
  222. /// <summary>
  223. /// Start
  224. /// </summary>
  225. protected override void Start()
  226. {
  227. base.Start();
  228. CalculateNextLightningTimestamp(0.0f);
  229. lifeTimeRemaining = (LifeTime <= 0.0f ? float.MaxValue : LifeTime);
  230. }
  231. /// <summary>
  232. /// Update
  233. /// </summary>
  234. protected override void Update()
  235. {
  236. base.Update();
  237. if (Time.timeScale <= 0.0f)
  238. {
  239. return;
  240. }
  241. else if ((lifeTimeRemaining -= LightningBoltScript.DeltaTime) < 0.0f)
  242. {
  243. GameObject.Destroy(gameObject);
  244. }
  245. if ((nextLightningTimestamp -= LightningBoltScript.DeltaTime) <= 0.0f)
  246. {
  247. CalculateNextLightningTimestamp(nextLightningTimestamp);
  248. if (ManualMode)
  249. {
  250. #if DEBUG && SHOW_MANUAL_WARNING
  251. if (!showedManualWarning)
  252. {
  253. showedManualWarning = true;
  254. Debug.LogWarning("Lightning bolt script is in manual mode. Trigger method must be called.");
  255. }
  256. #endif
  257. }
  258. else
  259. {
  260. CallLightning();
  261. }
  262. }
  263. if (AutomaticModeSeconds > 0.0f)
  264. {
  265. AutomaticModeSeconds = Mathf.Max(0.0f, AutomaticModeSeconds - LightningBoltScript.DeltaTime);
  266. ManualMode = (AutomaticModeSeconds == 0.0f);
  267. }
  268. }
  269. /// <summary>
  270. /// OnDrawGizmos
  271. /// </summary>
  272. protected virtual void OnDrawGizmos()
  273. {
  274. #if UNITY_EDITOR
  275. if (!HideGizmos)
  276. {
  277. Gizmos.color = Color.white;
  278. UnityEditor.Handles.color = Color.white;
  279. }
  280. #endif
  281. }
  282. /// <summary>
  283. /// Derived classes can override and can call this base class method last to add the lightning bolt parameters to the list of batched lightning bolts
  284. /// </summary>
  285. /// <param name="p">Lightning bolt creation parameters</param>
  286. public override void CreateLightningBolt(LightningBoltParameters p)
  287. {
  288. batchParameters.Add(p);
  289. // do not call the base method, we batch up and use CreateLightningBolts
  290. }
  291. /// <summary>
  292. /// Manually trigger the lightning once
  293. /// </summary>
  294. public void Trigger()
  295. {
  296. Trigger(-1.0f);
  297. }
  298. /// <summary>
  299. /// Manually trigger lightning
  300. /// </summary>
  301. /// <param name="seconds">Number of seconds to turn on automatic lightning for (sets AutomaticModeSeconds).</param>
  302. public void Trigger(float seconds)
  303. {
  304. CallLightning();
  305. if (seconds >= 0.0f)
  306. {
  307. AutomaticModeSeconds = Mathf.Max(0.0f, seconds);
  308. }
  309. }
  310. /// <summary>
  311. /// Manually trigger lightning
  312. /// </summary>
  313. /// <param name="start">Start position</param>
  314. /// <param name="end">End position</param>
  315. public void Trigger(Vector3? start, Vector3? end)
  316. {
  317. CallLightning(start, end);
  318. }
  319. }
  320. /// <summary>
  321. /// Lightning bolt prefab script, base script to create lightning using a prefab
  322. /// </summary>
  323. public class LightningBoltPrefabScript : LightningBoltPrefabScriptBase
  324. {
  325. /// <summary>The source game object, can be null</summary>
  326. [Header("Start/end")]
  327. [Tooltip("The source game object, can be null")]
  328. public GameObject Source;
  329. /// <summary>The destination game object, can be null</summary>
  330. [Tooltip("The destination game object, can be null")]
  331. public GameObject Destination;
  332. /// <summary>X, Y and Z for variance from the start point. Use positive values.</summary>
  333. [Tooltip("X, Y and Z for variance from the start point. Use positive values.")]
  334. public Vector3 StartVariance;
  335. /// <summary>X, Y and Z for variance from the end point. Use positive values.</summary>
  336. [Tooltip("X, Y and Z for variance from the end point. Use positive values.")]
  337. public Vector3 EndVariance;
  338. #if UNITY_EDITOR
  339. /// <summary>
  340. /// OnDrawGizmos
  341. /// </summary>
  342. protected override void OnDrawGizmos()
  343. {
  344. base.OnDrawGizmos();
  345. if (HideGizmos)
  346. {
  347. return;
  348. }
  349. else if (Source != null)
  350. {
  351. Gizmos.DrawIcon(Source.transform.position, "LightningPathStart.png");
  352. }
  353. if (Destination != null)
  354. {
  355. Gizmos.DrawIcon(Destination.transform.position, "LightningPathNext.png");
  356. }
  357. if (Source != null && Destination != null)
  358. {
  359. Gizmos.DrawLine(Source.transform.position, Destination.transform.position);
  360. Vector3 direction = (Destination.transform.position - Source.transform.position);
  361. Vector3 center = (Source.transform.position + Destination.transform.position) * 0.5f;
  362. float arrowSize = Mathf.Min(2.0f, direction.magnitude) * 2.0f;
  363. UnityEditor.Handles.ArrowHandleCap(0, center, Quaternion.LookRotation(direction), arrowSize, EventType.Repaint);
  364. }
  365. }
  366. #endif
  367. /// <summary>
  368. /// Create a lightning bolt
  369. /// </summary>
  370. /// <param name="parameters">Parameters</param>
  371. public override void CreateLightningBolt(LightningBoltParameters parameters)
  372. {
  373. parameters.Start = (Source == null ? parameters.Start : Source.transform.position);
  374. parameters.End = (Destination == null ? parameters.End : Destination.transform.position);
  375. parameters.StartVariance = StartVariance;
  376. parameters.EndVariance = EndVariance;
  377. base.CreateLightningBolt(parameters);
  378. }
  379. }
  380. }