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

View File

@ -5,6 +5,7 @@ using Entity;
using Entity.EntityData;
using GameFramework.Event;
using Procedure;
using Simulation;
using StarForce;
using UnityEngine;
using UnityGameFramework.Runtime;
@ -213,7 +214,17 @@ namespace CustomComponent
{
_currentEnemyCount++;
enemy.SetTarget(_player);
RemoveEnemyFromCache(enemy.Id);
_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))
@ -227,9 +238,41 @@ namespace CustomComponent
if (e is HideEntityCompleteEventArgs ne)
{
if (ne.EntityGroup.Name == "Enemy")
{
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);
}
}
}

View File

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

View File

@ -1,8 +1,9 @@
using System.Collections.Generic;
using UnityGameFramework.Runtime;
namespace Simulation
{
public sealed class SimulationWorld
public sealed class SimulationWorld : GameFrameworkComponent
{
private readonly List<EnemySimData> _enemies = new List<EnemySimData>();
private readonly List<ProjectileSimData> _projectiles = new List<ProjectileSimData>();
@ -24,6 +25,17 @@ namespace Simulation
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)
{
if (!EnemyBinding.TryGetSimulationIndex(entityId, out int simulationIndex))
@ -52,6 +64,17 @@ namespace Simulation
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)
{
if (!ProjectileBinding.TryGetSimulationIndex(entityId, out int simulationIndex))
@ -80,6 +103,17 @@ namespace Simulation
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)
{
if (!PickupBinding.TryGetSimulationIndex(entityId, out int simulationIndex))

View File

@ -241,6 +241,8 @@ Transform:
- {fileID: 472081678}
- {fileID: 2050832067}
- {fileID: 534968532}
- {fileID: 477326942}
- {fileID: 1652245191}
m_Father: {fileID: 1852670053}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &120093239
@ -802,6 +804,50 @@ MonoBehaviour:
m_EditorClassIdentifier:
_pixelsPerUnit: 100
_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
GameObject:
m_ObjectHideFlags: 0
@ -1336,6 +1382,50 @@ MonoBehaviour:
_hpBarItemTemplate: {fileID: 11414536, guid: 96d2e77dd9853514da336717bd5627c0, type: 3}
_hpBarInstanceRoot: {fileID: 1454214587}
_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
GameObject:
m_ObjectHideFlags: 0

View File

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