Checkpoint 2:

- 调整 SimulationWorld 作为 GameFrameworkComponent 组件
- ProcedureGame 接入 SimulationWorld 生命周期(创建/清理)并在敌人 Show/Hide 时同步注册/反注册
- 增加 EnemySimData 构建与缓存去重移除,避免重复注册和悬空映射
- SimulationWorld 增加 UpsertEnemy/UpsertProjectile/UpsertPickup,支持幂等注册
This commit is contained in:
SepComet 2026-02-20 18:47:00 +08:00
parent 3b8e0731f0
commit 83f8a356f7
6 changed files with 187 additions and 20 deletions

View File

@ -6,6 +6,7 @@
//------------------------------------------------------------ //------------------------------------------------------------
using CustomComponent; using CustomComponent;
using Simulation;
using StarForce; using StarForce;
using UI; using UI;
using UnityEngine; using UnityEngine;
@ -20,12 +21,15 @@ public partial class GameEntry : MonoBehaviour
public static HPBarComponent HPBar { get; private set; } public static HPBarComponent HPBar { get; private set; }
public static DamageTextComponent DamageText { get; private set; } public static DamageTextComponent DamageText { get; private set; }
#if UNITY_EDITOR || DEVELOPMENT_BUILD #if UNITY_EDITOR || DEVELOPMENT_BUILD
public static RuntimeDebugPanelComponent RuntimeDebugPanel { get; private set; } public static RuntimeDebugPanelComponent RuntimeDebugPanel { get; private set; }
#endif #endif
public static EnemyManagerComponent EnemyManager { get; private set; } public static EnemyManagerComponent EnemyManager { get; private set; }
public static SimulationWorld SimulationWorld { get; private set; }
public static SpriteCacheComponent SpriteCache { get; private set; } public static SpriteCacheComponent SpriteCache { get; private set; }
public static UIRouterComponent UIRouter { get; private set; } public static UIRouterComponent UIRouter { get; private set; }
@ -35,19 +39,14 @@ public partial class GameEntry : MonoBehaviour
BuiltinData = UnityGameFramework.Runtime.GameEntry.GetComponent<BuiltinDataComponent>(); BuiltinData = UnityGameFramework.Runtime.GameEntry.GetComponent<BuiltinDataComponent>();
HPBar = UnityGameFramework.Runtime.GameEntry.GetComponent<HPBarComponent>(); HPBar = UnityGameFramework.Runtime.GameEntry.GetComponent<HPBarComponent>();
DamageText = UnityGameFramework.Runtime.GameEntry.GetComponent<DamageTextComponent>(); DamageText = UnityGameFramework.Runtime.GameEntry.GetComponent<DamageTextComponent>();
if (DamageText == null && Base != null)
{
DamageText = Base.gameObject.AddComponent<DamageTextComponent>();
}
#if UNITY_EDITOR || DEVELOPMENT_BUILD #if UNITY_EDITOR || DEVELOPMENT_BUILD
RuntimeDebugPanel = UnityGameFramework.Runtime.GameEntry.GetComponent<RuntimeDebugPanelComponent>(); RuntimeDebugPanel = UnityGameFramework.Runtime.GameEntry.GetComponent<RuntimeDebugPanelComponent>();
if (RuntimeDebugPanel == null && Base != null)
{
RuntimeDebugPanel = Base.gameObject.AddComponent<RuntimeDebugPanelComponent>();
}
#endif #endif
EnemyManager = UnityGameFramework.Runtime.GameEntry.GetComponent<EnemyManagerComponent>(); EnemyManager = UnityGameFramework.Runtime.GameEntry.GetComponent<EnemyManagerComponent>();
SimulationWorld = UnityGameFramework.Runtime.GameEntry.GetComponent<SimulationWorld>();
SpriteCache = UnityGameFramework.Runtime.GameEntry.GetComponent<SpriteCacheComponent>(); SpriteCache = UnityGameFramework.Runtime.GameEntry.GetComponent<SpriteCacheComponent>();
UIRouter = UnityGameFramework.Runtime.GameEntry.GetComponent<UIRouterComponent>(); UIRouter = UnityGameFramework.Runtime.GameEntry.GetComponent<UIRouterComponent>();
} }
} }

View File

