Checkpoint 5 & Checkpoint 6:
- SimulationWorld 只做逻辑计算与数据输出,不再直接写 Transform - 在 ShowEntitySuccess 中接入 Pickup/Projectile 占位注册: - Drop 走 UpsertPickup(...) - Bullet/Projectile 走 UpsertProjectile(...) - 在 HideEntityComplete 中接入对应回收: - Drop 走 RemovePickupByEntityId(...) - Bullet/Projectile 走 RemoveProjectileByEntityId(...) - 新增占位数据构造: - CreatePickupSimData(...) - CreateProjectileSimData(...) - 调整 EnemyManagerComponent 职责,现在只管“敌人相关”: - 敌人刷怪节奏 - 敌人列表缓存与计数 - 玩家引用与 enemy.SetTarget(...) - 新增嵌套类 SimulationWorld.Presentation,专门消费 EnemySimData 并回写 position/rotation - 新增嵌套类 SimulationWorld.EntitySync 负责“Simulation 同步”: - 监听 ShowEntitySuccess/HideEntityComplete - 同步 Enemy/Drop/Projectile 到 SimulationWorld - 敌人 SimData 构建、Pickup/Projectile 占位数据构建
This commit is contained in:
parent
6494ebc5fd
commit
31fe7a4d61
|
|
@ -1,4 +1,4 @@
|
||||||
//------------------------------------------------------------
|
//------------------------------------------------------------
|
||||||
// Game Framework
|
// Game Framework
|
||||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||||
// Homepage: https://gameframework.cn/
|
// Homepage: https://gameframework.cn/
|
||||||
|
|
@ -46,6 +46,10 @@ public partial class GameEntry : MonoBehaviour
|
||||||
|
|
||||||
EnemyManager = UnityGameFramework.Runtime.GameEntry.GetComponent<EnemyManagerComponent>();
|
EnemyManager = UnityGameFramework.Runtime.GameEntry.GetComponent<EnemyManagerComponent>();
|
||||||
SimulationWorld = UnityGameFramework.Runtime.GameEntry.GetComponent<SimulationWorld>();
|
SimulationWorld = UnityGameFramework.Runtime.GameEntry.GetComponent<SimulationWorld>();
|
||||||
|
if (SimulationWorld == null && Base != null)
|
||||||
|
{
|
||||||
|
SimulationWorld = Base.gameObject.AddComponent<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>();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Components;
|
|
||||||
using DataTable;
|
using DataTable;
|
||||||
using Definition.Enum;
|
using Definition.Enum;
|
||||||
using Entity;
|
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;
|
||||||
|
|
@ -17,6 +15,7 @@ namespace CustomComponent
|
||||||
public class EnemyManagerComponent : GameFrameworkComponent
|
public class EnemyManagerComponent : GameFrameworkComponent
|
||||||
{
|
{
|
||||||
private const float MinSpawnRateScale = 0.1f;
|
private const float MinSpawnRateScale = 0.1f;
|
||||||
|
private const string EnemyGroupName = "Enemy";
|
||||||
|
|
||||||
private EntityComponent _entity;
|
private EntityComponent _entity;
|
||||||
|
|
||||||
|
|
@ -211,21 +210,14 @@ namespace CustomComponent
|
||||||
{
|
{
|
||||||
if (!(e is ShowEntitySuccessEventArgs ne)) return;
|
if (!(e is ShowEntitySuccessEventArgs ne)) return;
|
||||||
|
|
||||||
if (ne.Entity.Logic is EnemyBase enemy)
|
string entityGroupName = ne.Entity?.EntityGroup?.Name;
|
||||||
|
|
||||||
|
if (entityGroupName == EnemyGroupName && ne.Entity.Logic is EnemyBase enemy)
|
||||||
{
|
{
|
||||||
_currentEnemyCount++;
|
_currentEnemyCount++;
|
||||||
enemy.SetTarget(_player);
|
enemy.SetTarget(_player);
|
||||||
RemoveEnemyFromCache(enemy.Id);
|
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))
|
||||||
|
|
@ -238,7 +230,8 @@ namespace CustomComponent
|
||||||
{
|
{
|
||||||
if (e is HideEntityCompleteEventArgs ne)
|
if (e is HideEntityCompleteEventArgs ne)
|
||||||
{
|
{
|
||||||
if (ne.EntityGroup.Name == "Enemy")
|
string entityGroupName = ne.EntityGroup.Name;
|
||||||
|
if (entityGroupName == EnemyGroupName)
|
||||||
{
|
{
|
||||||
if (_currentEnemyCount > 0)
|
if (_currentEnemyCount > 0)
|
||||||
{
|
{
|
||||||
|
|
@ -246,30 +239,10 @@ namespace CustomComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
RemoveEnemyFromCache(ne.EntityId);
|
RemoveEnemyFromCache(ne.EntityId);
|
||||||
GameEntry.SimulationWorld?.RemoveEnemyByEntityId(ne.EntityId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static EnemySimData CreateEnemySimData(EnemyBase enemy, EnemyData enemyData)
|
|
||||||
{
|
|
||||||
MovementComponent movementComponent = enemy != null ? enemy.GetComponent<MovementComponent>() : null;
|
|
||||||
|
|
||||||
return new EnemySimData
|
|
||||||
{
|
|
||||||
EntityId = enemy.Id,
|
|
||||||
Position = enemy.CachedTransform.position,
|
|
||||||
Forward = enemy.CachedTransform.forward,
|
|
||||||
Speed = enemyData != null ? enemyData.SpeedBase : 0f,
|
|
||||||
AttackRange = 1f,
|
|
||||||
AvoidEnemyOverlap = movementComponent != null && movementComponent.AvoidEnemyOverlap,
|
|
||||||
EnemyBodyRadius = movementComponent != null ? movementComponent.EnemyBodyRadius : 0.45f,
|
|
||||||
SeparationIterations = movementComponent != null ? movementComponent.SeparationIterations : 2,
|
|
||||||
TargetType = 0,
|
|
||||||
State = 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RemoveEnemyFromCache(int entityId)
|
private void RemoveEnemyFromCache(int entityId)
|
||||||
{
|
{
|
||||||
for (int i = _enemies.Count - 1; i >= 0; i--)
|
for (int i = _enemies.Count - 1; i >= 0; i--)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e2d0f6135bae4675af78194b2272e280
|
||||||
|
timeCreated: 1771590468
|
||||||
|
|
@ -7,6 +7,7 @@ namespace Simulation
|
||||||
public int EntityId;
|
public int EntityId;
|
||||||
public Vector3 Position;
|
public Vector3 Position;
|
||||||
public Vector3 Forward;
|
public Vector3 Forward;
|
||||||
|
public Quaternion Rotation;
|
||||||
public float Speed;
|
public float Speed;
|
||||||
public float AttackRange;
|
public float AttackRange;
|
||||||
public bool AvoidEnemyOverlap;
|
public bool AvoidEnemyOverlap;
|
||||||
|
|
@ -0,0 +1,138 @@
|
||||||
|
using Components;
|
||||||
|
using Entity;
|
||||||
|
using Entity.EntityData;
|
||||||
|
using GameFramework.Event;
|
||||||
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
|
namespace Simulation
|
||||||
|
{
|
||||||
|
public partial class SimulationWorld
|
||||||
|
{
|
||||||
|
public sealed class EntitySync
|
||||||
|
{
|
||||||
|
private const string EnemyGroupName = "Enemy";
|
||||||
|
private const string DropGroupName = "Drop";
|
||||||
|
private const string BulletGroupName = "Bullet";
|
||||||
|
private const string ProjectileGroupName = "Projectile";
|
||||||
|
|
||||||
|
private readonly SimulationWorld _world;
|
||||||
|
|
||||||
|
public EntitySync(SimulationWorld world)
|
||||||
|
{
|
||||||
|
_world = world;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnStart()
|
||||||
|
{
|
||||||
|
GameEntry.Event.Subscribe(ShowEntitySuccessEventArgs.EventId, OnShowEntitySuccess);
|
||||||
|
GameEntry.Event.Subscribe(HideEntityCompleteEventArgs.EventId, OnHideEntityComplete);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnDestroy()
|
||||||
|
{
|
||||||
|
GameEntry.Event.Unsubscribe(ShowEntitySuccessEventArgs.EventId, OnShowEntitySuccess);
|
||||||
|
GameEntry.Event.Unsubscribe(HideEntityCompleteEventArgs.EventId, OnHideEntityComplete);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnShowEntitySuccess(object sender, GameEventArgs e)
|
||||||
|
{
|
||||||
|
if (e is not ShowEntitySuccessEventArgs args) return;
|
||||||
|
if (args.Entity == null) return;
|
||||||
|
if (_world == null) return;
|
||||||
|
|
||||||
|
string groupName = args.Entity.EntityGroup?.Name;
|
||||||
|
if (string.IsNullOrEmpty(groupName)) return;
|
||||||
|
|
||||||
|
if (groupName == EnemyGroupName && args.Entity.Logic is EnemyBase enemy)
|
||||||
|
{
|
||||||
|
_world.RegisterEnemyTransform(enemy.Id, enemy.CachedTransform);
|
||||||
|
_world.UpsertEnemy(CreateEnemySimData(enemy, args.UserData as EnemyData));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groupName == DropGroupName && args.Entity.Logic is EntityBase pickupEntity)
|
||||||
|
{
|
||||||
|
_world.UpsertPickup(CreatePickupSimData(pickupEntity));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((groupName == BulletGroupName || groupName == ProjectileGroupName) &&
|
||||||
|
args.Entity.Logic is EntityBase projectileEntity)
|
||||||
|
{
|
||||||
|
_world.UpsertProjectile(CreateProjectileSimData(projectileEntity));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnHideEntityComplete(object sender, GameEventArgs e)
|
||||||
|
{
|
||||||
|
if (e is not HideEntityCompleteEventArgs args) return;
|
||||||
|
if (args.EntityGroup == null) return;
|
||||||
|
if (_world == null) return;
|
||||||
|
|
||||||
|
string groupName = args.EntityGroup.Name;
|
||||||
|
if (groupName == EnemyGroupName)
|
||||||
|
{
|
||||||
|
_world.UnregisterEnemyTransform(args.EntityId);
|
||||||
|
_world.RemoveEnemyByEntityId(args.EntityId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groupName == DropGroupName)
|
||||||
|
{
|
||||||
|
_world.RemovePickupByEntityId(args.EntityId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groupName == BulletGroupName || groupName == ProjectileGroupName)
|
||||||
|
{
|
||||||
|
_world.RemoveProjectileByEntityId(args.EntityId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static EnemySimData CreateEnemySimData(EnemyBase enemy, EnemyData enemyData)
|
||||||
|
{
|
||||||
|
MovementComponent movementComponent = enemy != null ? enemy.GetComponent<MovementComponent>() : null;
|
||||||
|
|
||||||
|
return new EnemySimData
|
||||||
|
{
|
||||||
|
EntityId = enemy.Id,
|
||||||
|
Position = enemy.CachedTransform.position,
|
||||||
|
Forward = enemy.CachedTransform.forward,
|
||||||
|
Rotation = enemy.CachedTransform.rotation,
|
||||||
|
Speed = enemyData != null ? enemyData.SpeedBase : 0f,
|
||||||
|
AttackRange = 1f,
|
||||||
|
AvoidEnemyOverlap = movementComponent != null && movementComponent.AvoidEnemyOverlap,
|
||||||
|
EnemyBodyRadius = movementComponent != null ? movementComponent.EnemyBodyRadius : 0.45f,
|
||||||
|
SeparationIterations = movementComponent != null ? movementComponent.SeparationIterations : 2,
|
||||||
|
TargetType = 0,
|
||||||
|
State = 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PickupSimData CreatePickupSimData(EntityBase pickupEntity)
|
||||||
|
{
|
||||||
|
return new PickupSimData
|
||||||
|
{
|
||||||
|
EntityId = pickupEntity.Id,
|
||||||
|
Position = pickupEntity.CachedTransform.position,
|
||||||
|
PickupRadius = 0.35f,
|
||||||
|
State = 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ProjectileSimData CreateProjectileSimData(EntityBase projectileEntity)
|
||||||
|
{
|
||||||
|
return new ProjectileSimData
|
||||||
|
{
|
||||||
|
EntityId = projectileEntity.Id,
|
||||||
|
OwnerEntityId = 0,
|
||||||
|
Position = projectileEntity.CachedTransform.position,
|
||||||
|
Forward = projectileEntity.CachedTransform.forward,
|
||||||
|
Speed = 0f,
|
||||||
|
RemainingLifetime = 0f,
|
||||||
|
State = 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 54d72e7241f04623bcfddfd096086dbb
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Simulation
|
||||||
|
{
|
||||||
|
public partial class SimulationWorld
|
||||||
|
{
|
||||||
|
private sealed class Presentation
|
||||||
|
{
|
||||||
|
private readonly SimulationWorld _world;
|
||||||
|
|
||||||
|
public Presentation(SimulationWorld world)
|
||||||
|
{
|
||||||
|
_world = world;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnLateUpdate()
|
||||||
|
{
|
||||||
|
if (_world == null || !_world.UseSimulationMovement)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var enemyManager = GameEntry.EnemyManager;
|
||||||
|
if (enemyManager == null || enemyManager.Enemies == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var enemies = enemyManager.Enemies;
|
||||||
|
for (int i = 0; i < enemies.Count; i++)
|
||||||
|
{
|
||||||
|
if (enemies[i] is not EnemyBase enemyEntity || !enemyEntity.Available)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_world.TryGetEnemyData(enemyEntity.Id, out EnemySimData enemyData))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplyEnemyPresentation(enemyEntity, enemyData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ApplyEnemyPresentation(EnemyBase enemyEntity, in EnemySimData enemyData)
|
||||||
|
{
|
||||||
|
Transform enemyTransform = enemyEntity.CachedTransform;
|
||||||
|
enemyTransform.position = enemyData.Position;
|
||||||
|
|
||||||
|
Quaternion rotation = enemyData.Rotation;
|
||||||
|
float rotationMagnitude = Mathf.Abs(rotation.x) + Mathf.Abs(rotation.y) + Mathf.Abs(rotation.z) +
|
||||||
|
Mathf.Abs(rotation.w);
|
||||||
|
if (rotationMagnitude > float.Epsilon)
|
||||||
|
{
|
||||||
|
enemyTransform.rotation = rotation;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 forward = enemyData.Forward;
|
||||||
|
forward.y = 0f;
|
||||||
|
if (forward.sqrMagnitude > float.Epsilon)
|
||||||
|
{
|
||||||
|
enemyTransform.forward = forward.normalized;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ed4e018208ef0d042b99372c1f9ae778
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using CustomUtility;
|
using CustomUtility;
|
||||||
using Entity;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
namespace Simulation
|
namespace Simulation
|
||||||
{
|
{
|
||||||
public sealed class SimulationWorld : GameFrameworkComponent
|
public sealed partial class SimulationWorld : GameFrameworkComponent
|
||||||
{
|
{
|
||||||
private const float DefaultAttackRange = 1f;
|
private const float DefaultAttackRange = 1f;
|
||||||
private const int EnemyStateIdle = 0;
|
private const int EnemyStateIdle = 0;
|
||||||
|
|
@ -15,13 +14,17 @@ namespace Simulation
|
||||||
|
|
||||||
[SerializeField] private bool _useSimulationMovement;
|
[SerializeField] private bool _useSimulationMovement;
|
||||||
|
|
||||||
|
private EntitySync _entitySync;
|
||||||
|
private Presentation _presentation;
|
||||||
|
|
||||||
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>();
|
||||||
private readonly List<PickupSimData> _pickups = new List<PickupSimData>();
|
private readonly List<PickupSimData> _pickups = new List<PickupSimData>();
|
||||||
|
private readonly Dictionary<int, Transform> _enemyTransforms = new Dictionary<int, Transform>();
|
||||||
|
|
||||||
public EntityBinding EnemyBinding { get; } = new EntityBinding();
|
private EntityBinding EnemyBinding { get; } = new EntityBinding();
|
||||||
public EntityBinding ProjectileBinding { get; } = new EntityBinding();
|
private EntityBinding ProjectileBinding { get; } = new EntityBinding();
|
||||||
public EntityBinding PickupBinding { get; } = new EntityBinding();
|
private EntityBinding PickupBinding { get; } = new EntityBinding();
|
||||||
|
|
||||||
public IReadOnlyList<EnemySimData> Enemies => _enemies;
|
public IReadOnlyList<EnemySimData> Enemies => _enemies;
|
||||||
public IReadOnlyList<ProjectileSimData> Projectiles => _projectiles;
|
public IReadOnlyList<ProjectileSimData> Projectiles => _projectiles;
|
||||||
|
|
@ -33,7 +36,31 @@ namespace Simulation
|
||||||
_useSimulationMovement = enabled;
|
_useSimulationMovement = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int AddEnemy(in EnemySimData simData)
|
protected override void Awake()
|
||||||
|
{
|
||||||
|
base.Awake();
|
||||||
|
_entitySync = new EntitySync(this);
|
||||||
|
_presentation = new Presentation(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
_entitySync?.OnStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDestroy()
|
||||||
|
{
|
||||||
|
_entitySync?.OnDestroy();
|
||||||
|
_entitySync = null;
|
||||||
|
_presentation = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LateUpdate()
|
||||||
|
{
|
||||||
|
_presentation?.OnLateUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int AddEnemy(in EnemySimData simData)
|
||||||
{
|
{
|
||||||
int simulationIndex = _enemies.Count;
|
int simulationIndex = _enemies.Count;
|
||||||
_enemies.Add(simData);
|
_enemies.Add(simData);
|
||||||
|
|
@ -41,18 +68,18 @@ namespace Simulation
|
||||||
return simulationIndex;
|
return simulationIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int UpsertEnemy(in EnemySimData simData)
|
private int UpsertEnemy(in EnemySimData simData)
|
||||||
{
|
{
|
||||||
if (EnemyBinding.TryGetSimulationIndex(simData.EntityId, out int simulationIndex))
|
if (!EnemyBinding.TryGetSimulationIndex(simData.EntityId, out int simulationIndex))
|
||||||
{
|
{
|
||||||
_enemies[simulationIndex] = simData;
|
return AddEnemy(simData);
|
||||||
return simulationIndex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return AddEnemy(simData);
|
_enemies[simulationIndex] = simData;
|
||||||
|
return simulationIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool RemoveEnemyByEntityId(int entityId)
|
private bool RemoveEnemyByEntityId(int entityId)
|
||||||
{
|
{
|
||||||
if (!EnemyBinding.TryGetSimulationIndex(entityId, out int simulationIndex))
|
if (!EnemyBinding.TryGetSimulationIndex(entityId, out int simulationIndex))
|
||||||
{
|
{
|
||||||
|
|
@ -69,10 +96,40 @@ namespace Simulation
|
||||||
|
|
||||||
_enemies.RemoveAt(lastIndex);
|
_enemies.RemoveAt(lastIndex);
|
||||||
EnemyBinding.UnbindByEntityId(entityId);
|
EnemyBinding.UnbindByEntityId(entityId);
|
||||||
|
_enemyTransforms.Remove(entityId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int AddProjectile(in ProjectileSimData simData)
|
private void RegisterEnemyTransform(int entityId, Transform transform)
|
||||||
|
{
|
||||||
|
if (transform == null)
|
||||||
|
{
|
||||||
|
_enemyTransforms.Remove(entityId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_enemyTransforms[entityId] = transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnregisterEnemyTransform(int entityId)
|
||||||
|
{
|
||||||
|
_enemyTransforms.Remove(entityId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryGetEnemyData(int entityId, out EnemySimData enemyData)
|
||||||
|
{
|
||||||
|
if (!EnemyBinding.TryGetSimulationIndex(entityId, out int simulationIndex) || simulationIndex < 0 ||
|
||||||
|
simulationIndex >= _enemies.Count)
|
||||||
|
{
|
||||||
|
enemyData = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
enemyData = _enemies[simulationIndex];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int AddProjectile(in ProjectileSimData simData)
|
||||||
{
|
{
|
||||||
int simulationIndex = _projectiles.Count;
|
int simulationIndex = _projectiles.Count;
|
||||||
_projectiles.Add(simData);
|
_projectiles.Add(simData);
|
||||||
|
|
@ -80,18 +137,18 @@ namespace Simulation
|
||||||
return simulationIndex;
|
return simulationIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int UpsertProjectile(in ProjectileSimData simData)
|
private int UpsertProjectile(in ProjectileSimData simData)
|
||||||
{
|
{
|
||||||
if (ProjectileBinding.TryGetSimulationIndex(simData.EntityId, out int simulationIndex))
|
if (!ProjectileBinding.TryGetSimulationIndex(simData.EntityId, out int simulationIndex))
|
||||||
{
|
{
|
||||||
_projectiles[simulationIndex] = simData;
|
return AddProjectile(simData);
|
||||||
return simulationIndex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return AddProjectile(simData);
|
_projectiles[simulationIndex] = simData;
|
||||||
|
return simulationIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool RemoveProjectileByEntityId(int entityId)
|
private bool RemoveProjectileByEntityId(int entityId)
|
||||||
{
|
{
|
||||||
if (!ProjectileBinding.TryGetSimulationIndex(entityId, out int simulationIndex))
|
if (!ProjectileBinding.TryGetSimulationIndex(entityId, out int simulationIndex))
|
||||||
{
|
{
|
||||||
|
|
@ -111,7 +168,7 @@ namespace Simulation
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int AddPickup(in PickupSimData simData)
|
private int AddPickup(in PickupSimData simData)
|
||||||
{
|
{
|
||||||
int simulationIndex = _pickups.Count;
|
int simulationIndex = _pickups.Count;
|
||||||
_pickups.Add(simData);
|
_pickups.Add(simData);
|
||||||
|
|
@ -119,18 +176,18 @@ namespace Simulation
|
||||||
return simulationIndex;
|
return simulationIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int UpsertPickup(in PickupSimData simData)
|
private int UpsertPickup(in PickupSimData simData)
|
||||||
{
|
{
|
||||||
if (PickupBinding.TryGetSimulationIndex(simData.EntityId, out int simulationIndex))
|
if (!PickupBinding.TryGetSimulationIndex(simData.EntityId, out int simulationIndex))
|
||||||
{
|
{
|
||||||
_pickups[simulationIndex] = simData;
|
return AddPickup(simData);
|
||||||
return simulationIndex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return AddPickup(simData);
|
_pickups[simulationIndex] = simData;
|
||||||
|
return simulationIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool RemovePickupByEntityId(int entityId)
|
private bool RemovePickupByEntityId(int entityId)
|
||||||
{
|
{
|
||||||
if (!PickupBinding.TryGetSimulationIndex(entityId, out int simulationIndex))
|
if (!PickupBinding.TryGetSimulationIndex(entityId, out int simulationIndex))
|
||||||
{
|
{
|
||||||
|
|
@ -165,6 +222,7 @@ namespace Simulation
|
||||||
_enemies.Clear();
|
_enemies.Clear();
|
||||||
_projectiles.Clear();
|
_projectiles.Clear();
|
||||||
_pickups.Clear();
|
_pickups.Clear();
|
||||||
|
_enemyTransforms.Clear();
|
||||||
|
|
||||||
EnemyBinding.Clear();
|
EnemyBinding.Clear();
|
||||||
ProjectileBinding.Clear();
|
ProjectileBinding.Clear();
|
||||||
|
|
@ -180,21 +238,11 @@ namespace Simulation
|
||||||
|
|
||||||
Vector3 playerPosition = context.PlayerPosition;
|
Vector3 playerPosition = context.PlayerPosition;
|
||||||
playerPosition.y = 0f;
|
playerPosition.y = 0f;
|
||||||
EntityComponent entityComponent = GameEntry.Entity;
|
|
||||||
|
|
||||||
for (int i = 0; i < _enemies.Count; i++)
|
for (int i = 0; i < _enemies.Count; i++)
|
||||||
{
|
{
|
||||||
EnemySimData enemy = _enemies[i];
|
EnemySimData enemy = _enemies[i];
|
||||||
EnemyBase enemyEntity = null;
|
_enemyTransforms.TryGetValue(enemy.EntityId, out Transform enemyTransform);
|
||||||
Transform enemyTransform = null;
|
|
||||||
|
|
||||||
if (entityComponent != null &&
|
|
||||||
entityComponent.GetGameEntity(enemy.EntityId) is EnemyBase runtimeEnemy &&
|
|
||||||
runtimeEnemy.Available)
|
|
||||||
{
|
|
||||||
enemyEntity = runtimeEnemy;
|
|
||||||
enemyTransform = runtimeEnemy.CachedTransform;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector3 currentPosition = enemy.Position;
|
Vector3 currentPosition = enemy.Position;
|
||||||
currentPosition.y = 0f;
|
currentPosition.y = 0f;
|
||||||
|
|
@ -229,31 +277,13 @@ namespace Simulation
|
||||||
|
|
||||||
enemy.Position = desiredPosition;
|
enemy.Position = desiredPosition;
|
||||||
enemy.State = EnemyStateChasing;
|
enemy.State = EnemyStateChasing;
|
||||||
|
if (forward.sqrMagnitude > float.Epsilon)
|
||||||
|
{
|
||||||
|
enemy.Rotation = Quaternion.LookRotation(forward, Vector3.up);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_enemies[i] = enemy;
|
_enemies[i] = enemy;
|
||||||
|
|
||||||
if (enemyEntity != null)
|
|
||||||
{
|
|
||||||
ApplyEnemyPresentation(enemyEntity, enemy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ApplyEnemyPresentation(EnemyBase enemyEntity, in EnemySimData enemyData)
|
|
||||||
{
|
|
||||||
if (enemyEntity == null || !enemyEntity.Available)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
enemyEntity.CachedTransform.position = enemyData.Position;
|
|
||||||
|
|
||||||
Vector3 forward = enemyData.Forward;
|
|
||||||
forward.y = 0f;
|
|
||||||
if (forward.sqrMagnitude > float.Epsilon)
|
|
||||||
{
|
|
||||||
enemyEntity.CachedTransform.forward = forward.normalized;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1407,7 +1407,7 @@ Transform:
|
||||||
m_PrefabAsset: {fileID: 0}
|
m_PrefabAsset: {fileID: 0}
|
||||||
m_GameObject: {fileID: 1652245190}
|
m_GameObject: {fileID: 1652245190}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
m_ConstrainProportionsScale: 0
|
m_ConstrainProportionsScale: 0
|
||||||
|
|
@ -1426,6 +1426,7 @@ MonoBehaviour:
|
||||||
m_Script: {fileID: 11500000, guid: 8a558ebbc9cb4d94946ac9f4f27914d8, type: 3}
|
m_Script: {fileID: 11500000, guid: 8a558ebbc9cb4d94946ac9f4f27914d8, type: 3}
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier:
|
m_EditorClassIdentifier:
|
||||||
|
_useSimulationMovement: 0
|
||||||
--- !u!1 &1852670052
|
--- !u!1 &1852670052
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
|
|
||||||
|
|
@ -49,13 +49,13 @@
|
||||||
- `MeleeEnemy/RemoteEnemy.OnUpdate` 仅保留表现层或空实现(不再做核心移动计算)。
|
- `MeleeEnemy/RemoteEnemy.OnUpdate` 仅保留表现层或空实现(不再做核心移动计算)。
|
||||||
- 完成标准:同等刷怪量下,敌人移动结果与旧逻辑视觉一致,无明显穿模/停滞回归。
|
- 完成标准:同等刷怪量下,敌人移动结果与旧逻辑视觉一致,无明显穿模/停滞回归。
|
||||||
|
|
||||||
- [ ] Checkpoint 5:拆分“逻辑输出”与“表现层消费”
|
- [x] Checkpoint 5:拆分“逻辑输出”与“表现层消费”
|
||||||
- 逻辑层输出:`position/rotation/state`(必要时含 `isMoving`)。
|
- 逻辑层输出:`position/rotation/state`(必要时含 `isMoving`)。
|
||||||
- 表现层仅消费并回写 `Transform`,动画/特效/UI 不参与逻辑计算。
|
- 表现层仅消费并回写 `Transform`,动画/特效/UI 不参与逻辑计算。
|
||||||
- 明确边界:HPBar、DamageText、Animator 继续由表现层驱动。
|
- 明确边界:HPBar、DamageText、Animator 继续由表现层驱动。
|
||||||
- 完成标准:关闭/开启 Simulation 不影响 UI 事件链(血量、经验、金币、关卡流程)。
|
- 完成标准:关闭/开启 Simulation 不影响 UI 事件链(血量、经验、金币、关卡流程)。
|
||||||
|
|
||||||
- [ ] Checkpoint 6:补齐 Projectile/Pickup 的 Simulation 占位数据通道
|
- [x] Checkpoint 6:补齐 Projectile/Pickup 的 Simulation 占位数据通道
|
||||||
- 在 `SimulationWorld` 中接入 `ProjectileSimData / PickupSimData` 容器与绑定关系。
|
- 在 `SimulationWorld` 中接入 `ProjectileSimData / PickupSimData` 容器与绑定关系。
|
||||||
- 先不迁移完整行为,只保证创建、回收、索引同步路径可用。
|
- 先不迁移完整行为,只保证创建、回收、索引同步路径可用。
|
||||||
- 完成标准:投射物/掉落物实体生命周期正常,无索引越界与回收遗漏。
|
- 完成标准:投射物/掉落物实体生命周期正常,无索引越界与回收遗漏。
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue