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
|
||||
// Copyright © 2013-2021 Jiang Yin. All rights reserved.
|
||||
// Homepage: https://gameframework.cn/
|
||||
|
|
@ -46,6 +46,10 @@ public partial class GameEntry : MonoBehaviour
|
|||
|
||||
EnemyManager = UnityGameFramework.Runtime.GameEntry.GetComponent<EnemyManagerComponent>();
|
||||
SimulationWorld = UnityGameFramework.Runtime.GameEntry.GetComponent<SimulationWorld>();
|
||||
if (SimulationWorld == null && Base != null)
|
||||
{
|
||||
SimulationWorld = Base.gameObject.AddComponent<SimulationWorld>();
|
||||
}
|
||||
SpriteCache = UnityGameFramework.Runtime.GameEntry.GetComponent<SpriteCacheComponent>();
|
||||
UIRouter = UnityGameFramework.Runtime.GameEntry.GetComponent<UIRouterComponent>();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
using Components;
|
||||
using DataTable;
|
||||
using Definition.Enum;
|
||||
using Entity;
|
||||
using Entity.EntityData;
|
||||
using GameFramework.Event;
|
||||
using Procedure;
|
||||
using Simulation;
|
||||
using StarForce;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
|
@ -17,6 +15,7 @@ namespace CustomComponent
|
|||
public class EnemyManagerComponent : GameFrameworkComponent
|
||||
{
|
||||
private const float MinSpawnRateScale = 0.1f;
|
||||
private const string EnemyGroupName = "Enemy";
|
||||
|
||||
private EntityComponent _entity;
|
||||
|
||||
|
|
@ -211,21 +210,14 @@ namespace CustomComponent
|
|||
{
|
||||
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++;
|
||||
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))
|
||||
|
|
@ -238,7 +230,8 @@ namespace CustomComponent
|
|||
{
|
||||
if (e is HideEntityCompleteEventArgs ne)
|
||||
{
|
||||
if (ne.EntityGroup.Name == "Enemy")
|
||||
string entityGroupName = ne.EntityGroup.Name;
|
||||
if (entityGroupName == EnemyGroupName)
|
||||
{
|
||||
if (_currentEnemyCount > 0)
|
||||
{
|
||||
|
|
@ -246,30 +239,10 @@ namespace CustomComponent
|
|||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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 Vector3 Position;
|
||||
public Vector3 Forward;
|
||||
public Quaternion Rotation;
|
||||
public float Speed;
|
||||
public float AttackRange;
|
||||
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 CustomUtility;
|
||||
using Entity;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace Simulation
|
||||
{
|
||||
public sealed class SimulationWorld : GameFrameworkComponent
|
||||
public sealed partial class SimulationWorld : GameFrameworkComponent
|
||||
{
|
||||
private const float DefaultAttackRange = 1f;
|
||||
private const int EnemyStateIdle = 0;
|
||||
|
|
@ -15,13 +14,17 @@ namespace Simulation
|
|||
|
||||
[SerializeField] private bool _useSimulationMovement;
|
||||
|
||||
private EntitySync _entitySync;
|
||||
private Presentation _presentation;
|
||||
|
||||
private readonly List<EnemySimData> _enemies = new List<EnemySimData>();
|
||||
private readonly List<ProjectileSimData> _projectiles = new List<ProjectileSimData>();
|
||||
private readonly List<PickupSimData> _pickups = new List<PickupSimData>();
|
||||
private readonly Dictionary<int, Transform> _enemyTransforms = new Dictionary<int, Transform>();
|
||||
|
||||
public EntityBinding EnemyBinding { get; } = new EntityBinding();
|
||||
public EntityBinding ProjectileBinding { get; } = new EntityBinding();
|
||||
public EntityBinding PickupBinding { get; } = new EntityBinding();
|
||||
private EntityBinding EnemyBinding { get; } = new EntityBinding();
|
||||
private EntityBinding ProjectileBinding { get; } = new EntityBinding();
|
||||
private EntityBinding PickupBinding { get; } = new EntityBinding();
|
||||
|
||||
public IReadOnlyList<EnemySimData> Enemies => _enemies;
|
||||
public IReadOnlyList<ProjectileSimData> Projectiles => _projectiles;
|
||||
|
|
@ -33,7 +36,31 @@ namespace Simulation
|
|||
_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;
|
||||
_enemies.Add(simData);
|
||||
|
|
@ -41,18 +68,18 @@ namespace Simulation
|
|||
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))
|
||||
{
|
||||
return AddEnemy(simData);
|
||||
}
|
||||
|
||||
_enemies[simulationIndex] = simData;
|
||||
return simulationIndex;
|
||||
}
|
||||
|
||||
return AddEnemy(simData);
|
||||
}
|
||||
|
||||
public bool RemoveEnemyByEntityId(int entityId)
|
||||
private bool RemoveEnemyByEntityId(int entityId)
|
||||
{
|
||||
if (!EnemyBinding.TryGetSimulationIndex(entityId, out int simulationIndex))
|
||||
{
|
||||
|
|
@ -69,10 +96,40 @@ namespace Simulation
|
|||
|
||||
_enemies.RemoveAt(lastIndex);
|
||||
EnemyBinding.UnbindByEntityId(entityId);
|
||||
_enemyTransforms.Remove(entityId);
|
||||
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;
|
||||
_projectiles.Add(simData);
|
||||
|
|
@ -80,18 +137,18 @@ namespace Simulation
|
|||
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))
|
||||
{
|
||||
return AddProjectile(simData);
|
||||
}
|
||||
|
||||
_projectiles[simulationIndex] = simData;
|
||||
return simulationIndex;
|
||||
}
|
||||
|
||||
return AddProjectile(simData);
|
||||
}
|
||||
|
||||
public bool RemoveProjectileByEntityId(int entityId)
|
||||
private bool RemoveProjectileByEntityId(int entityId)
|
||||
{
|
||||
if (!ProjectileBinding.TryGetSimulationIndex(entityId, out int simulationIndex))
|
||||
{
|
||||
|
|
@ -111,7 +168,7 @@ namespace Simulation
|
|||
return true;
|
||||
}
|
||||
|
||||
public int AddPickup(in PickupSimData simData)
|
||||
private int AddPickup(in PickupSimData simData)
|
||||
{
|
||||
int simulationIndex = _pickups.Count;
|
||||
_pickups.Add(simData);
|
||||
|
|
@ -119,18 +176,18 @@ namespace Simulation
|
|||
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))
|
||||
{
|
||||
return AddPickup(simData);
|
||||
}
|
||||
|
||||
_pickups[simulationIndex] = simData;
|
||||
return simulationIndex;
|
||||
}
|
||||
|
||||
return AddPickup(simData);
|
||||
}
|
||||
|
||||
public bool RemovePickupByEntityId(int entityId)
|
||||
private bool RemovePickupByEntityId(int entityId)
|
||||
{
|
||||
if (!PickupBinding.TryGetSimulationIndex(entityId, out int simulationIndex))
|
||||
{
|
||||
|
|
@ -165,6 +222,7 @@ namespace Simulation
|
|||
_enemies.Clear();
|
||||
_projectiles.Clear();
|
||||
_pickups.Clear();
|
||||
_enemyTransforms.Clear();
|
||||
|
||||
EnemyBinding.Clear();
|
||||
ProjectileBinding.Clear();
|
||||
|
|
@ -180,21 +238,11 @@ namespace Simulation
|
|||
|
||||
Vector3 playerPosition = context.PlayerPosition;
|
||||
playerPosition.y = 0f;
|
||||
EntityComponent entityComponent = GameEntry.Entity;
|
||||
|
||||
for (int i = 0; i < _enemies.Count; i++)
|
||||
{
|
||||
EnemySimData enemy = _enemies[i];
|
||||
EnemyBase enemyEntity = null;
|
||||
Transform enemyTransform = null;
|
||||
|
||||
if (entityComponent != null &&
|
||||
entityComponent.GetGameEntity(enemy.EntityId) is EnemyBase runtimeEnemy &&
|
||||
runtimeEnemy.Available)
|
||||
{
|
||||
enemyEntity = runtimeEnemy;
|
||||
enemyTransform = runtimeEnemy.CachedTransform;
|
||||
}
|
||||
_enemyTransforms.TryGetValue(enemy.EntityId, out Transform enemyTransform);
|
||||
|
||||
Vector3 currentPosition = enemy.Position;
|
||||
currentPosition.y = 0f;
|
||||
|
|
@ -229,31 +277,13 @@ namespace Simulation
|
|||
|
||||
enemy.Position = desiredPosition;
|
||||
enemy.State = EnemyStateChasing;
|
||||
if (forward.sqrMagnitude > float.Epsilon)
|
||||
{
|
||||
enemy.Rotation = Quaternion.LookRotation(forward, Vector3.up);
|
||||
}
|
||||
}
|
||||
|
||||
_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_GameObject: {fileID: 1652245190}
|
||||
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_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
|
|
@ -1426,6 +1426,7 @@ MonoBehaviour:
|
|||
m_Script: {fileID: 11500000, guid: 8a558ebbc9cb4d94946ac9f4f27914d8, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
_useSimulationMovement: 0
|
||||
--- !u!1 &1852670052
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
|
|
|||
|
|
@ -49,13 +49,13 @@
|
|||
- `MeleeEnemy/RemoteEnemy.OnUpdate` 仅保留表现层或空实现(不再做核心移动计算)。
|
||||
- 完成标准:同等刷怪量下,敌人移动结果与旧逻辑视觉一致,无明显穿模/停滞回归。
|
||||
|
||||
- [ ] Checkpoint 5:拆分“逻辑输出”与“表现层消费”
|
||||
- [x] Checkpoint 5:拆分“逻辑输出”与“表现层消费”
|
||||
- 逻辑层输出:`position/rotation/state`(必要时含 `isMoving`)。
|
||||
- 表现层仅消费并回写 `Transform`,动画/特效/UI 不参与逻辑计算。
|
||||
- 明确边界:HPBar、DamageText、Animator 继续由表现层驱动。
|
||||
- 完成标准:关闭/开启 Simulation 不影响 UI 事件链(血量、经验、金币、关卡流程)。
|
||||
|
||||
- [ ] Checkpoint 6:补齐 Projectile/Pickup 的 Simulation 占位数据通道
|
||||
- [x] Checkpoint 6:补齐 Projectile/Pickup 的 Simulation 占位数据通道
|
||||
- 在 `SimulationWorld` 中接入 `ProjectileSimData / PickupSimData` 容器与绑定关系。
|
||||
- 先不迁移完整行为,只保证创建、回收、索引同步路径可用。
|
||||
- 完成标准:投射物/掉落物实体生命周期正常,无索引越界与回收遗漏。
|
||||
|
|
|
|||
Loading…
Reference in New Issue