extensions and actor changes
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
@@ -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) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user