@ -5,6 +5,7 @@ using Entity;
using Entity.EntityData; using Entity.EntityData;
using GameFramework.Event; using GameFramework.Event;
using Procedure; using Procedure;
using Simulation;
using StarForce; using StarForce;
using UnityEngine; using UnityEngine;
using UnityGameFramework.Runtime; using UnityGameFramework.Runtime;
@ -213,7 +214,17 @@ namespace CustomComponent
{ {
_currentEnemyCount++; _currentEnemyCount++;
enemy.SetTarget(_player); enemy.SetTarget(_player);
RemoveEnemyFromCache(enemy.Id);
_enemies.Add(enemy); _enemies.Add(enemy);
if (ne.UserData is EnemyData enemyData)
{
GameEntry.SimulationWorld?.UpsertEnemy(CreateEnemySimData(enemy, enemyData));
}
else
{
GameEntry.SimulationWorld?.UpsertEnemy(CreateEnemySimData(enemy, null));
}
} }
if (ne.EntityLogicType == typeof(Player)) if (ne.EntityLogicType == typeof(Player))
@ -228,11 +239,43 @@ namespace CustomComponent
{ {
if (ne.EntityGroup.Name == "Enemy") if (ne.EntityGroup.Name == "Enemy")
{ {
_currentEnemyCount--; if (_currentEnemyCount > 0)
{
_currentEnemyCount--;
}
RemoveEnemyFromCache(ne.EntityId);
GameEntry.SimulationWorld?.RemoveEnemyByEntityId(ne.EntityId);
}
}
}
private static EnemySimData CreateEnemySimData(EnemyBase enemy, EnemyData enemyData)
{
return new EnemySimData
{
EntityId = enemy.Id,
Position = enemy.CachedTransform.position,
Forward = enemy.CachedTransform.forward,
Speed = enemyData != null ? enemyData.SpeedBase : 0f,
AttackRange = 1f,
TargetType = 0,
State = 0
};
}
private void RemoveEnemyFromCache(int entityId)
{
for (int i = _enemies.Count - 1; i >= 0; i--)
{
EntityBase cachedEnemy = _enemies[i];
if (cachedEnemy == null || cachedEnemy.Id == entityId)
{
_enemies.RemoveAt(i);
} }
} }
} }
#endregion #endregion
} }
} }

View File

