Checkpoint 3 & Checkpoint 4:
- GameStateBattle 接入 Simulation 主更新入口 - SimulationWorld 增加开关 UseSimulationMovement(默认 false) - SimulationWorld.Tick(...) 现在在开关开启时执行敌人追踪/移动模拟,基本还原 MovementComponent 功能(敌人互斥) - 调整 IEnemySeparationSolver 系列方法,不再依赖 MovementComponent
This commit is contained in:
@@ -9,6 +9,9 @@ namespace Simulation
|
||||
public Vector3 Forward;
|
||||
public float Speed;
|
||||
public float AttackRange;
|
||||
public bool AvoidEnemyOverlap;
|
||||
public float EnemyBodyRadius;
|
||||
public int SeparationIterations;
|
||||
public int TargetType;
|
||||
public int State;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
using System.Collections.Generic;
|
||||
using CustomUtility;
|
||||
using Entity;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace Simulation
|
||||
{
|
||||
public sealed class SimulationWorld : GameFrameworkComponent
|
||||
{
|
||||
private const float DefaultAttackRange = 1f;
|
||||
private const int EnemyStateIdle = 0;
|
||||
private const int EnemyStateChasing = 1;
|
||||
private const int EnemyStateInAttackRange = 2;
|
||||
|
||||
[SerializeField] private bool _useSimulationMovement;
|
||||
|
||||
private readonly List<EnemySimData> _enemies = new List<EnemySimData>();
|
||||
private readonly List<ProjectileSimData> _projectiles = new List<ProjectileSimData>();
|
||||
private readonly List<PickupSimData> _pickups = new List<PickupSimData>();
|
||||
@@ -16,6 +26,12 @@ namespace Simulation
|
||||
public IReadOnlyList<EnemySimData> Enemies => _enemies;
|
||||
public IReadOnlyList<ProjectileSimData> Projectiles => _projectiles;
|
||||
public IReadOnlyList<PickupSimData> Pickups => _pickups;
|
||||
public bool UseSimulationMovement => _useSimulationMovement;
|
||||
|
||||
public void SetUseSimulationMovement(bool enabled)
|
||||
{
|
||||
_useSimulationMovement = enabled;
|
||||
}
|
||||
|
||||
public int AddEnemy(in EnemySimData simData)
|
||||
{
|
||||
@@ -136,7 +152,12 @@ namespace Simulation
|
||||
|
||||
public void Tick(in SimulationTickContext context)
|
||||
{
|
||||
_ = context;
|
||||
if (!_useSimulationMovement)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TickEnemies(in context);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
@@ -149,5 +170,91 @@ namespace Simulation
|
||||
ProjectileBinding.Clear();
|
||||
PickupBinding.Clear();
|
||||
}
|
||||
|
||||
private void TickEnemies(in SimulationTickContext context)
|
||||
{
|
||||
if (_enemies.Count == 0 || context.DeltaTime <= 0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Vector3 currentPosition = enemy.Position;
|
||||
currentPosition.y = 0f;
|
||||
|
||||
Vector3 toPlayer = playerPosition - currentPosition;
|
||||
float sqrDistance = toPlayer.sqrMagnitude;
|
||||
float attackRange = enemy.AttackRange > 0f ? enemy.AttackRange : DefaultAttackRange;
|
||||
float attackRangeSqr = attackRange * attackRange;
|
||||
|
||||
if (sqrDistance <= attackRangeSqr)
|
||||
{
|
||||
enemy.State = EnemyStateInAttackRange;
|
||||
}
|
||||
else if (enemy.Speed <= 0f || sqrDistance <= float.Epsilon)
|
||||
{
|
||||
enemy.State = EnemyStateIdle;
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector3 forward = toPlayer.normalized;
|
||||
enemy.Forward = forward;
|
||||
Vector3 desiredPosition = enemy.Position + forward * enemy.Speed * context.DeltaTime;
|
||||
if (enemy.AvoidEnemyOverlap && enemyTransform != null)
|
||||
{
|
||||
int separationIterations = enemy.SeparationIterations > 0 ? enemy.SeparationIterations : 1;
|
||||
desiredPosition = EnemySeparationSolverProvider.Resolve(
|
||||
enemyTransform,
|
||||
desiredPosition,
|
||||
forward,
|
||||
separationIterations);
|
||||
}
|
||||
|
||||
enemy.Position = desiredPosition;
|
||||
enemy.State = EnemyStateChasing;
|
||||
}
|
||||
|
||||
_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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user