using Unity.Burst; using Unity.Collections; using Unity.Jobs; using Unity.Mathematics; namespace SepCore.Simulation { internal static class ProjectileMovementSystem { // 读写集:R/W ProjectileTransform / ProjectileVelocity / ProjectileLife public static JobHandle Schedule(ProjectileStore projectiles, float deltaTime, float3 playerPosition, float maxDistanceFromPlayer, float maxVerticalOffsetFromPlayer, JobHandle dependency) { int projectileCount = projectiles.Count; if (projectileCount == 0) { return dependency; } float maxDistance = math.max(0f, maxDistanceFromPlayer); float maxSqrDistanceFromPlayer = maxDistance > 0f ? maxDistance * maxDistance : -1f; float maxVerticalOffset = math.max(0f, maxVerticalOffsetFromPlayer); ProjectileMovementJob job = new ProjectileMovementJob { Transforms = projectiles.Transform.Data.AsArray(), Velocities = projectiles.Velocity.Data.AsArray(), Lives = projectiles.Life.Data.AsArray(), DeltaTime = deltaTime, PlayerPosition = playerPosition, MaxSqrDistanceFromPlayer = maxSqrDistanceFromPlayer, MaxVerticalOffsetFromPlayer = maxVerticalOffset }; return job.Schedule(projectileCount, 64, dependency); } } [BurstCompile] internal struct ProjectileMovementJob : IJobParallelFor { public NativeArray Transforms; public NativeArray Velocities; public NativeArray Lives; public float DeltaTime; public float3 PlayerPosition; public float MaxSqrDistanceFromPlayer; public float MaxVerticalOffsetFromPlayer; public void Execute(int index) { ProjectileLifeSim life = Lives[index]; if (!life.Active) { return; } ProjectileTransformSim transform = Transforms[index]; float3 velocity = Velocities[index].Velocity; float3 nextPosition = transform.Position + velocity * DeltaTime; float nextAge = math.max(0f, life.Age + DeltaTime); bool shouldExpire = life.LifeTime > 0f && nextAge >= life.LifeTime; if (!shouldExpire && MaxSqrDistanceFromPlayer > 0f) { float3 horizontalDelta = new float3(nextPosition.x - PlayerPosition.x, 0f, nextPosition.z - PlayerPosition.z); shouldExpire = math.lengthsq(horizontalDelta) > MaxSqrDistanceFromPlayer; } if (!shouldExpire && MaxVerticalOffsetFromPlayer > 0f) { shouldExpire = math.abs(nextPosition.y - PlayerPosition.y) > MaxVerticalOffsetFromPlayer; } transform.Position = nextPosition; if (math.lengthsq(velocity) > float.Epsilon) { transform.Forward = math.normalizesafe(velocity, transform.Forward); } Transforms[index] = transform; life.Age = nextAge; life.Active = !shouldExpire; Lives[index] = life; } } }