Rasagar/Library/PackageCache/com.unity.visualeffectgraph/Samples~/VFXOutputEventHandlers/Runtime/VFXOutputEventPrefabSpawn.cs
2024-08-26 23:07:20 +03:00

212 lines
8.0 KiB
C#

using System;
namespace UnityEngine.VFX.Utility
{
[ExecuteAlways]
[RequireComponent(typeof(VisualEffect))]
class VFXOutputEventPrefabSpawn : VFXOutputEventAbstractHandler
{
public override bool canExecuteInEditor => true;
public uint instanceCount => m_InstanceCount;
public GameObject prefabToSpawn => m_PrefabToSpawn;
public bool parentInstances => m_ParentInstances;
#pragma warning disable 414, 649
[SerializeField, Tooltip("The maximum number of prefabs that can be active at a time")]
uint m_InstanceCount = 5;
[SerializeField, Tooltip("The prefab to enable upon event received. Prefabs are created as hidden and stored in a pool, upon enabling this behavior. Upon receiving an event a prefab from the pool is enabled and will be disabled when reaching its lifetime.")]
GameObject m_PrefabToSpawn;
[SerializeField, Tooltip("Whether to attach prefab instances to current game object. Use this setting to treat position and angle attributes as local space.")]
bool m_ParentInstances;
#pragma warning restore 414, 649
#if UNITY_EDITOR
bool m_Dirty = true;
#endif
[Tooltip("Whether to use the position attribute to set prefab position on spawn")]
public bool usePosition = true;
[Tooltip("Whether to use the angle attribute to set prefab rotation on spawn")]
public bool useAngle = true;
[Tooltip("Whether to use the scale attribute to set prefab localScale on spawn")]
public bool useScale = true;
[Tooltip("Whether to use the lifetime attribute to determine how long the prefab will be enabled")]
public bool useLifetime = true;
static readonly GameObject[] k_EmptyGameObjects = new GameObject[0];
static readonly float[] k_EmptyTimeToLive = new float[0];
GameObject[] m_Instances = k_EmptyGameObjects;
float[] m_TimesToLive = k_EmptyTimeToLive;
protected override void OnDisable()
{
base.OnDisable();
foreach (var instance in m_Instances)
instance.SetActive(false);
}
void OnDestroy()
{
DisposeInstances();
}
#if UNITY_EDITOR
void OnValidate()
{
m_Dirty = true;
}
#endif
void DisposeInstances()
{
foreach (var instance in m_Instances)
{
if (instance)
{
if (Application.isPlaying)
Destroy(instance);
else
DestroyImmediate(instance);
}
}
m_Instances = k_EmptyGameObjects;
m_TimesToLive = k_EmptyTimeToLive;
}
static readonly int k_PositionID = Shader.PropertyToID("position");
static readonly int k_AngleID = Shader.PropertyToID("angle");
static readonly int k_ScaleID = Shader.PropertyToID("scale");
static readonly int k_LifetimeID = Shader.PropertyToID("lifetime");
void UpdateHideFlag(GameObject instance)
{
instance.hideFlags = HideFlags.HideAndDontSave;
//We are using HideInHierarchy to prevent unexpected deletion in edit mode.
//instance.hideFlags = HideFlags.DontSave | HideFlags.NotEditable;
}
void CheckAndRebuildInstances()
{
bool rebuild = m_Instances.Length != m_InstanceCount;
#if UNITY_EDITOR
if (m_Dirty)
{
rebuild = true;
m_Dirty = false;
}
#endif
if (rebuild)
{
DisposeInstances();
if (m_PrefabToSpawn != null && m_InstanceCount != 0)
{
m_Instances = new GameObject[m_InstanceCount];
m_TimesToLive = new float[m_InstanceCount];
#if UNITY_EDITOR
var prefabAssetType = UnityEditor.PrefabUtility.GetPrefabAssetType(m_PrefabToSpawn);
#endif
for (int i = 0; i < m_Instances.Length; i++)
{
GameObject newInstance = null;
#if UNITY_EDITOR
if (prefabAssetType != UnityEditor.PrefabAssetType.NotAPrefab)
newInstance = UnityEditor.PrefabUtility.InstantiatePrefab(m_PrefabToSpawn) as GameObject;
if (newInstance == null)
newInstance = Instantiate(m_PrefabToSpawn);
#else
newInstance = Instantiate(m_PrefabToSpawn);
#endif
newInstance.name = $"{name} - #{i} - {m_PrefabToSpawn.name}";
newInstance.SetActive(false);
newInstance.transform.parent = m_ParentInstances ? transform : null;
UpdateHideFlag(newInstance);
m_Instances[i] = newInstance;
m_TimesToLive[i] = float.NegativeInfinity;
}
}
}
}
public override void OnVFXOutputEvent(VFXEventAttribute eventAttribute)
{
CheckAndRebuildInstances();
int freeIdx = -1;
for (int i = 0; i < m_Instances.Length; i++)
{
if (!m_Instances[i].activeSelf)
{
freeIdx = i;
break;
}
}
if (freeIdx != -1)
{
var availableInstance = m_Instances[freeIdx];
availableInstance.SetActive(true);
if (usePosition && eventAttribute.HasVector3(k_PositionID))
{
if (m_ParentInstances)
availableInstance.transform.localPosition = eventAttribute.GetVector3(k_PositionID);
else
availableInstance.transform.position = eventAttribute.GetVector3(k_PositionID);
}
if (useAngle && eventAttribute.HasVector3(k_AngleID))
{
if (parentInstances)
availableInstance.transform.localEulerAngles = eventAttribute.GetVector3(k_AngleID);
else
availableInstance.transform.eulerAngles = eventAttribute.GetVector3(k_AngleID);
}
if (useScale && eventAttribute.HasVector3(k_ScaleID))
availableInstance.transform.localScale = eventAttribute.GetVector3(k_ScaleID);
if (useLifetime && eventAttribute.HasFloat(k_LifetimeID))
m_TimesToLive[freeIdx] = eventAttribute.GetFloat(k_LifetimeID);
else
m_TimesToLive[freeIdx] = float.NegativeInfinity;
var handlers = availableInstance.GetComponentsInChildren<VFXOutputEventPrefabAttributeAbstractHandler>();
foreach (var handler in handlers)
handler.OnVFXEventAttribute(eventAttribute, m_VisualEffect);
} //Else, can't find an instance available, ignoring.
}
void Update()
{
if (Application.isPlaying || (executeInEditor && canExecuteInEditor))
{
CheckAndRebuildInstances();
var dt = Time.deltaTime;
for (int i = 0; i < m_Instances.Length; i++)
{
#if UNITY_EDITOR
//Reassign hide flag, "open prefab" could have resetted this hide flag.
UpdateHideFlag(m_Instances[i]);
#endif
// Negative infinity for non-time managed
if (m_TimesToLive[i] == float.NegativeInfinity)
continue;
// Else, manage time
if (m_TimesToLive[i] <= 0.0f && m_Instances[i].activeSelf)
m_Instances[i].SetActive(false);
else
m_TimesToLive[i] -= dt;
}
}
else
{
DisposeInstances();
}
}
}
}