extensions and actor changes

This commit is contained in:
2025-06-22 10:44:48 +02:00
parent 62b484fa49
commit b1050f627b
3 changed files with 99 additions and 66 deletions

View File

@@ -23,6 +23,13 @@ namespace RebootKit.Engine.Extensions {
} }
public static class float3Ex { public static class float3Ex {
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float3 With(this float3 vec, float? x = null, float? y = null, float? z = null) {
return new float3(x ?? vec.x,
y ?? vec.y,
z ?? vec.z);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsZero(this float3 vec, float epsilon = float.Epsilon) { public static bool IsZero(this float3 vec, float epsilon = float.Epsilon) {
return math.abs(vec.x) < epsilon && math.abs(vec.y) < epsilon && math.abs(vec.z) < epsilon; return math.abs(vec.x) < epsilon && math.abs(vec.y) < epsilon && math.abs(vec.z) < epsilon;

View File

@@ -1,32 +1,25 @@
using System; using RebootKit.Engine.Foundation;
using System.Threading;
using Cysharp.Threading.Tasks;
using RebootKit.Engine.Foundation;
using UnityEngine; using UnityEngine;
namespace RebootKit.Engine.Services.Simulation { namespace RebootKit.Engine.Services.Simulation {
public class Actor : MonoBehaviour { public abstract class Actor : MonoBehaviour {
[field: SerializeField] [field: SerializeField]
public SerializableGuid ActorGuid { get; private set; } = SerializableGuid.New(); public SerializableGuid ActorGuid { get; private set; } = SerializableGuid.New();
public bool IsInitialized { get; private set; } bool m_IsPlaying = false;
public bool IsPlaying {
bool m_Simulate; get {
public bool Simulate { return m_IsPlaying;
get => m_Simulate; }
set { set {
if (m_Simulate == value) { if (m_IsPlaying == value) {
return; return;
} }
m_Simulate = value; m_IsPlaying = value;
if (!IsInitialized) { if (m_IsPlaying) {
return;
}
if (m_Simulate) {
OnBeginPlay(); OnBeginPlay();
} else { } else {
OnEndPlay(); OnEndPlay();
@@ -34,24 +27,10 @@ namespace RebootKit.Engine.Services.Simulation {
} }
} }
async void Start() { public virtual void OnSpawned() {
IsInitialized = false;
await InitAsync(destroyCancellationToken);
IsInitialized = true;
if (Simulate) {
OnBeginPlay();
}
} }
void Update() { public virtual void OnDespawned() {
if (m_Simulate && IsInitialized) {
OnTick();
}
}
protected virtual async UniTask InitAsync(CancellationToken cancellationToken) {
await UniTask.Yield(cancellationToken);
} }
public virtual void OnBeginPlay() { public virtual void OnBeginPlay() {
@@ -60,7 +39,7 @@ namespace RebootKit.Engine.Services.Simulation {
public virtual void OnEndPlay() { public virtual void OnEndPlay() {
} }
public virtual void OnTick() { public virtual void OnTick(float deltaTime) {
} }
} }
} }

View File

@@ -1,7 +1,8 @@
using System.Collections.Generic; using System;
using System.Linq; using System.Collections.Generic;
using System.Threading; using System.Threading;
using Cysharp.Threading.Tasks; using Cysharp.Threading.Tasks;
using R3;
using RebootKit.Engine.Foundation; using RebootKit.Engine.Foundation;
using UnityEngine; using UnityEngine;
using UnityEngine.AddressableAssets; using UnityEngine.AddressableAssets;
@@ -12,12 +13,11 @@ using UnityEngine.SceneManagement;
using Logger = RebootKit.Engine.Foundation.Logger; using Logger = RebootKit.Engine.Foundation.Logger;
namespace RebootKit.Engine.Services.Simulation { namespace RebootKit.Engine.Services.Simulation {
public interface IWorldContext { public interface IWorldContext { }
}
public class WorldService : IService { public class WorldService : IService {
static readonly Logger s_Logger = new Logger(nameof(WorldService)); static readonly Logger s_Logger = new Logger(nameof(WorldService));
enum WorldState { enum WorldState {
Unloaded, Unloaded,
Loading, Loading,
@@ -28,14 +28,31 @@ namespace RebootKit.Engine.Services.Simulation {
WorldConfig m_Config; WorldConfig m_Config;
AsyncOperationHandle<SceneInstance> m_SceneInstance; AsyncOperationHandle<SceneInstance> m_SceneInstance;
struct ActorData {
public Actor Actor;
public readonly bool ManagedByAddressabled;
public ActorData(Actor actor, bool managedByAddressabled) {
Actor = actor;
ManagedByAddressabled = managedByAddressabled;
}
}
readonly List<ActorData> m_Actors = new List<ActorData>();
readonly IDisposable m_UpdateSubscription;
public IWorldContext Context { get; private set; } public IWorldContext Context { get; private set; }
readonly List<Actor> m_Actors = new List<Actor>(); public WorldService() {
m_UpdateSubscription = Observable.EveryUpdate()
.Subscribe(_ => { Tick(Time.deltaTime); });
}
public void Dispose() { public void Dispose() {
m_UpdateSubscription.Dispose();
Unload(); Unload();
} }
public async UniTask LoadAsync(WorldConfig worldConfig, CancellationToken cancellationToken) { public async UniTask LoadAsync(WorldConfig worldConfig, CancellationToken cancellationToken) {
await UniTask.WaitWhile(() => m_WorldState == WorldState.Loading, cancellationToken: cancellationToken); await UniTask.WaitWhile(() => m_WorldState == WorldState.Loading, cancellationToken: cancellationToken);
@@ -46,35 +63,24 @@ namespace RebootKit.Engine.Services.Simulation {
m_SceneInstance = worldConfig.mainScene.LoadSceneAsync(LoadSceneMode.Additive, false); m_SceneInstance = worldConfig.mainScene.LoadSceneAsync(LoadSceneMode.Additive, false);
await m_SceneInstance.ToUniTask(cancellationToken: cancellationToken); await m_SceneInstance.ToUniTask(cancellationToken: cancellationToken);
await m_SceneInstance.Result.ActivateAsync(); await m_SceneInstance.Result.ActivateAsync();
SceneManager.SetActiveScene(m_SceneInstance.Result.Scene); SceneManager.SetActiveScene(m_SceneInstance.Result.Scene);
foreach (GameObject root in m_SceneInstance.Result.Scene.GetRootGameObjects()) { foreach (GameObject root in m_SceneInstance.Result.Scene.GetRootGameObjects()) {
if (root.TryGetComponent(out IWorldContext worldContext)) { if (root.TryGetComponent(out IWorldContext worldContext)) {
Assert.IsNull(Context, "WorldContext is already set. There should be only one WorldContext in the scene."); Assert.IsNull(Context,
"WorldContext is already set. There should be only one WorldContext in the scene.");
Context = worldContext; Context = worldContext;
} }
foreach (Actor actor in root.GetComponentsInChildren<Actor>()) { foreach (Actor actor in root.GetComponentsInChildren<Actor>()) {
RegisterActor(actor); m_Actors.Add(new ActorData(actor, false));
} }
} }
m_WorldState = WorldState.Loaded; m_WorldState = WorldState.Loaded;
} }
public async UniTask<TRequiredActor> LoadAsync<TRequiredActor>(WorldConfig worldConfig, CancellationToken cancellationToken) where TRequiredActor : Actor {
await LoadAsync(worldConfig, cancellationToken);
TRequiredActor actor = m_Actors.FirstOrDefault(t => t is TRequiredActor) as TRequiredActor;
if (actor is null) {
s_Logger.Error($"Actor of type {typeof(TRequiredActor)} not found in the scene");
return null;
}
return actor;
}
public void Unload() { public void Unload() {
KillAllActors(); KillAllActors();
@@ -88,7 +94,13 @@ namespace RebootKit.Engine.Services.Simulation {
Context = null; Context = null;
} }
public async UniTask<TActor> SpawnActor<TActor>(AssetReferenceT<GameObject> asset, CancellationToken cancellationToken) where TActor : Actor { public async UniTask<TActor> SpawnActor<TActor>(AssetReferenceT<GameObject> asset,
CancellationToken cancellationToken) where TActor : Actor {
if (m_WorldState != WorldState.Loaded) {
s_Logger.Error("World is not loaded. Cannot spawn actor.");
return null;
}
GameObject gameObject = await Addressables.InstantiateAsync(asset); GameObject gameObject = await Addressables.InstantiateAsync(asset);
if (cancellationToken.IsCancellationRequested) { if (cancellationToken.IsCancellationRequested) {
asset.ReleaseInstance(gameObject); asset.ReleaseInstance(gameObject);
@@ -96,6 +108,9 @@ namespace RebootKit.Engine.Services.Simulation {
} }
if (gameObject.TryGetComponent(out TActor actor)) { if (gameObject.TryGetComponent(out TActor actor)) {
actor.OnSpawned();
actor.IsPlaying = true;
m_Actors.Add(new ActorData(actor, true));
return actor; return actor;
} }
@@ -103,20 +118,52 @@ namespace RebootKit.Engine.Services.Simulation {
return null; return null;
} }
public void RegisterActor(Actor actor) {
m_Actors.Add(actor);
}
public void KillActor(Actor actor) { public void KillActor(Actor actor) {
Addressables.ReleaseInstance(actor.gameObject); ActorData actorData = default;
bool found = false;
for (int i = m_Actors.Count - 1; i >= 0; i--) {
if (m_Actors[i].Actor == actor) {
found = true;
actorData = m_Actors[i];
m_Actors.RemoveAt(i);
break;
}
}
Assert.IsTrue(found, $"Actor {actor.name} not found in the world actors list.");
actor.IsPlaying = false;
actor.OnDespawned();
if (actorData.ManagedByAddressabled) {
Addressables.ReleaseInstance(actor.gameObject);
}
} }
public void KillAllActors() { public void KillAllActors() {
foreach (Actor actor in m_Actors) { foreach (ActorData actorData in m_Actors) {
Addressables.ReleaseInstance(actor.gameObject); actorData.Actor.IsPlaying = false;
actorData.Actor.OnDespawned();
if (actorData.ManagedByAddressabled) {
Addressables.ReleaseInstance(actorData.Actor.gameObject);
} else {
UnityEngine.Object.Destroy(actorData.Actor.gameObject);
}
} }
m_Actors.Clear(); m_Actors.Clear();
} }
void Tick(float deltaTime) {
if (m_WorldState != WorldState.Loaded) {
return;
}
foreach (ActorData actorData in m_Actors) {
if (actorData.Actor.IsPlaying) {
actorData.Actor.OnTick(deltaTime);
}
}
}
} }
} }