@ -1,5 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using CustomEvent;
using DataTable; using DataTable;
using Definition.Enum; using Definition.Enum;
using Entity; using Entity;
@ -24,11 +23,11 @@ namespace Procedure
{ {
public override bool UseNativeDialog => false; public override bool UseNativeDialog => false;
private HudForm _hudForm = null; private HudForm _hudForm;
private bool _hudInitialized = false; private bool _hudInitialized;
private IFsm<IProcedureManager> _procedureOwner = null; private IFsm<IProcedureManager> _procedureOwner;
private PlayerData _currentPlayerData = null; private PlayerData _currentPlayerData;
public int CurrentLevel = 1; public int CurrentLevel = 1;
private GameStateType _currentGameState = GameStateType.None; private GameStateType _currentGameState = GameStateType.None;
@ -88,6 +87,7 @@ namespace Procedure
base.OnEnter(procedureOwner); base.OnEnter(procedureOwner);
_procedureOwner = procedureOwner; _procedureOwner = procedureOwner;
GameEntry.SimulationWorld?.Clear();
GameEntry.Event.Subscribe(OpenUIFormSuccessEventArgs.EventId, OpenUIFormSuccess); GameEntry.Event.Subscribe(OpenUIFormSuccessEventArgs.EventId, OpenUIFormSuccess);
GameEntry.Event.Subscribe(ShowEntitySuccessEventArgs.EventId, ShowEntitySuccess); GameEntry.Event.Subscribe(ShowEntitySuccessEventArgs.EventId, ShowEntitySuccess);
@ -135,6 +135,7 @@ namespace Procedure
Player = null; Player = null;
_procedureOwner = null; _procedureOwner = null;
GameEntry.SimulationWorld?.Clear();
GameEntry.Event.Unsubscribe(OpenUIFormSuccessEventArgs.EventId, OpenUIFormSuccess); GameEntry.Event.Unsubscribe(OpenUIFormSuccessEventArgs.EventId, OpenUIFormSuccess);
GameEntry.Event.Unsubscribe(ShowEntitySuccessEventArgs.EventId, ShowEntitySuccess); GameEntry.Event.Unsubscribe(ShowEntitySuccessEventArgs.EventId, ShowEntitySuccess);

View File

@ -1,8 +1,9 @@
using System.Collections.Generic; using System.Collections.Generic;
using UnityGameFramework.Runtime;
namespace Simulation namespace Simulation
{ {
public sealed class SimulationWorld public sealed class SimulationWorld : GameFrameworkComponent
{ {
private readonly List<EnemySimData> _enemies = new List<EnemySimData>(); private readonly List<EnemySimData> _enemies = new List<EnemySimData>();
private readonly List<ProjectileSimData> _projectiles = new List<ProjectileSimData>(); private readonly List<ProjectileSimData> _projectiles = new List<ProjectileSimData>();
@ -24,6 +25,17 @@ namespace Simulation
return simulationIndex; return simulationIndex;
} }
public int UpsertEnemy(in EnemySimData simData)
{
if (EnemyBinding.TryGetSimulationIndex(simData.EntityId, out int simulationIndex))
{
_enemies[simulationIndex] = simData;
return simulationIndex;
}
return AddEnemy(simData);
}
public bool RemoveEnemyByEntityId(int entityId) public bool RemoveEnemyByEntityId(int entityId)
{ {
if (!EnemyBinding.TryGetSimulationIndex(entityId, out int simulationIndex)) if (!EnemyBinding.TryGetSimulationIndex(entityId, out int simulationIndex))
@ -52,6 +64,17 @@ namespace Simulation
return simulationIndex; return simulationIndex;
} }
public int UpsertProjectile(in ProjectileSimData simData)
{
if (ProjectileBinding.TryGetSimulationIndex(simData.EntityId, out int simulationIndex))
{
_projectiles[simulationIndex] = simData;
return simulationIndex;
}
return AddProjectile(simData);
}
public bool RemoveProjectileByEntityId(int entityId) public bool RemoveProjectileByEntityId(int entityId)
{ {
if (!ProjectileBinding.TryGetSimulationIndex(entityId, out int simulationIndex)) if (!ProjectileBinding.TryGetSimulationIndex(entityId, out int simulationIndex))
@ -80,6 +103,17 @@ namespace Simulation
return simulationIndex; return simulationIndex;
} }
public int UpsertPickup(in PickupSimData simData)
{
if (PickupBinding.TryGetSimulationIndex(simData.EntityId, out int simulationIndex))
{
_pickups[simulationIndex] = simData;
return simulationIndex;
}
return AddPickup(simData);
}
public bool RemovePickupByEntityId(int entityId) public bool RemovePickupByEntityId(int entityId)
{ {
if (!PickupBinding.TryGetSimulationIndex(entityId, out int simulationIndex)) if (!PickupBinding.TryGetSimulationIndex(entityId, out int simulationIndex))

View File

@ -241,6 +241,8 @@ Transform:
- {fileID: 472081678} - {fileID: 472081678}
- {fileID: 2050832067} - {fileID: 2050832067}
- {fileID: 534968532} - {fileID: 534968532}
- {fileID: 477326942}
- {fileID: 1652245191}
m_Father: {fileID: 1852670053} m_Father: {fileID: 1852670053}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &120093239 --- !u!1 &120093239
@ -802,6 +804,50 @@ MonoBehaviour:
m_EditorClassIdentifier: m_EditorClassIdentifier:
_pixelsPerUnit: 100 _pixelsPerUnit: 100
_defaultPivot: {x: 0.5, y: 0.5} _defaultPivot: {x: 0.5, y: 0.5}
--- !u!1 &477326941
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 477326942}
- component: {fileID: 477326943}
m_Layer: 0
m_Name: RuntimeDebugPanel
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &477326942
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 477326941}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 119167776}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &477326943
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 477326941}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 1d8ada5157a04921a6e543a040e57960, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1 &513208572 --- !u!1 &513208572
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@ -1336,6 +1382,50 @@ MonoBehaviour:
_hpBarItemTemplate: {fileID: 11414536, guid: 96d2e77dd9853514da336717bd5627c0, type: 3} _hpBarItemTemplate: {fileID: 11414536, guid: 96d2e77dd9853514da336717bd5627c0, type: 3}
_hpBarInstanceRoot: {fileID: 1454214587} _hpBarInstanceRoot: {fileID: 1454214587}
_instancePoolCapacity: 16 _instancePoolCapacity: 16
--- !u!1 &1652245190
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1652245191}
- component: {fileID: 1652245192}
m_Layer: 0
m_Name: SimulationWorld
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &1652245191
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1652245190}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 119167776}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &1652245192
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1652245190}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 8a558ebbc9cb4d94946ac9f4f27914d8, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1 &1852670052 --- !u!1 &1852670052
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0

View File

@ -32,7 +32,7 @@
- 新建 `SimulationTickContext`(至少包含 `deltaTime`、`playerPosition`)。 - 新建 `SimulationTickContext`(至少包含 `deltaTime`、`playerPosition`)。
- 完成标准:工程可编译,场景运行行为与当前一致(只加结构,不切链路)。 - 完成标准:工程可编译,场景运行行为与当前一致(只加结构,不切链路)。
- [ ] Checkpoint 2敌人生命周期接入 Simulation保持 GameFramework 生命周期不变) - [x] Checkpoint 2敌人生命周期接入 Simulation保持 GameFramework 生命周期不变)
- 在敌人 `Show/Hide` 时同步注册/反注册到 `SimulationWorld``EntityBinding` - 在敌人 `Show/Hide` 时同步注册/反注册到 `SimulationWorld``EntityBinding`
- `EnemyManagerComponent` 继续负责刷怪与实体显隐,不改外部调用方式。 - `EnemyManagerComponent` 继续负责刷怪与实体显隐,不改外部调用方式。
- 完成标准:敌人数量统计与当前一致,无重复注册、无悬空索引。 - 完成标准:敌人数量统计与当前一致,无重复注册、无悬空索引。