// // 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. // using UnityEngine; using System.Collections; using System.Collections.Generic; namespace DigitalRuby.ThunderAndLightning { /// /// Lightning bolt spline script, helps lightning curve around path points /// public class LightningSplineScript : LightningBoltPathScriptBase { /// /// For performance, cap generations /// public const int MaxSplineGenerations = 5; /// The distance hint for each spline segment. Set to <= 0 to use the generations to determine how many spline segments to use. If > 0, it will be divided by Generations before being applied. This value is a guideline and is approximate, and not uniform on the spline. [Header("Lightning Spline Properties")] [Tooltip("The distance hint for each spline segment. Set to <= 0 to use the generations to determine how many spline segments to use. " + "If > 0, it will be divided by Generations before being applied. This value is a guideline and is approximate, and not uniform on the spline.")] public float DistancePerSegmentHint = 0.0f; private readonly List prevSourcePoints = new List(new Vector3[] { Vector3.zero }); private readonly List sourcePoints = new List(); private List savedSplinePoints = new List(); private int previousGenerations = -1; private float previousDistancePerSegment = -1.0f; private bool SourceChanged() { if (sourcePoints.Count != prevSourcePoints.Count) { return true; } for (int i = 0; i < sourcePoints.Count; i++) { if (sourcePoints[i] != prevSourcePoints[i]) { return true; } } return false; } /// /// Start /// protected override void Start() { base.Start(); } /// /// Update /// protected override void Update() { base.Update(); } /// /// Create a lightning bolt /// /// Parameters public override void CreateLightningBolt(LightningBoltParameters parameters) { if (LightningPath == null) { return; } sourcePoints.Clear(); try { foreach (GameObject obj in LightningPath) { if (obj != null) { sourcePoints.Add(obj.transform.position); } } } catch (System.NullReferenceException) { return; } if (sourcePoints.Count < PathGenerator.MinPointsForSpline) { Debug.LogError("To create spline lightning, you need a lightning path with at least " + PathGenerator.MinPointsForSpline + " points."); } else { Generations = parameters.Generations = Mathf.Clamp(Generations, 1, MaxSplineGenerations); parameters.Points.Clear(); if (previousGenerations != Generations || previousDistancePerSegment != DistancePerSegmentHint || SourceChanged()) { previousGenerations = Generations; previousDistancePerSegment = DistancePerSegmentHint; PopulateSpline(parameters.Points, sourcePoints, Generations, DistancePerSegmentHint, Camera); prevSourcePoints.Clear(); prevSourcePoints.AddRange(sourcePoints); savedSplinePoints.Clear(); savedSplinePoints.AddRange(parameters.Points); } else { parameters.Points.AddRange(savedSplinePoints); } parameters.SmoothingFactor = (parameters.Points.Count - 1) / sourcePoints.Count; base.CreateLightningBolt(parameters); } } /// /// Create a new lightning bolt parameters instance or get from cache /// /// LightningBoltParameters protected override LightningBoltParameters OnCreateParameters() { LightningBoltParameters p = LightningBoltParameters.GetOrCreateParameters(); p.Generator = LightningGeneratorPath.PathGeneratorInstance; return p; } /// /// Triggers lightning that follows a set of points, rather than the standard lightning bolt that goes between two points. /// /// Points to follow /// Whether to spline the lightning through the points or not public void Trigger(List points, bool spline) { if (points.Count < 2) { return; } Generations = Mathf.Clamp(Generations, 1, MaxSplineGenerations); LightningBoltParameters parameters = CreateParameters(); parameters.Points.Clear(); if (spline && points.Count > 3) { LightningSplineScript.PopulateSpline(parameters.Points, points, Generations, DistancePerSegmentHint, Camera); parameters.SmoothingFactor = (parameters.Points.Count - 1) / points.Count; } else { parameters.Points.AddRange(points); parameters.SmoothingFactor = 1; } base.CreateLightningBolt(parameters); CreateLightningBoltsNow(); } /// /// Populate a list of spline points from source points /// /// List to fill with spline points /// Source points /// Generations /// Distance per segment hint - if non-zero, attempts to maintain this distance between spline points. /// Optional camera public static void PopulateSpline(List splinePoints, List sourcePoints, int generations, float distancePerSegmentHit, Camera camera) { splinePoints.Clear(); PathGenerator.Is2D = (camera != null && camera.orthographic); if (distancePerSegmentHit > 0.0f) { PathGenerator.CreateSplineWithSegmentDistance(splinePoints, sourcePoints, distancePerSegmentHit / generations, false); } else { PathGenerator.CreateSpline(splinePoints, sourcePoints, sourcePoints.Count * generations * generations, false); } } } }