保留 Tick 作为唯一入口,但让 SimulationWorld 只做 orchestration
- Tick 仍然是唯一仿真入口。SimulationWorld 顶层现在只保留全局开关、bridge 服务和生命周期入口,原来散落在类上的大部分跨域状态已经从 SimulationWorld 收口到新的 SimulationWorld.RuntimeModules。 - 新的 runtime modules 把状态按域分成了 SimulationStateStore、JobDataRuntimeState、CollisionPipelineRuntimeState、TargetSelectionRuntimeState,并通过同名代理把现有 partial 的调用面基本保持不变。 - CollisionPipeline 和 target-selection 的配置也一起下沉到了运行时模块。为了避免现有反射型测试立刻失效,保留了 _collisionQueryInputs 和 _areaCollisionRequests 这两个顶层兼容字段。
This commit is contained in:
parent
736a2a65bd
commit
224db4cd5c
|
|
@ -1,39 +1,18 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Unity.Collections;
|
using Unity.Collections;
|
||||||
using Unity.Mathematics;
|
|
||||||
|
|
||||||
namespace Simulation
|
namespace Simulation
|
||||||
{
|
{
|
||||||
public sealed partial class SimulationWorld
|
public sealed partial class SimulationWorld
|
||||||
{
|
{
|
||||||
// Shared native buffers, collision stats, and channel-level constants.
|
// Shared channel constants plus compatibility fields still reflected by tests.
|
||||||
private const int CollisionSourceTypeProjectile = 1;
|
private const int CollisionSourceTypeProjectile = 1;
|
||||||
private const int CollisionSourceTypeArea = 2;
|
private const int CollisionSourceTypeArea = 2;
|
||||||
private const int CollisionShapeCircle = 0;
|
private const int CollisionShapeCircle = 0;
|
||||||
private const int CollisionShapeSector = 1;
|
private const int CollisionShapeSector = 1;
|
||||||
|
|
||||||
private NativeList<EnemyJobInputData> _enemyJobInputs;
|
// Kept as top-level fields because current regression tests reflect them directly.
|
||||||
private NativeList<EnemyJobOutputData> _enemyJobOutputs;
|
|
||||||
private NativeList<EnemyJobOutputData> _enemyJobSeparationOutputs;
|
|
||||||
private NativeList<float2> _enemySeparationPreviousPushes;
|
|
||||||
private NativeList<float2> _enemySeparationCurrentPushes;
|
|
||||||
private NativeList<ProjectileJobInputData> _projectileJobInputs;
|
|
||||||
private NativeList<ProjectileJobOutputData> _projectileJobOutputs;
|
|
||||||
private NativeList<CollisionQueryData> _collisionQueryInputs;
|
private NativeList<CollisionQueryData> _collisionQueryInputs;
|
||||||
private NativeList<CollisionCandidateData> _collisionCandidates;
|
|
||||||
private NativeParallelMultiHashMap<long, int> _enemySeparationBuckets;
|
|
||||||
private NativeParallelMultiHashMap<long, int> _enemyCollisionBuckets;
|
|
||||||
private readonly List<AreaCollisionRequestData> _areaCollisionRequests = new(16);
|
private readonly List<AreaCollisionRequestData> _areaCollisionRequests = new(16);
|
||||||
private readonly List<AreaCollisionHitEventData> _areaCollisionHitEvents = new(32);
|
|
||||||
private readonly HashSet<long> _areaCollisionHitDedupKeys = new();
|
|
||||||
private int _lastCollisionQueryCount;
|
|
||||||
private int _lastProjectileCollisionQueryCount;
|
|
||||||
private int _lastAreaCollisionQueryCount;
|
|
||||||
private int _lastCollisionCandidateCount;
|
|
||||||
private int _lastProjectileCollisionCandidateCount;
|
|
||||||
private int _lastAreaCollisionCandidateCount;
|
|
||||||
private int _lastResolvedAreaHitCount;
|
|
||||||
private float _lastCollisionCellSize;
|
|
||||||
private bool _lastCollisionHasEnemyTargets;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,3 @@
|
||||||
using Unity.Jobs;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Simulation
|
namespace Simulation
|
||||||
{
|
{
|
||||||
public sealed partial class SimulationWorld
|
public sealed partial class SimulationWorld
|
||||||
|
|
@ -9,37 +6,5 @@ namespace Simulation
|
||||||
// Request buffering, broad-phase scheduling, resolve, and presentation
|
// Request buffering, broad-phase scheduling, resolve, and presentation
|
||||||
// dispatch live in dedicated partial files under Jobs/.
|
// dispatch live in dedicated partial files under Jobs/.
|
||||||
private const int PlayerEntityId = -1;
|
private const int PlayerEntityId = -1;
|
||||||
private JobHandle _collisionCandidateQueryHandle;
|
|
||||||
private bool _collisionCandidateQueryScheduled;
|
|
||||||
|
|
||||||
[Header("Projectile Collision Query")]
|
|
||||||
[Tooltip("Projectile broad-phase collision query radius.")]
|
|
||||||
[SerializeField]
|
|
||||||
private float _projectileCollisionQueryRadius = 0.35f;
|
|
||||||
|
|
||||||
[Tooltip("Maximum retained candidates per projectile query.")]
|
|
||||||
[SerializeField]
|
|
||||||
private int _projectileMaxCandidatesPerQuery = 1;
|
|
||||||
|
|
||||||
[Tooltip("Broad-phase bucket cell size. <=0 derives from query radius.")]
|
|
||||||
[SerializeField]
|
|
||||||
private float _projectileCollisionCellSize = 0f;
|
|
||||||
|
|
||||||
[Header("Projectile Hit Event Dispatch")]
|
|
||||||
[Tooltip("Dispatch projectile hit presentation event.")]
|
|
||||||
[SerializeField]
|
|
||||||
private bool _dispatchProjectileHitPresentationEvent = true;
|
|
||||||
|
|
||||||
[Tooltip("Request hit marker when projectile hits.")]
|
|
||||||
[SerializeField]
|
|
||||||
private bool _dispatchProjectileHitMarkerEvent = true;
|
|
||||||
|
|
||||||
[Tooltip("Request hit effect when projectile hits.")]
|
|
||||||
[SerializeField]
|
|
||||||
private bool _dispatchProjectileHitEffectEvent = true;
|
|
||||||
|
|
||||||
[Tooltip("Default hit effect entity type id in presentation event. 0 means not specified.")]
|
|
||||||
[SerializeField]
|
|
||||||
private int _projectileHitPresentationEffectTypeId = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
using Components;
|
||||||
|
using Entity;
|
||||||
|
using Entity.EntityData;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Simulation
|
||||||
|
{
|
||||||
|
public sealed partial class SimulationWorld
|
||||||
|
{
|
||||||
|
#region Entity To Sim Data
|
||||||
|
|
||||||
|
private static EnemySimData CreateEnemyInitialSimData(EnemyBase enemy, EnemyData enemyData)
|
||||||
|
{
|
||||||
|
Transform enemyTransform = enemy.CachedTransform;
|
||||||
|
MovementComponent movementComponent = enemy.GetComponent<MovementComponent>();
|
||||||
|
|
||||||
|
float speed = 0f;
|
||||||
|
if (enemyData != null)
|
||||||
|
{
|
||||||
|
speed = enemyData.SpeedBase;
|
||||||
|
}
|
||||||
|
else if (movementComponent != null)
|
||||||
|
{
|
||||||
|
speed = movementComponent.Speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
float attackRange = enemy.AttackRange > 0f
|
||||||
|
? enemy.AttackRange
|
||||||
|
: DefaultAttackRange;
|
||||||
|
|
||||||
|
return new EnemySimData
|
||||||
|
{
|
||||||
|
EntityId = enemy.Id,
|
||||||
|
Position = enemyTransform.position,
|
||||||
|
Forward = enemyTransform.forward,
|
||||||
|
Rotation = enemyTransform.rotation,
|
||||||
|
Speed = speed,
|
||||||
|
AttackRange = attackRange,
|
||||||
|
AvoidEnemyOverlap = movementComponent != null && movementComponent.AvoidEnemyOverlap,
|
||||||
|
EnemyBodyRadius = movementComponent != null ? movementComponent.EnemyBodyRadius : 0.45f,
|
||||||
|
SeparationIterations = movementComponent != null ? movementComponent.SeparationIterations : 2,
|
||||||
|
TargetType = 0,
|
||||||
|
State = EnemyStateIdle
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ProjectileSimData CreateProjectileInitialSimData(EntityBase projectileEntity, object userData)
|
||||||
|
{
|
||||||
|
Vector3 forward = projectileEntity.CachedTransform.forward;
|
||||||
|
int ownerEntityId = 0;
|
||||||
|
Vector3 velocity = Vector3.zero;
|
||||||
|
float speed = 0f;
|
||||||
|
float lifeTime = 0f;
|
||||||
|
|
||||||
|
if (userData is EnemyProjectileData enemyProjectileData)
|
||||||
|
{
|
||||||
|
ownerEntityId = enemyProjectileData.OwnerEntityId;
|
||||||
|
|
||||||
|
Vector3 direction = enemyProjectileData.Direction;
|
||||||
|
direction.y = 0f;
|
||||||
|
if (direction.sqrMagnitude > Mathf.Epsilon)
|
||||||
|
{
|
||||||
|
direction.Normalize();
|
||||||
|
forward = direction;
|
||||||
|
}
|
||||||
|
else if (forward.sqrMagnitude > Mathf.Epsilon)
|
||||||
|
{
|
||||||
|
forward = forward.normalized;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
forward = Vector3.forward;
|
||||||
|
}
|
||||||
|
|
||||||
|
speed = Mathf.Max(0f, enemyProjectileData.Speed);
|
||||||
|
velocity = forward * speed;
|
||||||
|
lifeTime = Mathf.Max(0f, enemyProjectileData.LifeTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ProjectileSimData
|
||||||
|
{
|
||||||
|
EntityId = projectileEntity.Id,
|
||||||
|
OwnerEntityId = ownerEntityId,
|
||||||
|
Position = projectileEntity.CachedTransform.position,
|
||||||
|
Forward = forward,
|
||||||
|
Velocity = velocity,
|
||||||
|
Speed = speed,
|
||||||
|
LifeTime = lifeTime,
|
||||||
|
Age = 0f,
|
||||||
|
Active = true,
|
||||||
|
RemainingLifetime = lifeTime,
|
||||||
|
State = ProjectileStateActive
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PickupSimData CreatePickupInitialSimData(EntityBase pickupEntity)
|
||||||
|
{
|
||||||
|
return new PickupSimData
|
||||||
|
{
|
||||||
|
EntityId = pickupEntity.Id,
|
||||||
|
Position = pickupEntity.CachedTransform.position,
|
||||||
|
PickupRadius = 0.35f,
|
||||||
|
State = 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bdedabee0342441e9551c802dd66693a
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,151 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Unity.Collections;
|
||||||
|
using Unity.Jobs;
|
||||||
|
using Unity.Mathematics;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Simulation
|
||||||
|
{
|
||||||
|
public sealed partial class SimulationWorld
|
||||||
|
{
|
||||||
|
[SerializeField] private CollisionPipelineSettings _collisionPipelineSettings = new();
|
||||||
|
[SerializeField] private TargetSelectionSettings _targetSelectionSettings = new();
|
||||||
|
|
||||||
|
private readonly SimulationStateStore _simulationState = new();
|
||||||
|
private readonly JobDataRuntimeState _jobDataRuntime = new();
|
||||||
|
private readonly CollisionPipelineRuntimeState _collisionPipelineRuntime = new();
|
||||||
|
private readonly TargetSelectionRuntimeState _targetSelectionRuntime = new();
|
||||||
|
|
||||||
|
private List<EnemySimData> _enemies => _simulationState.Enemies;
|
||||||
|
private List<ProjectileSimData> _projectiles => _simulationState.Projectiles;
|
||||||
|
private List<PickupSimData> _pickups => _simulationState.Pickups;
|
||||||
|
private List<int> _projectileRecycleEntityIds => _simulationState.ProjectileRecycleEntityIds;
|
||||||
|
private HashSet<int> _projectileResolvedEntityIds => _collisionPipelineRuntime.ProjectileResolvedEntityIds;
|
||||||
|
|
||||||
|
private EntityBinding EnemyBinding => _simulationState.EnemyBinding;
|
||||||
|
private EntityBinding ProjectileBinding => _simulationState.ProjectileBinding;
|
||||||
|
private EntityBinding PickupBinding => _simulationState.PickupBinding;
|
||||||
|
|
||||||
|
private ref NativeList<EnemyJobInputData> _enemyJobInputs => ref _jobDataRuntime.EnemyJobInputs;
|
||||||
|
private ref NativeList<EnemyJobOutputData> _enemyJobOutputs => ref _jobDataRuntime.EnemyJobOutputs;
|
||||||
|
private ref NativeList<EnemyJobOutputData> _enemyJobSeparationOutputs => ref _jobDataRuntime.EnemyJobSeparationOutputs;
|
||||||
|
private ref NativeList<float2> _enemySeparationPreviousPushes => ref _jobDataRuntime.EnemySeparationPreviousPushes;
|
||||||
|
private ref NativeList<float2> _enemySeparationCurrentPushes => ref _jobDataRuntime.EnemySeparationCurrentPushes;
|
||||||
|
private ref NativeList<ProjectileJobInputData> _projectileJobInputs => ref _jobDataRuntime.ProjectileJobInputs;
|
||||||
|
private ref NativeList<ProjectileJobOutputData> _projectileJobOutputs => ref _jobDataRuntime.ProjectileJobOutputs;
|
||||||
|
private ref NativeList<CollisionCandidateData> _collisionCandidates => ref _jobDataRuntime.CollisionCandidates;
|
||||||
|
private ref NativeParallelMultiHashMap<long, int> _enemySeparationBuckets => ref _jobDataRuntime.EnemySeparationBuckets;
|
||||||
|
private ref NativeParallelMultiHashMap<long, int> _enemyCollisionBuckets => ref _jobDataRuntime.EnemyCollisionBuckets;
|
||||||
|
private List<AreaCollisionHitEventData> _areaCollisionHitEvents => _jobDataRuntime.AreaCollisionHitEvents;
|
||||||
|
private HashSet<long> _areaCollisionHitDedupKeys => _jobDataRuntime.AreaCollisionHitDedupKeys;
|
||||||
|
private ref int _lastCollisionQueryCount => ref _jobDataRuntime.LastCollisionQueryCount;
|
||||||
|
private ref int _lastProjectileCollisionQueryCount => ref _jobDataRuntime.LastProjectileCollisionQueryCount;
|
||||||
|
private ref int _lastAreaCollisionQueryCount => ref _jobDataRuntime.LastAreaCollisionQueryCount;
|
||||||
|
private ref int _lastCollisionCandidateCount => ref _jobDataRuntime.LastCollisionCandidateCount;
|
||||||
|
private ref int _lastProjectileCollisionCandidateCount => ref _jobDataRuntime.LastProjectileCollisionCandidateCount;
|
||||||
|
private ref int _lastAreaCollisionCandidateCount => ref _jobDataRuntime.LastAreaCollisionCandidateCount;
|
||||||
|
private ref int _lastResolvedAreaHitCount => ref _jobDataRuntime.LastResolvedAreaHitCount;
|
||||||
|
private ref float _lastCollisionCellSize => ref _jobDataRuntime.LastCollisionCellSize;
|
||||||
|
private ref bool _lastCollisionHasEnemyTargets => ref _jobDataRuntime.LastCollisionHasEnemyTargets;
|
||||||
|
|
||||||
|
private ref JobHandle _collisionCandidateQueryHandle => ref _collisionPipelineRuntime.CollisionCandidateQueryHandle;
|
||||||
|
private ref bool _collisionCandidateQueryScheduled => ref _collisionPipelineRuntime.CollisionCandidateQueryScheduled;
|
||||||
|
|
||||||
|
private ref NativeParallelMultiHashMap<long, int> _enemyTargetBuckets => ref _targetSelectionRuntime.EnemyTargetBuckets;
|
||||||
|
private ref bool _enemyTargetBucketsDirty => ref _targetSelectionRuntime.EnemyTargetBucketsDirty;
|
||||||
|
|
||||||
|
private float _projectileCollisionQueryRadius => _collisionPipelineSettings.ProjectileCollisionQueryRadius;
|
||||||
|
private int _projectileMaxCandidatesPerQuery => _collisionPipelineSettings.ProjectileMaxCandidatesPerQuery;
|
||||||
|
private float _projectileCollisionCellSize => _collisionPipelineSettings.ProjectileCollisionCellSize;
|
||||||
|
private bool _dispatchProjectileHitPresentationEvent => _collisionPipelineSettings.DispatchProjectileHitPresentationEvent;
|
||||||
|
private bool _dispatchProjectileHitMarkerEvent => _collisionPipelineSettings.DispatchProjectileHitMarkerEvent;
|
||||||
|
private bool _dispatchProjectileHitEffectEvent => _collisionPipelineSettings.DispatchProjectileHitEffectEvent;
|
||||||
|
private int _projectileHitPresentationEffectTypeId => _collisionPipelineSettings.ProjectileHitPresentationEffectTypeId;
|
||||||
|
private float _targetSelectionCellSize => _targetSelectionSettings.CellSize;
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
private sealed class CollisionPipelineSettings
|
||||||
|
{
|
||||||
|
[Header("Projectile Collision Query")]
|
||||||
|
[Tooltip("Projectile broad-phase collision query radius.")]
|
||||||
|
public float ProjectileCollisionQueryRadius = 0.35f;
|
||||||
|
|
||||||
|
[Tooltip("Maximum retained candidates per projectile query.")]
|
||||||
|
public int ProjectileMaxCandidatesPerQuery = 1;
|
||||||
|
|
||||||
|
[Tooltip("Broad-phase bucket cell size. <=0 derives from query radius.")]
|
||||||
|
public float ProjectileCollisionCellSize = 0f;
|
||||||
|
|
||||||
|
[Header("Projectile Hit Event Dispatch")]
|
||||||
|
[Tooltip("Dispatch projectile hit presentation event.")]
|
||||||
|
public bool DispatchProjectileHitPresentationEvent = true;
|
||||||
|
|
||||||
|
[Tooltip("Request hit marker when projectile hits.")]
|
||||||
|
public bool DispatchProjectileHitMarkerEvent = true;
|
||||||
|
|
||||||
|
[Tooltip("Request hit effect when projectile hits.")]
|
||||||
|
public bool DispatchProjectileHitEffectEvent = true;
|
||||||
|
|
||||||
|
[Tooltip("Default hit effect entity type id in presentation event. 0 means not specified.")]
|
||||||
|
public int ProjectileHitPresentationEffectTypeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
private sealed class TargetSelectionSettings
|
||||||
|
{
|
||||||
|
[Header("Target Selection")]
|
||||||
|
[Tooltip("Spatial hash cell size for nearest-enemy queries.")]
|
||||||
|
public float CellSize = 2f;
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class SimulationStateStore
|
||||||
|
{
|
||||||
|
public readonly List<EnemySimData> Enemies = new();
|
||||||
|
public readonly List<ProjectileSimData> Projectiles = new();
|
||||||
|
public readonly List<PickupSimData> Pickups = new();
|
||||||
|
public readonly List<int> ProjectileRecycleEntityIds = new();
|
||||||
|
public readonly EntityBinding EnemyBinding = new();
|
||||||
|
public readonly EntityBinding ProjectileBinding = new();
|
||||||
|
public readonly EntityBinding PickupBinding = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class JobDataRuntimeState
|
||||||
|
{
|
||||||
|
public NativeList<EnemyJobInputData> EnemyJobInputs;
|
||||||
|
public NativeList<EnemyJobOutputData> EnemyJobOutputs;
|
||||||
|
public NativeList<EnemyJobOutputData> EnemyJobSeparationOutputs;
|
||||||
|
public NativeList<float2> EnemySeparationPreviousPushes;
|
||||||
|
public NativeList<float2> EnemySeparationCurrentPushes;
|
||||||
|
public NativeList<ProjectileJobInputData> ProjectileJobInputs;
|
||||||
|
public NativeList<ProjectileJobOutputData> ProjectileJobOutputs;
|
||||||
|
public NativeList<CollisionCandidateData> CollisionCandidates;
|
||||||
|
public NativeParallelMultiHashMap<long, int> EnemySeparationBuckets;
|
||||||
|
public NativeParallelMultiHashMap<long, int> EnemyCollisionBuckets;
|
||||||
|
public readonly List<AreaCollisionHitEventData> AreaCollisionHitEvents = new(32);
|
||||||
|
public readonly HashSet<long> AreaCollisionHitDedupKeys = new();
|
||||||
|
public int LastCollisionQueryCount;
|
||||||
|
public int LastProjectileCollisionQueryCount;
|
||||||
|
public int LastAreaCollisionQueryCount;
|
||||||
|
public int LastCollisionCandidateCount;
|
||||||
|
public int LastProjectileCollisionCandidateCount;
|
||||||
|
public int LastAreaCollisionCandidateCount;
|
||||||
|
public int LastResolvedAreaHitCount;
|
||||||
|
public float LastCollisionCellSize;
|
||||||
|
public bool LastCollisionHasEnemyTargets;
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class CollisionPipelineRuntimeState
|
||||||
|
{
|
||||||
|
public JobHandle CollisionCandidateQueryHandle;
|
||||||
|
public bool CollisionCandidateQueryScheduled;
|
||||||
|
public readonly HashSet<int> ProjectileResolvedEntityIds = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class TargetSelectionRuntimeState
|
||||||
|
{
|
||||||
|
public NativeParallelMultiHashMap<long, int> EnemyTargetBuckets;
|
||||||
|
public bool EnemyTargetBucketsDirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e562a5a506b1424bb1a9eff97c8c469e
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
using Components;
|
|
||||||
using Entity;
|
using Entity;
|
||||||
using Entity.EntityData;
|
using Entity.EntityData;
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Simulation
|
namespace Simulation
|
||||||
{
|
{
|
||||||
|
|
@ -103,41 +101,6 @@ namespace Simulation
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static EnemySimData CreateEnemyInitialSimData(EnemyBase enemy, EnemyData enemyData)
|
|
||||||
{
|
|
||||||
Transform enemyTransform = enemy.CachedTransform;
|
|
||||||
MovementComponent movementComponent = enemy.GetComponent<MovementComponent>();
|
|
||||||
|
|
||||||
float speed = 0f;
|
|
||||||
if (enemyData != null)
|
|
||||||
{
|
|
||||||
speed = enemyData.SpeedBase;
|
|
||||||
}
|
|
||||||
else if (movementComponent != null)
|
|
||||||
{
|
|
||||||
speed = movementComponent.Speed;
|
|
||||||
}
|
|
||||||
|
|
||||||
float attackRange = enemy != null && enemy.AttackRange > 0f
|
|
||||||
? enemy.AttackRange
|
|
||||||
: DefaultAttackRange;
|
|
||||||
|
|
||||||
return new EnemySimData
|
|
||||||
{
|
|
||||||
EntityId = enemy.Id,
|
|
||||||
Position = enemyTransform.position,
|
|
||||||
Forward = enemyTransform.forward,
|
|
||||||
Rotation = enemyTransform.rotation,
|
|
||||||
Speed = speed,
|
|
||||||
AttackRange = attackRange,
|
|
||||||
AvoidEnemyOverlap = movementComponent != null && movementComponent.AvoidEnemyOverlap,
|
|
||||||
EnemyBodyRadius = movementComponent != null ? movementComponent.EnemyBodyRadius : 0.45f,
|
|
||||||
SeparationIterations = movementComponent != null ? movementComponent.SeparationIterations : 2,
|
|
||||||
TargetType = 0,
|
|
||||||
State = EnemyStateIdle
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Projectile Simulation State
|
#region Projectile Simulation State
|
||||||
|
|
@ -196,55 +159,6 @@ namespace Simulation
|
||||||
RemoveProjectileByEntityId(entityId);
|
RemoveProjectileByEntityId(entityId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ProjectileSimData CreateProjectileInitialSimData(EntityBase projectileEntity, object userData)
|
|
||||||
{
|
|
||||||
Vector3 forward = projectileEntity.CachedTransform.forward;
|
|
||||||
int ownerEntityId = 0;
|
|
||||||
Vector3 velocity = Vector3.zero;
|
|
||||||
float speed = 0f;
|
|
||||||
float lifeTime = 0f;
|
|
||||||
|
|
||||||
if (userData is EnemyProjectileData enemyProjectileData)
|
|
||||||
{
|
|
||||||
ownerEntityId = enemyProjectileData.OwnerEntityId;
|
|
||||||
|
|
||||||
Vector3 direction = enemyProjectileData.Direction;
|
|
||||||
direction.y = 0f;
|
|
||||||
if (direction.sqrMagnitude > Mathf.Epsilon)
|
|
||||||
{
|
|
||||||
direction.Normalize();
|
|
||||||
forward = direction;
|
|
||||||
}
|
|
||||||
else if (forward.sqrMagnitude > Mathf.Epsilon)
|
|
||||||
{
|
|
||||||
forward = forward.normalized;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
forward = Vector3.forward;
|
|
||||||
}
|
|
||||||
|
|
||||||
speed = Mathf.Max(0f, enemyProjectileData.Speed);
|
|
||||||
velocity = forward * speed;
|
|
||||||
lifeTime = Mathf.Max(0f, enemyProjectileData.LifeTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ProjectileSimData
|
|
||||||
{
|
|
||||||
EntityId = projectileEntity.Id,
|
|
||||||
OwnerEntityId = ownerEntityId,
|
|
||||||
Position = projectileEntity.CachedTransform.position,
|
|
||||||
Forward = forward,
|
|
||||||
Velocity = velocity,
|
|
||||||
Speed = speed,
|
|
||||||
LifeTime = lifeTime,
|
|
||||||
Age = 0f,
|
|
||||||
Active = true,
|
|
||||||
RemainingLifetime = lifeTime,
|
|
||||||
State = ProjectileStateActive
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Pickup Simulation State
|
#region Pickup Simulation State
|
||||||
|
|
@ -303,17 +217,6 @@ namespace Simulation
|
||||||
RemovePickupByEntityId(entityId);
|
RemovePickupByEntityId(entityId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PickupSimData CreatePickupInitialSimData(EntityBase pickupEntity)
|
|
||||||
{
|
|
||||||
return new PickupSimData
|
|
||||||
{
|
|
||||||
EntityId = pickupEntity.Id,
|
|
||||||
Position = pickupEntity.CachedTransform.position,
|
|
||||||
PickupRadius = 0.35f,
|
|
||||||
State = 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -6,11 +6,6 @@ namespace Simulation
|
||||||
{
|
{
|
||||||
public sealed partial class SimulationWorld
|
public sealed partial class SimulationWorld
|
||||||
{
|
{
|
||||||
private NativeParallelMultiHashMap<long, int> _enemyTargetBuckets;
|
|
||||||
private bool _enemyTargetBucketsDirty = true;
|
|
||||||
|
|
||||||
[SerializeField] private float _targetSelectionCellSize = 2f;
|
|
||||||
|
|
||||||
public bool TryGetNearestEnemyEntityId(Vector3 origin, float maxSqrRange, out int enemyEntityId)
|
public bool TryGetNearestEnemyEntityId(Vector3 origin, float maxSqrRange, out int enemyEntityId)
|
||||||
{
|
{
|
||||||
enemyEntityId = 0;
|
enemyEntityId = 0;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,9 @@ namespace Simulation
|
||||||
{
|
{
|
||||||
// Partial layout:
|
// Partial layout:
|
||||||
// - SimulationWorld.cs: 核心状态、常量和 Unity 生命周期入口点。
|
// - SimulationWorld.cs: 核心状态、常量和 Unity 生命周期入口点。
|
||||||
|
// - SimulationWorld.RuntimeModules.cs: 运行时域对象、配置和状态代理。
|
||||||
// - SimulationWorld.SimEntityState.cs: 模拟状态的增删改查和生命周期注册。
|
// - SimulationWorld.SimEntityState.cs: 模拟状态的增删改查和生命周期注册。
|
||||||
|
// - SimulationWorld.EntityToSimData.cs: Unity 实体到 sim data 的初始化适配。
|
||||||
// - SimulationWorld.EntitySync.cs: GameFramework 实体 show/hide 事件桥。
|
// - SimulationWorld.EntitySync.cs: GameFramework 实体 show/hide 事件桥。
|
||||||
// - SimulationWorld.TargetSelectionSpatialIndex.cs: 最近敌空间索引查询。
|
// - SimulationWorld.TargetSelectionSpatialIndex.cs: 最近敌空间索引查询。
|
||||||
// - Presentation/SimulationWorld.TransformSync.cs: late-update transform 同步桥。
|
// - Presentation/SimulationWorld.TransformSync.cs: late-update transform 同步桥。
|
||||||
|
|
@ -42,16 +44,6 @@ namespace Simulation
|
||||||
private TransformSync _transformSync;
|
private TransformSync _transformSync;
|
||||||
private HitPresentation _hitPresentation;
|
private HitPresentation _hitPresentation;
|
||||||
|
|
||||||
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 List<int> _projectileRecycleEntityIds = new List<int>();
|
|
||||||
private readonly HashSet<int> _projectileResolvedEntityIds = new HashSet<int>();
|
|
||||||
|
|
||||||
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<EnemySimData> Enemies => _enemies;
|
||||||
public IReadOnlyList<ProjectileSimData> Projectiles => _projectiles;
|
public IReadOnlyList<ProjectileSimData> Projectiles => _projectiles;
|
||||||
public IReadOnlyList<PickupSimData> Pickups => _pickups;
|
public IReadOnlyList<PickupSimData> Pickups => _pickups;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue