Files
Cielonos/Packages/com.opsive.behaviordesigner/Runtime/Tasks/Actions/GameObject/Instantiate.cs
2026-05-10 11:47:55 -04:00

236 lines
10 KiB
C#

#if GRAPH_DESIGNER
/// ---------------------------------------------
/// Behavior Designer
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.GameObjectTasks
{
using Opsive.GraphDesigner.Runtime.Variables;
using System.Collections;
using UnityEngine;
using UnityEngine.Pool;
[Opsive.Shared.Utility.Category("GameObject")]
[Opsive.Shared.Utility.Description("Instantiates a GameObject prefab with configurable delay, position, rotation, scale (or prefab scale), and parent. Can optionally auto-destroy after a duration.")]
public class Instantiate : Action
{
[Tooltip("The prefab to instantiate.")]
[SerializeField] protected SharedVariable<GameObject> m_Prefab;
[Tooltip("The delay before instantiation (in seconds).")]
[SerializeField] protected SharedVariable<float> m_Delay = 0f;
[Tooltip("The GameObject to use for position and rotation. If null, uses Position and Rotation values.")]
[SerializeField] protected SharedVariable<GameObject> m_Location;
[Tooltip("Should local position and rotation be used when Transform is specified?")]
[SerializeField] protected SharedVariable<bool> m_UseLocalTransform = false;
[Tooltip("The position to instantiate at. Only used if Location is null.")]
[SerializeField] protected SharedVariable<Vector3> m_Position;
[Tooltip("The rotation to instantiate with. Only used if Location is null.")]
[SerializeField] protected SharedVariable<Vector3> m_Rotation;
[Tooltip("Use the prefab's local scale instead of the Scale value.")]
[SerializeField] protected SharedVariable<bool> m_UsePrefabScale = false;
[Tooltip("The scale to instantiate with. Only used if Use Prefab Scale is disabled.")]
[SerializeField] protected SharedVariable<Vector3> m_Scale = Vector3.one;
[Tooltip("The parent GameObject to assign. If null, no parent is assigned.")]
[SerializeField] protected SharedVariable<GameObject> m_Parent;
[Tooltip("Should object pooling be used?")]
[SerializeField] protected SharedVariable<bool> m_UsePooling = false;
[Tooltip("The initial pool size. Only used if Use Pooling is enabled.")]
[SerializeField] protected SharedVariable<int> m_PoolSize = 10;
[Tooltip("The maximum pool size. Only used if Use Pooling is enabled.")]
[SerializeField] protected SharedVariable<int> m_MaxPoolSize = 20;
[Tooltip("Should the GameObject be auto-destroyed after a duration?")]
[SerializeField] protected SharedVariable<bool> m_AutoDestroy = false;
[Tooltip("The duration before auto-destroy. Only used if Auto Destroy is enabled.")]
[SerializeField] protected SharedVariable<float> m_DestroyDuration = 5f;
[Tooltip("The name to assign to the instantiated GameObject. If empty, uses the prefab name.")]
[SerializeField] protected SharedVariable<string> m_ObjectName;
[Tooltip("The instantiated GameObject.")]
[SerializeField] [RequireShared] protected SharedVariable<GameObject> m_InstantiatedObject;
private float m_ElapsedTime;
private bool m_HasInstantiated;
private ObjectPool<GameObject> m_Pool;
/// <summary>
/// Called when the state machine is initialized.
/// </summary>
public override void OnAwake()
{
base.OnAwake();
if (m_UsePooling.Value && m_Prefab.Value != null) {
InitializePool();
}
}
/// <summary>
/// Initializes the object pool.
/// </summary>
private void InitializePool()
{
if (m_Pool != null) {
return;
}
m_Pool = new ObjectPool<GameObject>(
createFunc: () => {
var instance = m_Parent.Value != null ? UnityEngine.Object.Instantiate(m_Prefab.Value, m_Parent.Value.transform) : UnityEngine.Object.Instantiate(m_Prefab.Value);
instance.SetActive(false);
return instance;
},
actionOnGet: (obj) => {
obj.SetActive(true);
},
actionOnRelease: (obj) => {
obj.SetActive(false);
PoolManager.Instance.Unregister(obj);
},
actionOnDestroy: (obj) => {
UnityEngine.Object.Destroy(obj);
},
collectionCheck: true,
defaultCapacity: m_PoolSize.Value,
maxSize: m_MaxPoolSize.Value
);
}
/// <summary>
/// Called when the state machine starts.
/// </summary>
public override void OnStart()
{
base.OnStart();
m_ElapsedTime = 0f;
m_HasInstantiated = false;
}
/// <summary>
/// Instantiates the GameObject after delay.
/// </summary>
/// <returns>The status of the action.</returns>
public override TaskStatus OnUpdate()
{
if (m_Prefab.Value == null) {
Debug.LogError("Instantiate: Prefab is not assigned.");
return TaskStatus.Success;
}
m_ElapsedTime += Time.deltaTime;
if (!m_HasInstantiated && m_ElapsedTime >= m_Delay.Value) {
// Initialize pool if needed.
if (m_UsePooling.Value && m_Pool == null && m_Prefab.Value != null) {
InitializePool();
}
// Determine position and rotation.
Vector3 position;
Quaternion rotation;
if (m_Location.Value != null) {
position = m_UseLocalTransform.Value ? m_Location.Value.transform.localPosition : m_Location.Value.transform.position;
rotation = m_UseLocalTransform.Value ? m_Location.Value.transform.localRotation : m_Location.Value.transform.rotation;
} else {
position = m_Position.Value;
rotation = Quaternion.Euler(m_Rotation.Value);
}
// Get or instantiate the GameObject.
GameObject instantiated;
if (m_UsePooling.Value && m_Pool != null) {
instantiated = m_Pool.Get();
PoolManager.Instance.Register(instantiated, m_Pool);
if (m_Parent.Value != null) {
instantiated.transform.SetParent(m_Parent.Value.transform);
}
instantiated.transform.position = position;
instantiated.transform.rotation = rotation;
} else {
instantiated = m_Parent.Value != null ? UnityEngine.Object.Instantiate(m_Prefab.Value, position, rotation, m_Parent.Value.transform) : UnityEngine.Object.Instantiate(m_Prefab.Value, position, rotation);
}
instantiated.transform.localScale = m_UsePrefabScale.Value ? m_Prefab.Value.transform.localScale : m_Scale.Value;
if (!string.IsNullOrEmpty(m_ObjectName.Value)) {
instantiated.name = m_ObjectName.Value;
}
m_InstantiatedObject.Value = instantiated;
m_HasInstantiated = true;
// Setup auto-destroy if enabled.
if (m_AutoDestroy.Value) {
StartCoroutine(ReleaseOrDestroyAfterDelay(instantiated, m_DestroyDuration.Value, m_UsePooling.Value && m_Pool != null));
}
}
return m_HasInstantiated ? TaskStatus.Success : TaskStatus.Running;
}
/// <summary>
/// Releases the GameObject back to the pool or destroys it after a delay.
/// </summary>
/// <param name="obj">The GameObject to release or destroy.</param>
/// <param name="delay">The delay before releasing or destroying.</param>
/// <param name="usePooling">Should the object be released to the pool? If false, it will be destroyed.</param>
/// <returns>The coroutine enumerator.</returns>
private IEnumerator ReleaseOrDestroyAfterDelay(GameObject obj, float delay, bool usePooling)
{
yield return new WaitForSeconds(delay);
if (obj == null) {
yield break;
}
if (usePooling) {
if (!obj.activeSelf) {
yield break;
}
// Check if object is no longer registered with PoolManager (already released).
if (PoolManager.Instance.GetPool(obj) == null) {
yield break;
}
PoolManager.Instance.ReleaseToPool(obj);
} else {
UnityEngine.Object.Destroy(obj);
}
}
/// <summary>
/// Called when the state machine is destroyed.
/// </summary>
public override void OnDestroy()
{
base.OnDestroy();
// Clean up pool when state machine is destroyed.
if (m_Pool != null) {
m_Pool.Dispose();
m_Pool = null;
}
}
/// <summary>
/// Resets the action values back to their default.
/// </summary>
public override void Reset()
{
base.Reset();
m_Prefab = null;
m_Delay = 0f;
m_Location = null;
m_UseLocalTransform = false;
m_Position = Vector3.zero;
m_Rotation = Vector3.zero;
m_UsePrefabScale = false;
m_Scale = Vector3.one;
m_Parent = null;
m_UsePooling = false;
m_PoolSize = 10;
m_MaxPoolSize = 20;
m_AutoDestroy = false;
m_DestroyDuration = 5f;
m_ObjectName = null;
m_InstantiatedObject = null;
}
}
}
#endif