//
// Procedural Lightning for Unity
// (c) 2015 Digital Ruby, LLC
// Source code may be used for personal or commercial projects.
// Source code may NOT be redistributed or sold.
//
// comment out to disable lightning path lines
#define SHOW_LIGHTNING_PATH
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace DigitalRuby.ThunderAndLightning
{
///
/// Base lghtning bolt path script
///
public abstract class LightningBoltPathScriptBase : LightningBoltPrefabScriptBase
{
/// The game objects to follow for the lightning path
[Header("Lightning Path Properties")]
[Tooltip("The game objects to follow for the lightning path")]
public List LightningPath;
private readonly List currentPathObjects = new List();
#if UNITY_EDITOR
[System.NonSerialized]
private bool gizmosCleanedUp;
[System.NonSerialized]
private readonly List lastGizmos = new List();
private void DoGizmoCleanup()
{
if (gizmosCleanedUp)
{
return;
}
gizmosCleanedUp = true;
foreach (var obj in Resources.FindObjectsOfTypeAll(typeof(LightningGizmoScript)))
{
LightningGizmoScript s;
s = obj as LightningGizmoScript;
if (s == null)
{
GameObject gObj = obj as GameObject;
if (gObj != null)
{
s = gObj.GetComponent();
}
}
if (s != null)
{
GameObject.DestroyImmediate(s, true);
}
}
}
///
/// OnDrawGizmos
///
protected override void OnDrawGizmos()
{
base.OnDrawGizmos();
DoGizmoCleanup();
if (HideGizmos)
{
return;
}
#if SHOW_LIGHTNING_PATH
bool noLightningPath = (LightningPath == null || LightningPath.Count == 0);
#else
bool noLightningPath = true;
#endif
// remove any objects that were taken out of the list and cleanup the gizmo script
for (int i = lastGizmos.Count - 1; i >= 0; i--)
{
if (noLightningPath || !LightningPath.Contains(lastGizmos[i]))
{
if (lastGizmos[i] != null)
{
LightningGizmoScript s = lastGizmos[i].GetComponent();
if (s != null)
{
GameObject.DestroyImmediate(s, true);
}
}
lastGizmos.RemoveAt(i);
}
}
// no objects, we are done
if (noLightningPath)
{
return;
}
// add gizmo scripts and draw lines as needed
Vector3 gizmoPosition;
HashSet gizmos = new HashSet();
Vector3? previousPoint = null;
LightningGizmoScript gizmoScript;
lastGizmos.Clear();
for (int index = 0; index < LightningPath.Count; index++)
{
GameObject o = LightningPath[index];
if (o == null || !o.activeInHierarchy)
{
continue;
}
else if ((gizmoScript = o.GetComponent()) == null)
{
// we need to add the gizmo script so that this object can be selectable by tapping on the lightning bolt in the scene view
gizmoScript = o.AddComponent();
}
gizmoScript.hideFlags = HideFlags.HideInInspector;
// setup label based on whether we've seen this one before
if (gizmos.Add(o))
{
gizmoScript.Label = index.ToString();
}
else
{
gizmoScript.Label += ", " + index.ToString();
}
gizmoScript.LightningBoltScript = this;
gizmoPosition = o.transform.position;
if (previousPoint != null && previousPoint.Value != gizmoPosition)
{
// draw a line and arrow in the proper direction
Gizmos.DrawLine(previousPoint.Value, gizmoPosition);
Vector3 direction = (gizmoPosition - previousPoint.Value);
Vector3 center = (previousPoint.Value + gizmoPosition) * 0.5f;
float arrowSize = Mathf.Min(1.0f, direction.magnitude) * 2.0f;
UnityEditor.Handles.ArrowHandleCap(0, center, Quaternion.LookRotation(direction), arrowSize, EventType.Repaint);
}
previousPoint = gizmoPosition;
lastGizmos.Add(o);
}
}
#endif
///
/// Get the game objects in the path currently - null or inactive objects are not returned
///
/// List of game objects in the path
protected List GetCurrentPathObjects()
{
currentPathObjects.Clear();
if (LightningPath != null)
{
foreach (GameObject obj in LightningPath)
{
if (obj != null && obj.activeInHierarchy)
{
currentPathObjects.Add(obj);
}
}
}
return currentPathObjects;
}
///
/// Create lightning bolt path parameters
///
/// Lightning bolt path parameters
protected override LightningBoltParameters OnCreateParameters()
{
LightningBoltParameters p = base.OnCreateParameters();
p.Generator = LightningGeneratorPath.GeneratorInstance;
return p;
}
}
///
/// Lightning bolt path script implementation
///
public class LightningBoltPathScript : LightningBoltPathScriptBase
{
/// 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.
[Tooltip("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.")]
[Range(0.01f, 1.0f)]
public float Speed = 1.0f;
/// 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.
[Tooltip("Repeat when the path completes?")]
[SingleLineClamp("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.", 1.0, 500.0)]
public RangeOfFloats SpeedIntervalRange = new RangeOfFloats { Minimum = 1.0f, Maximum = 1.0f };
/// Repeat when the path completes?
[Tooltip("Repeat when the path completes?")]
public bool Repeat = true;
private float nextInterval = 1.0f;
private int nextIndex;
private Vector3? lastPoint;
///
/// Create a lightning bolt
///
/// Parameters
public override void CreateLightningBolt(LightningBoltParameters parameters)
{
Vector3? currentPoint = null;
List lightningPath = GetCurrentPathObjects();
if (lightningPath.Count < 2)
{
return;
}
else if (nextIndex >= lightningPath.Count)
{
if (!Repeat)
{
return;
}
else if (lightningPath[lightningPath.Count - 1] == lightningPath[0])
{
nextIndex = 1;
}
else
{
nextIndex = 0;
lastPoint = null;
}
}
try
{
if (lastPoint == null)
{
lastPoint = lightningPath[nextIndex++].transform.position;
}
currentPoint = lightningPath[nextIndex].transform.position;
if (lastPoint != null && currentPoint != null)
{
parameters.Start = lastPoint.Value;
parameters.End = currentPoint.Value;
base.CreateLightningBolt(parameters);
if ((nextInterval -= Speed) <= 0.0f)
{
float speedValue = UnityEngine.Random.Range(SpeedIntervalRange.Minimum, SpeedIntervalRange.Maximum);
nextInterval = speedValue + nextInterval;
lastPoint = currentPoint;
nextIndex++;
}
}
}
catch (System.NullReferenceException)
{
// null reference exception can happen here, in which case we bail as the list is broken until the null object gets taken out
}
}
///
/// Reset everything
///
public void Reset()
{
lastPoint = null;
nextIndex = 0;
nextInterval = 1.0f;
}
}
}