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 _enemies => _simulationState.Enemies; private List _projectiles => _simulationState.Projectiles; private List _pickups => _simulationState.Pickups; private List _projectileRecycleEntityIds => _simulationState.ProjectileRecycleEntityIds; private HashSet _projectileResolvedEntityIds => _collisionPipelineRuntime.ProjectileResolvedEntityIds; private EntityBinding EnemyBinding => _simulationState.EnemyBinding; private EntityBinding ProjectileBinding => _simulationState.ProjectileBinding; private EntityBinding PickupBinding => _simulationState.PickupBinding; private ref NativeList _enemyJobInputs => ref _jobDataRuntime.EnemyJobInputs; private ref NativeList _enemyJobOutputs => ref _jobDataRuntime.EnemyJobOutputs; private ref NativeList _enemyJobSeparationOutputs => ref _jobDataRuntime.EnemyJobSeparationOutputs; private ref NativeList _enemySeparationPreviousPushes => ref _jobDataRuntime.EnemySeparationPreviousPushes; private ref NativeList _enemySeparationCurrentPushes => ref _jobDataRuntime.EnemySeparationCurrentPushes; private ref NativeList _projectileJobInputs => ref _jobDataRuntime.ProjectileJobInputs; private ref NativeList _projectileJobOutputs => ref _jobDataRuntime.ProjectileJobOutputs; private ref NativeList _collisionCandidates => ref _jobDataRuntime.CollisionCandidates; private ref NativeParallelMultiHashMap _enemySeparationBuckets => ref _jobDataRuntime.EnemySeparationBuckets; private ref NativeParallelMultiHashMap _enemyCollisionBuckets => ref _jobDataRuntime.EnemyCollisionBuckets; private List _areaCollisionHitEvents => _jobDataRuntime.AreaCollisionHitEvents; private HashSet _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 _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 Enemies = new(); public readonly List Projectiles = new(); public readonly List Pickups = new(); public readonly List ProjectileRecycleEntityIds = new(); public readonly EntityBinding EnemyBinding = new(); public readonly EntityBinding ProjectileBinding = new(); public readonly EntityBinding PickupBinding = new(); } private sealed class JobDataRuntimeState { public NativeList EnemyJobInputs; public NativeList EnemyJobOutputs; public NativeList EnemyJobSeparationOutputs; public NativeList EnemySeparationPreviousPushes; public NativeList EnemySeparationCurrentPushes; public NativeList ProjectileJobInputs; public NativeList ProjectileJobOutputs; public NativeList CollisionCandidates; public NativeParallelMultiHashMap EnemySeparationBuckets; public NativeParallelMultiHashMap EnemyCollisionBuckets; public readonly List AreaCollisionHitEvents = new(32); public readonly HashSet 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 ProjectileResolvedEntityIds = new(); } private sealed class TargetSelectionRuntimeState { public NativeParallelMultiHashMap EnemyTargetBuckets; public bool EnemyTargetBucketsDirty = true; } } }