diff --git a/Runtime/Engine/Code/Animations/AnimationClipNode.cs b/Runtime/Engine/Code/Animations/AnimationClipNode.cs index d5bcdc2..62e1075 100644 --- a/Runtime/Engine/Code/Animations/AnimationClipNode.cs +++ b/Runtime/Engine/Code/Animations/AnimationClipNode.cs @@ -1,24 +1,53 @@ -using UnityEngine; +using System; +using UnityEngine; using UnityEngine.Animations; using UnityEngine.Playables; namespace RebootKit.Engine.Animations { + [Serializable] public class AnimationClipNode : IReAnimatorNode { [field: SerializeField] public string Name { get; private set; } + AnimationClipPlayable m_Playable; + + bool m_PlayOnce; + Action m_Callback; + public AnimationClip Clip; public void Tick(float deltaTime) { + if (m_Playable.GetPlayState() == PlayState.Paused) { + return; + } + + if (m_PlayOnce) { + if (m_Playable.GetTime() >= m_Playable.GetAnimationClip().length) { + m_Playable.Pause(); + + m_Callback?.Invoke(); + m_Callback = null; + + m_PlayOnce = false; + } + } } public IPlayable Build(PlayableGraph graph) { - AnimationClipPlayable playable = AnimationClipPlayable.Create(graph, Clip); - return playable; + m_Playable = AnimationClipPlayable.Create(graph, Clip); + return m_Playable; } public bool TryFindChild(string name, out IReAnimatorNode node) { node = null; return false; } + + public void PlayOnceWithCallback(Action callback) { + m_Playable.SetTime(0.0); + m_Playable.Play(); + + m_Callback = callback; + m_PlayOnce = true; + } } } \ No newline at end of file diff --git a/Runtime/Engine/Code/Animations/LayerMixerNode.cs b/Runtime/Engine/Code/Animations/LayerMixerNode.cs new file mode 100644 index 0000000..95db6d9 --- /dev/null +++ b/Runtime/Engine/Code/Animations/LayerMixerNode.cs @@ -0,0 +1,88 @@ +using System; +using UnityEngine; +using UnityEngine.Animations; +using UnityEngine.Playables; + +namespace RebootKit.Engine.Animations { + [Serializable] + public class LayerMixerNode : IReAnimatorNode { + [Serializable] + struct LayerState { + [SerializeReference] public IReAnimatorNode node; + + public AvatarMask mask; + public bool isAdditive; + + [Range(0.0f, 1.0f)] public float targetWeight; + [NonSerialized] public float CurrentWeight; + } + + [field: SerializeField] public string Name { get; private set; } + + AnimationLayerMixerPlayable m_LayerMixer; + + [SerializeField] float m_TransitionSpeed = 5.0f; + [SerializeField] LayerState[] m_Layers; + + public void Tick(float deltaTime) { + for (int i = 0; i < m_Layers.Length; ++i){ + if (m_TransitionSpeed > 0.0f) { + m_Layers[i].CurrentWeight = Mathf.MoveTowards(m_Layers[i].CurrentWeight, + m_Layers[i].targetWeight, + m_TransitionSpeed * deltaTime); + } else { + m_Layers[i].CurrentWeight = m_Layers[i].targetWeight; + } + + m_LayerMixer.SetInputWeight(i, m_Layers[i].CurrentWeight); + + m_Layers[i].node.Tick(deltaTime); + } + } + + public IPlayable Build(PlayableGraph graph) { + m_LayerMixer = AnimationLayerMixerPlayable.Create(graph, m_Layers.Length); + + for (int i = 0; i < m_Layers.Length; ++i) { + IPlayable playable = m_Layers[i].node.Build(graph); + + if (playable is AnimationMixerPlayable mixerPlayable) { + m_LayerMixer.ConnectInput(i, mixerPlayable, 0, m_Layers[i].targetWeight); + } else if (playable is AnimationClipPlayable clipPlayable) { + m_LayerMixer.ConnectInput(i, clipPlayable, 0, m_Layers[i].targetWeight); + } else if (playable is AnimationLayerMixerPlayable layerMixerPlayable) { + m_LayerMixer.ConnectInput(i, layerMixerPlayable, 0, m_Layers[i].targetWeight); + } + + m_LayerMixer.SetLayerAdditive((uint)i, m_Layers[i].isAdditive); + + if (m_Layers[i].mask != null) { + m_LayerMixer.SetLayerMaskFromAvatarMask((uint)i, m_Layers[i].mask); + } + } + + return m_LayerMixer; + } + + public bool TryFindChild(string name, out IReAnimatorNode node) { + for (int i = 0; i < m_Layers.Length; i++) { + if (m_Layers[i].node.Name.Equals(name, StringComparison.Ordinal)) { + node = m_Layers[i].node; + return true; + } + + if (m_Layers[i].node.TryFindChild(name, out IReAnimatorNode childNode)) { + node = childNode; + return true; + } + } + + node = null; + return false; + } + + public void SetLayerWeight(int index, float weight) { + m_Layers[index].targetWeight = weight; + } + } +} diff --git a/Runtime/Engine/Code/Animations/LayerMixerNode.cs.meta b/Runtime/Engine/Code/Animations/LayerMixerNode.cs.meta new file mode 100644 index 0000000..b20c2ba --- /dev/null +++ b/Runtime/Engine/Code/Animations/LayerMixerNode.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 977149bceb2944b1b405035704d40f52 +timeCreated: 1754706607 \ No newline at end of file diff --git a/Runtime/Engine/Code/Animations/MixerNode.cs b/Runtime/Engine/Code/Animations/MixerNode.cs index 0272191..2714d4c 100644 --- a/Runtime/Engine/Code/Animations/MixerNode.cs +++ b/Runtime/Engine/Code/Animations/MixerNode.cs @@ -47,6 +47,8 @@ namespace RebootKit.Engine.Animations { m_Mixer.ConnectInput(i, mixerPlayable, 0, m_Inputs[i].targetWeight); } else if (playable is AnimationClipPlayable clipPlayable) { m_Mixer.ConnectInput(i, clipPlayable, 0, m_Inputs[i].targetWeight); + } else if (playable is AnimationLayerMixerPlayable layerMixerPlayable) { + m_Mixer.ConnectInput(i, layerMixerPlayable, 0, m_Inputs[i].targetWeight); } } diff --git a/Runtime/Engine/Code/Animations/ReAnimator.cs b/Runtime/Engine/Code/Animations/ReAnimator.cs index e2e0d16..bedd5ad 100644 --- a/Runtime/Engine/Code/Animations/ReAnimator.cs +++ b/Runtime/Engine/Code/Animations/ReAnimator.cs @@ -17,55 +17,27 @@ namespace RebootKit.Engine.Animations { bool TryFindChild(string name, out IReAnimatorNode node); } - - [Serializable] - class ReAnimatorLayer { - public string name; - public AvatarMask mask; - public bool isAdditive; - [Range(0.0f, 1.0f)] public float weight = 1.0f; - - [SerializeReference] public IReAnimatorNode root; - } [DefaultExecutionOrder(-100)] public class ReAnimator : MonoBehaviour { static readonly Logger s_Logger = new Logger(nameof(ReAnimator)); [SerializeField] Animator m_Animator; - [SerializeField] ReAnimatorLayer[] m_Layers; + [SerializeField] LayerMixerNode m_Root; PlayableGraph m_Graph; AnimationPlayableOutput m_Output; - AnimationLayerMixerPlayable m_LayerMixer; - void Awake() { Assert.IsNotNull(m_Animator, "Animator is not set"); - Assert.IsTrue(m_Layers.Length > 0, "Atleast one layer must be defined!"); m_Animator.runtimeAnimatorController = null; m_Graph = PlayableGraph.Create(name); m_Output = AnimationPlayableOutput.Create(m_Graph, "Animation Output", m_Animator); - m_LayerMixer = AnimationLayerMixerPlayable.Create(m_Graph, m_Layers.Length); - m_Output.SetSourcePlayable(m_LayerMixer, 0); - - for (int i = 0; i < m_Layers.Length; ++i) { - IPlayable playable = m_Layers[i].root.Build(m_Graph); - - if (playable is AnimationMixerPlayable mixerPlayable) { - m_LayerMixer.ConnectInput(i, mixerPlayable, 0, m_Layers[i].weight); - } else if (playable is AnimationClipPlayable clipPlayable) { - m_LayerMixer.ConnectInput(i, clipPlayable, 0, m_Layers[i].weight); - } - - m_LayerMixer.SetLayerAdditive((uint)i, m_Layers[i].isAdditive); - - if (m_Layers[i].mask != null) { - m_LayerMixer.SetLayerMaskFromAvatarMask((uint)i, m_Layers[i].mask); - } + if (m_Root.Build(m_Graph) is AnimationLayerMixerPlayable layerMixer) { + m_Output.SetSourcePlayable(layerMixer, 0); } m_Graph.Play(); @@ -78,27 +50,18 @@ namespace RebootKit.Engine.Animations { } void Update() { - for (int i = 0; i < m_Layers.Length; i++) { - m_Layers[i].root.Tick(Time.deltaTime); - } + m_Root.Tick(Time.deltaTime); } public void SetLayerWeight(int layer, float weight) { - m_Layers[layer].weight = weight; - m_LayerMixer.SetInputWeight(layer, weight); + m_Root.SetLayerWeight(layer, weight); } public IReAnimatorNode FindNode(string nodeName) { - foreach (ReAnimatorLayer layer in m_Layers) { - if (layer.root.Name.Equals(nodeName, StringComparison.Ordinal)) { - return layer.root; - } - - if (layer.root.TryFindChild(nodeName, out IReAnimatorNode childNode)) { - return childNode; - } + if (m_Root.TryFindChild(nodeName, out IReAnimatorNode node)) { + return node; } - + s_Logger.Error($"Couldn't find node with name: {nodeName}"); return null; } diff --git a/Runtime/Engine/Code/Main/RR.cs b/Runtime/Engine/Code/Main/RR.cs index f39d7ed..e6cbedb 100755 --- a/Runtime/Engine/Code/Main/RR.cs +++ b/Runtime/Engine/Code/Main/RR.cs @@ -224,6 +224,15 @@ namespace RebootKit.Engine.Main { return Network.Actors.SpawnActor(assetReference, position, rotation); } + public static void DestroyActor(Actor actor) { + if (actor == null) { + s_Logger.Error("Cannot destroy actor. Actor is null."); + return; + } + + Network.Actors.DestroyActor(actor); + } + public static Actor FindSpawnedActor(ushort actorID) { if (Network is null) { s_Logger.Error("NetworkSystemInstance is not initialized. Cannot find actor."); @@ -252,6 +261,16 @@ namespace RebootKit.Engine.Main { Network.SendPossessedActor(clientID, actorID); } + public static IEnumerable Actors() { + foreach (Actor actor in Network.Actors.InSceneActors) { + yield return actor; + } + + foreach (Actor actor in Network.Actors.SpawnedActors) { + yield return actor; + } + } + // // @MARK: Service API // Services seems to be useless in the current architecture. Consider removing this API in the future. diff --git a/Runtime/Engine/Code/Shaders.meta b/Runtime/Engine/Code/Shaders.meta new file mode 100644 index 0000000..a031c85 --- /dev/null +++ b/Runtime/Engine/Code/Shaders.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8f2313df373f454f9f49020342ed07a8 +timeCreated: 1754968660 \ No newline at end of file diff --git a/Runtime/Engine/Code/Shaders/StencilMask.shader b/Runtime/Engine/Code/Shaders/StencilMask.shader new file mode 100644 index 0000000..e245e72 --- /dev/null +++ b/Runtime/Engine/Code/Shaders/StencilMask.shader @@ -0,0 +1,22 @@ +Shader "Unlit/StencilMask" { + Properties { + [IntRange] _StencilID("Stencil ID", Range(0, 255)) = 0 + } + SubShader { + Tags { + "RenderType"="Opaque" + "Queue"="Geometry-1" + "RenderPipeline" = "UniversalPipeline" + } + + Pass { + Blend Zero One + ZWrite Off + Stencil { + Ref [_StencilID] + Comp Always + Pass Replace + } + } + } +} \ No newline at end of file diff --git a/Runtime/Engine/Code/Shaders/StencilMask.shader.meta b/Runtime/Engine/Code/Shaders/StencilMask.shader.meta new file mode 100644 index 0000000..d3a59de --- /dev/null +++ b/Runtime/Engine/Code/Shaders/StencilMask.shader.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 20ca2eacae4c45dd891af0222af0295a +timeCreated: 1754968671 \ No newline at end of file diff --git a/Runtime/Engine/Code/Simulation/Actor.cs b/Runtime/Engine/Code/Simulation/Actor.cs index fa65b41..c19eb0a 100644 --- a/Runtime/Engine/Code/Simulation/Actor.cs +++ b/Runtime/Engine/Code/Simulation/Actor.cs @@ -10,6 +10,7 @@ using Unity.Collections; using Unity.Mathematics; using UnityEngine; using UnityEngine.Assertions; +using UnityEngine.ResourceManagement.AsyncOperations; using Logger = RebootKit.Engine.Foundation.Logger; namespace RebootKit.Engine.Simulation { @@ -233,16 +234,17 @@ namespace RebootKit.Engine.Simulation { } } + [DeclareFoldoutGroup("Actor")] public abstract class Actor : MonoBehaviour { static readonly Logger s_ActorLogger = new Logger(nameof(Actor)); - [field: SerializeField, TriInspector.ReadOnly] public string SourceActorPath { get; internal set; } = ""; - [field: SerializeField, TriInspector.ReadOnly] public ulong ActorStaticID { get; internal set; } - [field: SerializeField, TriInspector.ReadOnly] public ushort ActorID { get; internal set; } + [field: SerializeField, TriInspector.ReadOnly, Group("Actor")] public string SourceActorPath { get; internal set; } = ""; + [field: SerializeField, TriInspector.ReadOnly, Group("Actor")] public ulong ActorStaticID { get; internal set; } + [field: SerializeField, TriInspector.ReadOnly, Group("Actor")] public ushort ActorID { get; internal set; } [NonSerialized] internal IActorData Data; - [SerializeField] string m_ActorName = ""; + [SerializeField, Group("Actor")] string m_ActorName = ""; public string ActorName { get { @@ -252,20 +254,20 @@ namespace RebootKit.Engine.Simulation { internal bool IsLocalOnly; - [SerializeField] internal Rigidbody actorRigidbody; + [SerializeField, Group("Actor")] internal Rigidbody actorRigidbody; [InfoBox("If empty, will use GetComponentsInChildren() to find colliders.")] - [SerializeField] Collider[] m_OverrideActorColliders; + [SerializeField, Group("Actor")] Collider[] m_OverrideActorColliders; - [SerializeField] bool m_SetKinematicOnMount = true; - [SerializeField] bool m_DisableCollidersOnMount = true; + [SerializeField, Group("Actor")] bool m_SetKinematicOnMount = true; + [SerializeField, Group("Actor")] bool m_DisableCollidersOnMount = true; internal ActorFlags Flags = ActorFlags.None; // @NOTE: Sync won't happen if actor is mounted to another actor. - [SerializeField] internal bool syncTransform = true; - [SerializeField] internal bool syncPosition = true; - [SerializeField] internal bool syncRotation = true; - [SerializeField] internal bool syncScale = false; + [SerializeField, Group("Actor")] internal bool syncTransform = true; + [SerializeField, Group("Actor")] internal bool syncPosition = true; + [SerializeField, Group("Actor")] internal bool syncRotation = true; + [SerializeField, Group("Actor")] internal bool syncScale = false; class ActorClientState { public ulong LastSyncTick; @@ -286,7 +288,7 @@ namespace RebootKit.Engine.Simulation { public Quaternion localRotation; } - [SerializeField] AttachmentSocket[] m_AttachmentSockets; + [SerializeField, Group("Actor")] AttachmentSocket[] m_AttachmentSockets; // @NOTE: Master actor is the actor that this actor is attached to, if any. internal Actor MasterActor; @@ -296,6 +298,8 @@ namespace RebootKit.Engine.Simulation { public bool IsDataDirty { get; protected internal set; } internal ActorsManager Manager; + internal AsyncOperationHandle AssetHandle; + internal DateTime LastCoreStateSyncTime = DateTime.MinValue; // diff --git a/Runtime/Engine/Code/Simulation/ActorsManager.cs b/Runtime/Engine/Code/Simulation/ActorsManager.cs index a0d20fd..048a46c 100644 --- a/Runtime/Engine/Code/Simulation/ActorsManager.cs +++ b/Runtime/Engine/Code/Simulation/ActorsManager.cs @@ -9,6 +9,7 @@ using Unity.Collections; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.Assertions; +using UnityEngine.ResourceManagement.AsyncOperations; using Logger = RebootKit.Engine.Foundation.Logger; using Object = UnityEngine.Object; @@ -21,14 +22,15 @@ namespace RebootKit.Engine.Simulation { readonly NetworkSystem m_Network; - readonly List m_InSceneActors = new List(); - readonly List m_SpawnedActors = new List(); + internal readonly List InSceneActors = new List(); + internal readonly List SpawnedActors = new List(); // @NOTE: 0 is reserved for no actor so we should start assigning IDs from 1. ushort m_ActorIDCounter; + List m_ActorIDFreeList = new List(ushort.MaxValue); - public ushort InSceneActorsCount { get { return (ushort) m_InSceneActors.Count; } } - public ushort SpawnedActorsCount { get { return (ushort) m_SpawnedActors.Count; } } + public ushort InSceneActorsCount { get { return (ushort) InSceneActors.Count; } } + public ushort SpawnedActorsCount { get { return (ushort) SpawnedActors.Count; } } public int TotalActorsCount { get { return InSceneActorsCount + SpawnedActorsCount; } } public ActorsManager(NetworkSystem networkSystem) { @@ -41,11 +43,11 @@ namespace RebootKit.Engine.Simulation { // @MARK: Update // public void Tick(float deltaTime) { - foreach (Actor actor in m_InSceneActors) { + foreach (Actor actor in InSceneActors) { actor.OnClientTick(deltaTime); } - foreach (Actor actor in m_SpawnedActors) { + foreach (Actor actor in SpawnedActors) { actor.OnClientTick(deltaTime); } } @@ -58,8 +60,8 @@ namespace RebootKit.Engine.Simulation { return; } - TickActorsList(m_InSceneActors, dt); - TickActorsList(m_SpawnedActors, dt); + TickActorsList(InSceneActors, dt); + TickActorsList(SpawnedActors, dt); } void TickActorsList(List actors, float deltaTime) { @@ -137,19 +139,14 @@ namespace RebootKit.Engine.Simulation { return null; } - GameObject actorObject = assetReference.InstantiateAsync(position, rotation).WaitForCompletion(); - Actor actor = actorObject.GetComponent(); - if (actor is null) { - s_Logger.Error($"GameObject {actorObject.name} does not have an Actor component."); - Object.Destroy(actorObject); - return null; - } - - actor.Manager = this; - actor.SourceActorPath = assetReference.AssetGUID; - actor.ActorID = actorID; - actor.Data = actor.InternalCreateActorData(); - m_SpawnedActors.Add(actor); + Actor actor = InstantiateActor(assetReference.AssetGUID, + actorID, + position, + rotation, + null, + default); + actor.IsLocalOnly = false; + SpawnedActors.Add(actor); foreach (NetworkClientState client in m_Network.Clients.Values) { if (client.IsServer) { @@ -166,6 +163,18 @@ namespace RebootKit.Engine.Simulation { } bool TryGenerateNextActorID(out ushort actorID) { + if (m_ActorIDFreeList.Count > 0) { + actorID = m_ActorIDFreeList[0]; + m_ActorIDFreeList.RemoveAtSwapBack(0); + return true; + } + + if (m_ActorIDCounter >= ushort.MaxValue) { + s_Logger.Error("Reached maximum number of actor ids which is: " + ushort.MaxValue); + actorID = 0; + return false; + } + m_ActorIDCounter += 1; actorID = m_ActorIDCounter; return true; @@ -178,44 +187,42 @@ namespace RebootKit.Engine.Simulation { } m_ActorIDCounter = 0; + m_ActorIDFreeList.Clear(); - foreach (Actor actor in m_InSceneActors) { + foreach (Actor actor in InSceneActors) { if (!TryGenerateNextActorID(out ushort actorID)) { s_Logger.Error("Failed to generate actor ID. Probably reached the limit of 65535 actors."); return; } - + actor.ActorID = actorID; } } - - public Actor SpawnLocalOnlyActor(AssetReferenceGameObject assetReference, Vector3 position, Quaternion rotation) { - if (!assetReference.RuntimeKeyIsValid()) { - s_Logger.Error("Trying to spawn an actor with an invalid asset reference."); - return null; + + public void DestroyActor(Actor actor) { + if (actor.IsLocalOnly) { + Object.Destroy(actor.gameObject); + return; } - if (!TryGenerateNextActorID(out ushort actorID)) { - s_Logger.Error("Cannot spawn actor: Failed to generate next actor ID."); - return null; + if (!RR.IsServer()) { + s_Logger.Error("Only the server can destroy non-local only actors"); + return; } - GameObject actorObject = assetReference.InstantiateAsync(position, rotation).WaitForCompletion(); - Actor actor = actorObject.GetComponent(); - if (actor is null) { - s_Logger.Error($"GameObject {actorObject.name} does not have an Actor component."); - Object.Destroy(actorObject); - return null; + if (InSceneActors.Contains(actor)) { + s_Logger.Error("InScene actors cannot be destroyed!"); + return; } - actor.Manager = this; - actor.IsLocalOnly = true; - actor.SourceActorPath = assetReference.AssetGUID; - actor.ActorID = actorID; - actor.Data = actor.InternalCreateActorData(); - // m_SpawnedActors.Add(actor); + for (int i = SpawnedActors.Count - 1; i >= 0; --i) { + if (SpawnedActors[i] == actor) { + m_ActorIDFreeList.Add(actor.ActorID); + return; + } + } - return actor; + s_Logger.Error($"Failed to destroy an actor(name: {actor.ActorName}, id: {actor.ActorID})"); } // @@ -228,11 +235,11 @@ namespace RebootKit.Engine.Simulation { actor.Manager = this; - m_InSceneActors.Add(actor); + InSceneActors.Add(actor); } Actor FindInSceneActorWithStaticID(ulong staticID) { - foreach (Actor actor in m_InSceneActors) { + foreach (Actor actor in InSceneActors) { if (actor.ActorStaticID == staticID) { return actor; } @@ -242,13 +249,13 @@ namespace RebootKit.Engine.Simulation { } public Actor FindActorByID(ushort actorID) { - foreach (Actor actor in m_InSceneActors) { + foreach (Actor actor in InSceneActors) { if (actor.ActorID == actorID) { return actor; } } - foreach (Actor actor in m_SpawnedActors) { + foreach (Actor actor in SpawnedActors) { if (actor.ActorID == actorID) { return actor; } @@ -258,15 +265,40 @@ namespace RebootKit.Engine.Simulation { } public void CleanUp() { - m_InSceneActors.Clear(); + InSceneActors.Clear(); - foreach (Actor actor in m_SpawnedActors) { + foreach (Actor actor in SpawnedActors) { if (actor.OrNull() != null) { Object.Destroy(actor.gameObject); } } - m_SpawnedActors.Clear(); + SpawnedActors.Clear(); + } + + public Actor SpawnLocalOnlyActor(AssetReferenceGameObject assetReference, Vector3 position, Quaternion rotation) { + if (!assetReference.RuntimeKeyIsValid()) { + s_Logger.Error("Trying to spawn an actor with an invalid asset reference."); + return null; + } + + AsyncOperationHandle handle = Addressables.InstantiateAsync(assetReference, position, rotation); + GameObject actorObject = handle.WaitForCompletion(); + Actor actor = actorObject.GetComponent(); + if (actor == null) { + s_Logger.Error($"GameObject {actorObject.name} does not have an Actor component."); + handle.Release(); + return null; + } + + actor.Manager = this; + actor.AssetHandle = handle; + actor.IsLocalOnly = true; + actor.SourceActorPath = assetReference.AssetGUID; + actor.ActorID = 0; + actor.Data = actor.InternalCreateActorData(); + + return actor; } /// @@ -355,50 +387,55 @@ namespace RebootKit.Engine.Simulation { return; } - SpawnLocalActor(assetGUID.ToString(), - coreState, - stateData); + InstantiateActor(assetGUID.ToString(), + coreState.ActorID, + coreState.Position, + coreState.Rotation, + coreState, + stateData); } - void SpawnLocalActor(string guid, - ActorCoreStateSnapshot coreStateSnapshot, - NativeSlice stateData) { + Actor InstantiateActor(string guid, + ushort actorID, + Vector3 position, + Quaternion rotation, + ActorCoreStateSnapshot? coreStateSnapshot, + NativeSlice stateData) { AssetReferenceGameObject assetReference = new AssetReferenceGameObject(guid); if (!assetReference.RuntimeKeyIsValid()) { s_Logger.Error($"Invalid asset reference for actor with GUID {guid}"); - return; + return null; } - GameObject actorObject = assetReference - .InstantiateAsync(coreStateSnapshot.Position, coreStateSnapshot.Rotation) - .WaitForCompletion(); - if (actorObject == null) { - s_Logger.Error($"Failed to instantiate actor with GUID {guid}"); - return; - } + AsyncOperationHandle handle = Addressables.InstantiateAsync(assetReference, position, rotation); + GameObject actorObject = handle.WaitForCompletion(); Actor actor = actorObject.GetComponent(); - if (actor is null) { + if (actor == null) { s_Logger.Error($"GameObject {actorObject.name} does not have an Actor component."); - Object.Destroy(actorObject); - return; + Addressables.ReleaseInstance(handle); + return null; } actor.Manager = this; + actor.AssetHandle = handle; actor.SourceActorPath = guid; - actor.ActorID = coreStateSnapshot.ActorID; + actor.ActorID = actorID; actor.Data = actor.InternalCreateActorData(); if (!RR.IsServer()) { actor.InitializeOnClient(); } - actor.RestoreCoreState(coreStateSnapshot); + if (coreStateSnapshot.HasValue) { + actor.RestoreCoreState(coreStateSnapshot.Value); + } + if (stateData.Length > 0) { DataSerializationUtils.Deserialize(stateData, ref actor.Data); } - m_SpawnedActors.Add(actor); + return actor; } // @@ -427,7 +464,7 @@ namespace RebootKit.Engine.Simulation { internal void WriteInSceneActorsStates(NetworkBufferWriter writer) { writer.Write((ushort) InSceneActorsCount); - foreach (Actor actor in m_InSceneActors) { + foreach (Actor actor in InSceneActors) { writer.Write(actor.ActorStaticID); writer.Write(actor.ActorID); actor.GetCoreStateSnapshot().Serialize(writer); @@ -443,7 +480,7 @@ namespace RebootKit.Engine.Simulation { for (int i = 0; i < count; ++i) { reader.Read(out ulong actorStaticID); reader.Read(out ushort actorID); - + s_Logger.Info($"Reading actor with StaticID {actorStaticID} and ID {actorID} during synchronization."); Actor actor = FindInSceneActorWithStaticID(actorStaticID); @@ -467,8 +504,8 @@ namespace RebootKit.Engine.Simulation { actor.ActorID = actorID; s_Logger.Info("Actor id set to " + actor.ActorID); } - - foreach (Actor inSceneActor in m_InSceneActors) { + + foreach (Actor inSceneActor in InSceneActors) { s_Logger.Info($"InSceneActor: StaticID={inSceneActor.ActorStaticID}, ID={inSceneActor.ActorID}, Path={inSceneActor.SourceActorPath}"); } @@ -481,7 +518,7 @@ namespace RebootKit.Engine.Simulation { return; } - foreach (Actor actor in m_SpawnedActors) { + foreach (Actor actor in SpawnedActors) { if (actor.OrNull() == null) { continue; }