| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342 |
- using System.Collections;
- using System.Collections.Generic;
- using System.Linq;
- using UnityEngine;
- public class SplineDraggableContext
- {
- public GameObject Dragable;
- public float Time;
- public bool Invalid;
- public GameObject DependentObject;
- }
- public enum DraggableSplineEndAction
- {
- Respawn,
- Destroy
- }
- public class Spline : MonoBehaviour
- {
- public GameObject DraggablePrefab;
- public float NewNodeOffset;
- public List<SplineSegment> Segments = new List<SplineSegment>();
- public float SpawnStartDelay = 0;
- public float SpawnDelay = 1;
- public int SpawnCount = 0;
- public float MoveSpeed = 0.5f;
- public DraggableSplineEndAction DraggableSplineEndAction;
- public float PreWarmTime = 100f;
- private float _spawnTimer;
- private float _totalLen;
- private int _spawned;
- private List<SplineDraggableContext> _contexts = new List<SplineDraggableContext>();
- public void AddNode()
- {
- Vector3 point = transform.position;
- Vector3 dir = Vector3.forward;
- float len = NewNodeOffset;
- if (_lines != null && _lines.Count > 0)
- {
- dir = (_lines.Last().To - _lines.Last().From).normalized;
- point = _lines.Last().From;
- }
- var obj = new GameObject($"Spline{Segments.Count}");
- obj.transform.parent = transform;
- obj.transform.position = point + dir * len;
- var segment = obj.AddComponent<SplineSegment>();
- var lastSegment = Segments.LastOrDefault();
- segment.H1 = new GameObject("Handle1").AddComponent<SplineHandle>();
- segment.H2 = new GameObject("Handle2").AddComponent<SplineHandle>();
- segment.H1.transform.parent = segment.transform;
- segment.H1.transform.position = point + dir * (len / 3);
- segment.H2.transform.parent = segment.transform;
- segment.H2.transform.position = point + dir * ((len / 3) * 2);
- Segments.Add(segment);
- }
- public void Close()
- {
- var first = Segments.First();
- var last = Segments.Last();
- last.transform.position = transform.position;
- Init();
- Segments.First().H1.SnapOppositeToAxis();
- }
- private bool Changed()
- {
- return Segments.Select(_ => _.Changed()).ToArray().Any(_ => _);
- }
- // Start is called before the first frame update
- void Start()
- {
- }
- void OnValidate()
- {
- Init();
- }
- void DrawDraggable(SplineDraggableContext ctx, float delta)
- {
- ctx.Time += delta;
- var dist = MoveSpeed * ctx.Time;
- if (dist > _totalLen)
- {
- if (DraggableSplineEndAction == DraggableSplineEndAction.Respawn)
- {
- dist = dist % _totalLen;
- var loopTime = _totalLen / MoveSpeed;
- ctx.Time = ctx.Time % loopTime;
- }
- else
- {
- ctx.Invalid = true;
- return;
- }
- }
- for (int i = 0; i < _lines.Count; ++i)
- {
- if (dist < _lines[i].Length)
- {
- var dir = (_lines[i].To - _lines[i].From).normalized;
- ctx.Dragable.transform.position = _lines[i].From;
- ctx.Dragable.transform.LookAt(_lines[i].To);
- return;
- }
- else
- dist -= _lines[i].Length;
- }
- }
- void DoPrewarm()
- {
- while (PreWarmTime > 0)
- {
- var dt = 0f;
- if (SpawnCount == 0 || _spawned < SpawnCount)
- {
- if (_contexts.Count == 0)
- {
- var d = SpawnStartDelay;
- PreWarmTime -= d;
- }
- else
- {
- var d = SpawnDelay;
- PreWarmTime -= d;
- }
- var obj = Instantiate(DraggablePrefab, transform);
- var particleSystem = obj.GetComponentInChildren<ParticleSystem>();
- _contexts.Add(new SplineDraggableContext()
- {
- Dragable = obj,
- DependentObject = particleSystem != null ? particleSystem.gameObject : null
- });
- ++_spawned;
- if (PreWarmTime > 0)
- {
- dt = Mathf.Min(SpawnDelay, PreWarmTime);
- PreWarmTime -= dt;
- }
- }
- else
- {
- dt = PreWarmTime;
- PreWarmTime = 0;
- }
- foreach (var context in _contexts)
- {
- DrawDraggable(context, dt);
- if (context.Invalid || context.DependentObject == null)
- Destroy(context.Dragable);
- }
- _contexts.RemoveAll(_ => _.Invalid || _.DependentObject == null);
- }
- }
- // Update is called once per frame
- void Update()
- {
- if(Changed())
- Init();
- if(Segments.RemoveAll(_ => _ == null) > 0)
- Init();
- if (PreWarmTime > 0)
- {
- DoPrewarm();
- }
- var dt = Time.deltaTime;
- if (SpawnCount == 0 || _spawned < SpawnCount)
- {
- var spawnNew = false;
- _spawnTimer += dt;
- if (_contexts.Count == 0)
- {
- if (spawnNew = _spawnTimer > SpawnStartDelay)
- {
- _spawnTimer -= SpawnStartDelay;
- }
- }
- else
- {
- if (spawnNew = _spawnTimer > SpawnDelay)
- _spawnTimer -= SpawnDelay;
- }
- if (spawnNew)
- {
- var obj = Instantiate(DraggablePrefab, transform);
- var particleSystem = obj.GetComponentInChildren<ParticleSystem>();
- _contexts.Add(new SplineDraggableContext()
- {
- Dragable = obj,
- DependentObject = particleSystem != null ? particleSystem.gameObject : null
- });
- ++_spawned;
- }
- }
- foreach (var context in _contexts)
- {
- DrawDraggable(context, dt);
- if (context.Invalid || context.DependentObject == null)
- Destroy(context.Dragable);
- }
- _contexts.RemoveAll(_ => _.Invalid || _.DependentObject == null);
- PreWarmTime = 0;
- }
- void Init()
- {
- _lines.Clear();
- _controls.Clear();
- _totalLen = 0;
- Segments.RemoveAll(_ => _ == null);
- var p0 = transform.position;
- for(var s = 0; s < Segments.Count; ++s)
- {
- var segment = Segments[s];
- Vector3 p1 = segment.H1.transform.position;
- Vector3 p2 = segment.H2.transform.position;
- Vector3 p3 = segment.transform.position;
- Vector3 v0 = p0;
- Gizmos.color = Color.white;
- for (int i = 1; i < 1001; i++)
- {
- var t = i / 1000.0f;
- var v1 = Mathf.Pow(1f - t, 3) * p0
- + 3 * t * Mathf.Pow(1 - t, 2) * p1
- + 3 * Mathf.Pow(t, 2) * (1 - t) * p2
- + Mathf.Pow(t, 3) * p3;
- var len = Vector3.Distance(v0, v1);
- _totalLen += len;
- _lines.Add(new Line()
- {
- From = v0,
- To = v1,
- Length = len
- });
- v0 = v1;
- }
- _controls.Add(new Line()
- {
- From = p0,
- To = p1
- });
- _controls.Add(new Line()
- {
- From = p3,
- To = p2
- });
- p0 = p3;
- segment.H1.Opposite = null;
- segment.H1.Origin = null;
- segment.H2.Opposite = null;
- segment.H2.Origin = null;
- if (s > 0)
- {
- var prevSegment = Segments[s - 1];
- prevSegment.H2.Opposite = segment.H1.gameObject;
- prevSegment.H2.Origin = prevSegment.gameObject;
- segment.H1.Opposite = prevSegment.H2.gameObject;
- segment.H1.Origin = prevSegment.gameObject;
- if (s + 1 == Segments.Count)
- {
- var firstSegment = Segments.First();
- var dist = Vector3.Distance(segment.transform.position, transform.position);
- if (dist < 0.0000001f)
- {
- segment.H2.Opposite = firstSegment.H1.gameObject;
- segment.H2.Origin = gameObject;
- firstSegment.H1.Opposite = segment.H2.gameObject;
- firstSegment.H1.Origin = gameObject;
- }
- }
- }
- }
- Debug.Log($"Full spline len {_totalLen}.");
- transform.hasChanged = false;
- }
- void OnDrawGizmos()
- {
- if(Segments.RemoveAll(_ => _ == null) > 0)
- Init();
- if (Changed())
- Init();
- Gizmos.color = Color.white;
- foreach (var line in _lines)
- {
- Gizmos.DrawLine(line.From, line.To);
- }
- Gizmos.color = Color.green;
- foreach (var line in _controls)
- {
- Gizmos.DrawLine(line.From, line.To);
- }
- }
- private List<Line> _controls = new List<Line>();
- private List<Line> _lines = new List<Line>();
- struct Line
- {
- public Vector3 From;
- public Vector3 To;
- public float Length;
- }
-
- }
|