rabidus-test/Assets/Dreamteck/Splines/Components/ObjectController.cs

848 lines
26 KiB
C#
Raw Permalink Normal View History

2023-07-24 16:38:13 +03:00
using UnityEngine;
using System.Collections;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Dreamteck.Splines
{
[AddComponentMenu("Dreamteck/Splines/Users/Object Controller")]
public class ObjectController : SplineUser
{
[System.Serializable]
internal class ObjectControl
{
public bool isNull
{
get
{
return gameObject == null;
}
}
public Transform transform
{
get {
if (gameObject == null) return null;
return gameObject.transform;
}
}
public GameObject gameObject;
public Vector3 position = Vector3.zero;
public Quaternion rotation = Quaternion.identity;
public Vector3 scale = Vector3.one;
public bool active = true;
public Vector3 baseScale = Vector3.one;
public ObjectControl(GameObject input)
{
gameObject = input;
baseScale = gameObject.transform.localScale;
}
public void Destroy()
{
if (gameObject == null) return;
GameObject.Destroy(gameObject);
}
public void DestroyImmediate()
{
if (gameObject == null) return;
GameObject.DestroyImmediate(gameObject);
}
public void Apply()
{
if (gameObject == null) return;
transform.position = position;
transform.rotation = rotation;
transform.localScale = scale;
gameObject.SetActive(active);
}
}
public enum ObjectMethod { Instantiate, GetChildren }
public enum Positioning { Stretch, Clip }
public enum Iteration { Ordered, Random }
[SerializeField]
[HideInInspector]
public GameObject[] objects = new GameObject[0];
public ObjectMethod objectMethod
{
get { return _objectMethod; }
set
{
if (value != _objectMethod)
{
if (value == ObjectMethod.GetChildren)
{
_objectMethod = value;
Spawn();
}
else _objectMethod = value;
}
}
}
public int spawnCount
{
get { return _spawnCount; }
set
{
if (value != _spawnCount)
{
if (value < 0) value = 0;
if (_objectMethod == ObjectMethod.Instantiate)
{
if (value < _spawnCount)
{
_spawnCount = value;
Remove();
}
else
{
_spawnCount = value;
Spawn();
}
}
else _spawnCount = value;
}
}
}
public Positioning objectPositioning
{
get { return _objectPositioning; }
set
{
if (value != _objectPositioning)
{
_objectPositioning = value;
Rebuild();
}
}
}
public Iteration iteration
{
get { return _iteration; }
set
{
if (value != _iteration)
{
_iteration = value;
Rebuild();
}
}
}
#if UNITY_EDITOR
public bool retainPrefabInstancesInEditor
{
get { return _retainPrefabInstancesInEditor; }
set
{
if (value != _retainPrefabInstancesInEditor)
{
_retainPrefabInstancesInEditor = value;
Clear();
Spawn();
Rebuild();
}
}
}
#endif
public int randomSeed
{
get { return _randomSeed; }
set
{
if (value != _randomSeed)
{
_randomSeed = value;
Rebuild();
}
}
}
public Vector3 minOffset
{
get { return _minOffset; }
set
{
if (value != _minOffset)
{
_minOffset = value;
Rebuild();
}
}
}
public Vector3 maxOffset
{
get { return _maxOffset; }
set
{
if (value != _maxOffset)
{
_maxOffset = value;
Rebuild();
}
}
}
public bool offsetUseWorldCoords
{
get { return _offsetUseWorldCoords; }
set
{
if (value != _offsetUseWorldCoords)
{
_offsetUseWorldCoords = value;
Rebuild();
}
}
}
public Vector3 minRotation
{
get { return _minRotation; }
set
{
if (value != _minRotation)
{
_minRotation = value;
Rebuild();
}
}
}
public Vector3 maxRotation
{
get { return _maxRotation; }
set
{
if (value != _maxRotation)
{
_maxRotation = value;
Rebuild();
}
}
}
public Vector3 rotationOffset
{
get { return (_maxRotation+_minRotation)/2f; }
set
{
if (value != _minRotation || value != _maxRotation)
{
_minRotation = _maxRotation = value;
Rebuild();
}
}
}
public Vector3 minScaleMultiplier
{
get { return _minScaleMultiplier; }
set
{
if (value != _minScaleMultiplier)
{
_minScaleMultiplier = value;
Rebuild();
}
}
}
public Vector3 maxScaleMultiplier
{
get { return _maxScaleMultiplier; }
set
{
if (value != _maxScaleMultiplier)
{
_maxScaleMultiplier = value;
Rebuild();
}
}
}
public bool uniformScaleLerp
{
get { return _uniformScaleLerp; }
set
{
if(value != _uniformScaleLerp)
{
_uniformScaleLerp = value;
Rebuild();
}
}
}
public bool shellOffset
{
get { return _shellOffset; }
set
{
if (value != _shellOffset)
{
_shellOffset = value;
Rebuild();
}
}
}
public bool applyRotation
{
get { return _applyRotation; }
set
{
if (value != _applyRotation)
{
_applyRotation = value;
Rebuild();
}
}
}
public bool rotateByOffset
{
get { return _rotateByOffset; }
set
{
if (value != _rotateByOffset)
{
_rotateByOffset = value;
Rebuild();
}
}
}
public bool applyScale
{
get { return _applyScale; }
set
{
if (value != _applyScale)
{
_applyScale = value;
Rebuild();
}
}
}
public float evaluateOffset
{
get { return _evaluateOffset; }
set
{
if (value != _evaluateOffset)
{
_evaluateOffset = value;
Rebuild();
}
}
}
public float minObjectDistance
{
get { return _minObjectDistance; }
set
{
if (value != _minObjectDistance)
{
_minObjectDistance = value;
Rebuild();
}
}
}
public float maxObjectDistance
{
get { return _maxObjectDistance; }
set
{
if (value != _maxObjectDistance)
{
_maxObjectDistance = value;
Rebuild();
}
}
}
public ObjectControllerCustomRuleBase customOffsetRule
{
get { return _customOffsetRule; }
set
{
if (value != _customOffsetRule)
{
_customOffsetRule = value;
Rebuild();
}
}
}
public ObjectControllerCustomRuleBase customRotationRule
{
get { return _customRotationRule; }
set
{
if (value != _customRotationRule)
{
_customRotationRule = value;
Rebuild();
}
}
}
public ObjectControllerCustomRuleBase customScaleRule
{
get { return _customScaleRule; }
set
{
if (value != _customScaleRule)
{
_customScaleRule = value;
Rebuild();
}
}
}
[SerializeField]
[HideInInspector]
private float _evaluateOffset = 0f;
[SerializeField]
[HideInInspector]
private int _spawnCount = 0;
#if UNITY_EDITOR
[SerializeField]
[HideInInspector]
private bool _retainPrefabInstancesInEditor = true;
#endif
[SerializeField]
[HideInInspector]
private Positioning _objectPositioning = Positioning.Stretch;
[SerializeField]
[HideInInspector]
private Iteration _iteration = Iteration.Ordered;
[SerializeField]
[HideInInspector]
private int _randomSeed = 1;
[SerializeField]
[HideInInspector]
private Vector3 _minOffset = Vector3.zero;
[SerializeField]
[HideInInspector]
private Vector3 _maxOffset = Vector3.zero;
[SerializeField]
[HideInInspector]
private bool _offsetUseWorldCoords = false;
[SerializeField]
[HideInInspector]
private Vector3 _minRotation = Vector3.zero;
[SerializeField]
[HideInInspector]
private Vector3 _maxRotation = Vector3.zero;
[SerializeField]
[HideInInspector]
private bool _uniformScaleLerp = true;
[SerializeField]
[HideInInspector]
private Vector3 _minScaleMultiplier = Vector3.one;
[SerializeField]
[HideInInspector]
private Vector3 _maxScaleMultiplier = Vector3.one;
[SerializeField]
[HideInInspector]
private bool _shellOffset = false;
[SerializeField]
[HideInInspector]
private bool _applyRotation = true;
[SerializeField]
[HideInInspector]
private bool _rotateByOffset = false;
[SerializeField]
[HideInInspector]
private bool _applyScale = false;
[SerializeField]
[HideInInspector]
private ObjectMethod _objectMethod = ObjectMethod.Instantiate;
[HideInInspector]
public bool delayedSpawn = false;
[HideInInspector]
public float spawnDelay = 0.1f;
[SerializeField]
[HideInInspector]
private int lastChildCount = 0;
[SerializeField]
[HideInInspector]
private ObjectControl[] spawned = new ObjectControl[0];
[SerializeField]
[HideInInspector]
private bool _useCustomObjectDistance = false;
[SerializeField]
[HideInInspector]
private float _minObjectDistance = 0f;
[SerializeField]
[HideInInspector]
private float _maxObjectDistance = 0f;
[SerializeField]
[HideInInspector]
private ObjectControllerCustomRuleBase _customOffsetRule;
[SerializeField]
[HideInInspector]
private ObjectControllerCustomRuleBase _customRotationRule;
[SerializeField]
[HideInInspector]
private ObjectControllerCustomRuleBase _customScaleRule;
System.Random offsetRandomizer, shellRandomizer, rotationRandomizer, scaleRandomizer, distanceRandomizer;
public void Clear()
{
for (int i = 0; i < spawned.Length; i++)
{
if (spawned[i] == null || spawned[i].transform == null) continue;
spawned[i].transform.localScale = spawned[i].baseScale;
if (_objectMethod == ObjectMethod.GetChildren) spawned[i].gameObject.SetActive(false);
else
{
#if UNITY_EDITOR
if (!Application.isPlaying) spawned[i].DestroyImmediate();
else spawned[i].Destroy();
#else
spawned[i].Destroy();
#endif
}
}
spawned = new ObjectControl[0];
}
private void OnValidate()
{
if (_spawnCount < 0) _spawnCount = 0;
}
private void Remove()
{
if (_spawnCount >= spawned.Length) return;
for (int i = spawned.Length - 1; i >= _spawnCount; i--)
{
if (i >= spawned.Length) break;
if (spawned[i] == null) continue;
spawned[i].transform.localScale = spawned[i].baseScale;
if (_objectMethod == ObjectMethod.GetChildren) spawned[i].gameObject.SetActive(false);
else
{
if (Application.isEditor) spawned[i].DestroyImmediate();
else spawned[i].Destroy();
}
}
ObjectControl[] newSpawned = new ObjectControl[_spawnCount];
for (int i = 0; i < newSpawned.Length; i++)
{
newSpawned[i] = spawned[i];
}
spawned = newSpawned;
Rebuild();
}
public void GetAll()
{
ObjectControl[] newSpawned = new ObjectControl[transform.childCount];
int index = 0;
foreach (Transform child in transform)
{
if (newSpawned[index] == null)
{
newSpawned[index++] = new ObjectControl(child.gameObject);
continue;
}
bool found = false;
for (int i = 0; i < spawned.Length; i++)
{
if (spawned[i].gameObject == child.gameObject)
{
newSpawned[index++] = spawned[i];
found = true;
break;
}
}
if (!found) newSpawned[index++] = new ObjectControl(child.gameObject);
}
spawned = newSpawned;
}
public void Spawn()
{
if (_objectMethod == ObjectMethod.Instantiate)
{
if (delayedSpawn && Application.isPlaying)
{
StopCoroutine("InstantiateAllWithDelay");
StartCoroutine(InstantiateAllWithDelay());
}
else InstantiateAll();
}
else GetAll();
Rebuild();
}
protected override void LateRun()
{
base.LateRun();
if (_objectMethod == ObjectMethod.GetChildren && lastChildCount != transform.childCount)
{
Spawn();
lastChildCount = transform.childCount;
}
}
IEnumerator InstantiateAllWithDelay()
{
if (spline == null) yield break;
if (objects.Length == 0) yield break;
for (int i = spawned.Length; i <= spawnCount; i++)
{
InstantiateSingle();
yield return new WaitForSeconds(spawnDelay);
}
}
private void InstantiateAll()
{
if (spline == null) return;
if (objects.Length == 0) return;
for (int i = spawned.Length; i < spawnCount; i++) InstantiateSingle();
}
private void InstantiateSingle()
{
if (objects.Length == 0) return;
int index = 0;
if (_iteration == Iteration.Ordered)
{
index = spawned.Length - Mathf.FloorToInt(spawned.Length / objects.Length) * objects.Length;
}
else index = Random.Range(0, objects.Length);
if (objects[index] == null) return;
ObjectControl[] newSpawned = new ObjectControl[spawned.Length + 1];
spawned.CopyTo(newSpawned, 0);
#if UNITY_EDITOR
if (!Application.isPlaying && retainPrefabInstancesInEditor)
{
GameObject go = (GameObject)UnityEditor.PrefabUtility.InstantiatePrefab(objects[index]);
go.transform.position = transform.position;
go.transform.rotation = transform.rotation;
newSpawned[newSpawned.Length - 1] = new ObjectControl(go);
} else
{
newSpawned[newSpawned.Length - 1] = new ObjectControl((GameObject)Instantiate(objects[index], transform.position, transform.rotation));
}
#else
newSpawned[newSpawned.Length - 1] = new ObjectControl((GameObject)Instantiate(objects[index], transform.position, transform.rotation));
#endif
newSpawned[newSpawned.Length - 1].transform.parent = transform;
spawned = newSpawned;
}
protected override void Build()
{
base.Build();
offsetRandomizer = new System.Random(_randomSeed);
if(_shellOffset) shellRandomizer = new System.Random(_randomSeed + 1);
rotationRandomizer = new System.Random(_randomSeed + 2);
scaleRandomizer = new System.Random(_randomSeed + 3);
distanceRandomizer = new System.Random(_randomSeed + 4);
bool hasCustomOffset = _customOffsetRule != null;
bool hasCustomRotation = _customRotationRule != null;
bool hasCustomScale = _customScaleRule != null;
bool randomScaleMultiplier = _minScaleMultiplier != _maxScaleMultiplier;
double distancePercentAccum = 0.0;
for (int i = 0; i < spawned.Length; i++)
{
if (spawned[i] == null)
{
Clear();
Spawn();
break;
}
float percent = 0f;
if (spawned.Length > 1)
{
if(!_useCustomObjectDistance)
{
if (spline.isClosed)
{
percent = (float)i / spawned.Length;
}
else
{
percent = (float)i / (spawned.Length - 1);
}
} else
{
percent = (float)distancePercentAccum;
}
}
percent += _evaluateOffset;
if (percent > 1f)
{
percent -= 1f;
}
else if (percent < 0f)
{
percent += 1f;
}
if (objectPositioning == Positioning.Clip)
{
spline.Evaluate(percent, ref evalResult);
}
else
{
Evaluate(percent, ref evalResult);
}
ModifySample(ref evalResult);
spawned[i].position = evalResult.position;
if (_applyScale)
{
if (hasCustomScale)
{
_customScaleRule.SetContext(this, evalResult, i, spawned.Length);
spawned[i].scale = _customOffsetRule.GetScale();
}
else
{
Vector3 scale = spawned[i].baseScale * evalResult.size;
Vector3 multiplier = _minScaleMultiplier;
if (randomScaleMultiplier)
{
if (_uniformScaleLerp)
{
multiplier = Vector3.Lerp(new Vector3(_minScaleMultiplier.x, _minScaleMultiplier.y, _minScaleMultiplier.z), new Vector3(_maxScaleMultiplier.x, _maxScaleMultiplier.y, _maxScaleMultiplier.z), (float)scaleRandomizer.NextDouble());
}
else
{
multiplier.x = Mathf.Lerp(_minScaleMultiplier.x, _maxScaleMultiplier.x, (float)scaleRandomizer.NextDouble());
multiplier.y = Mathf.Lerp(_minScaleMultiplier.y, _maxScaleMultiplier.y, (float)scaleRandomizer.NextDouble());
multiplier.z = Mathf.Lerp(_minScaleMultiplier.z, _maxScaleMultiplier.z, (float)scaleRandomizer.NextDouble());
}
}
scale.x *= multiplier.x;
scale.y *= multiplier.y;
scale.z *= multiplier.z;
spawned[i].scale = scale;
}
}
else
{
spawned[i].scale = spawned[i].baseScale;
}
Vector3 right = Vector3.Cross(evalResult.forward, evalResult.up).normalized;
Vector3 posOffset = _minOffset;
if (hasCustomOffset)
{
_customOffsetRule.SetContext(this, evalResult, i, spawned.Length);
posOffset = _customOffsetRule.GetOffset();
}
else if (_minOffset != _maxOffset)
{
if(_shellOffset)
{
float x = _maxOffset.x - _minOffset.x;
float y = _maxOffset.y - _minOffset.y;
float angleInRadians = (float)shellRandomizer.NextDouble() * 360f * Mathf.Deg2Rad;
posOffset = new Vector2(0.5f * Mathf.Cos(angleInRadians), 0.5f * Mathf.Sin(angleInRadians));
posOffset.x *= x;
posOffset.y *= y;
} else
{
float rnd = (float)offsetRandomizer.NextDouble();
posOffset.x = Mathf.Lerp(_minOffset.x, _maxOffset.x, rnd);
rnd = (float)offsetRandomizer.NextDouble();
posOffset.y = Mathf.Lerp(_minOffset.y, _maxOffset.y, rnd);
rnd = (float)offsetRandomizer.NextDouble();
posOffset.z = Mathf.Lerp(_minOffset.z, _maxOffset.z, rnd);
}
}
if (_offsetUseWorldCoords)
{
spawned[i].position += posOffset;
}
else
{
spawned[i].position += right * posOffset.x * evalResult.size + evalResult.up * posOffset.y * evalResult.size;
}
if (_applyRotation)
{
if (hasCustomRotation)
{
_customRotationRule.SetContext(this, evalResult, i, spawned.Length);
spawned[i].rotation = _customRotationRule.GetRotation();
}
else
{
Quaternion offsetRot = Quaternion.Euler(Mathf.Lerp(_minRotation.x, _maxRotation.x, (float)rotationRandomizer.NextDouble()), Mathf.Lerp(_minRotation.y, _maxRotation.y, (float)rotationRandomizer.NextDouble()), Mathf.Lerp(_minRotation.z, _maxRotation.z, (float)rotationRandomizer.NextDouble()));
if (_rotateByOffset) spawned[i].rotation = Quaternion.LookRotation(evalResult.forward, spawned[i].position - evalResult.position) * offsetRot;
else spawned[i].rotation = evalResult.rotation * offsetRot;
}
}
if (_objectPositioning == Positioning.Clip)
{
if (percent < clipFrom || percent > clipTo) spawned[i].active = false;
else spawned[i].active = true;
}
if (_useCustomObjectDistance)
{
if (objectPositioning == Positioning.Clip)
{
distancePercentAccum = spline.Travel(distancePercentAccum, Mathf.Lerp(_minObjectDistance, _maxObjectDistance, (float)distanceRandomizer.NextDouble()));
}
else
{
distancePercentAccum = Travel(distancePercentAccum, Mathf.Lerp(_minObjectDistance, _maxObjectDistance, (float)distanceRandomizer.NextDouble()));
}
}
}
}
protected override void PostBuild()
{
base.PostBuild();
for (int i = 0; i < spawned.Length; i++)
{
spawned[i].Apply();
}
}
}
}