/// <summary> /// Handles the starship's targeting. /// </summary> protected override void OnUpdate() { // Note that we need to pass the quadrant system structure to WithReadOnly(). This is because other systems are // reading from it in parallel and we need to ensure the safety system that we will not modify the memory. var quadrantHashMap = QuadrantSystem.QuadrantHashMap; Entities.WithReadOnly(quadrantHashMap).WithAll <SpaceshipTag>().ForEach( (Entity entity, ref TargetingComponent target, in Translation pos, in TeamComponent team, in BoidComponent boid) => { var hashMapKey = QuadrantSystem.HashKeyFromPosition(pos.Value); // Search neighboring quadrants if the currently locked target is still in range, // or find a new closest enemy target var minDistance = float.MaxValue; var closestEntity = target.TargetEntity; var closestPos = target.TargetPosition; var newTargetFound = false; if (SearchQuadrantNeighbors(in quadrantHashMap, hashMapKey, entity, target.TargetingRadius, pos.Value, team.Team, target.TargetLocked, target.TargetEntity, ref minDistance, ref closestEntity, ref closestPos, ref newTargetFound)) { return; // Currently locked target still in range } if (newTargetFound) { target.TargetPosition = closestPos; target.TargetEntity = closestEntity; target.TargetLocked = true; } else { target.TargetLocked = false; } }).ScheduleParallel();
/// <summary> /// Handles flocking simulation. /// </summary> protected override void OnUpdate() { var deltaTime = Time.DeltaTime; // Note that we need to pass the quadrant system structure to WithReadOnly(). This is because other systems are // reading from it in parallel and we need to ensure the safety system that we will not modify the memory. var quadrantHashMap = QuadrantSystem.QuadrantHashMap; // Query for all entities with BoidObstacle tag component var allObstacles = GetEntityQuery(ComponentType.ReadOnly <BoidObstacle>()).ToEntityArray(Allocator.TempJob); Entities.WithReadOnly(quadrantHashMap).WithReadOnly(allObstacles).WithAll <SpaceshipTag>().ForEach( (Entity entity, ref MovementComponent movement, in BoidComponent boid, in Translation pos, in TargetingComponent target) => { var neighborCnt = 0; var cohesionPos = float3.zero; // For average position in neighborhood var alignmentVec = float3.zero; // For average alignment vector var separationVec = float3.zero; // For average separation vector // Get all translation components (will be needed for the obstacle positions) var allTranslations = GetComponentDataFromEntity <Translation>(true); // Search neighboring quadrants and update aggregate steering data var hashMapKey = QuadrantSystem.HashKeyFromPosition(pos.Value); SearchQuadrantNeighbors(in quadrantHashMap, hashMapKey, entity, boid.CellRadius, pos.Value, ref neighborCnt, ref cohesionPos, ref alignmentVec, ref separationVec); // Average steering data if (neighborCnt > 0) { separationVec /= neighborCnt; cohesionPos /= neighborCnt; alignmentVec /= neighborCnt; } else { // apply no steering cohesionPos = pos.Value; alignmentVec = movement.Heading; separationVec = movement.Heading; } // Compute obstacle avoidance var avoidanceHeading = float3.zero; var avoidObstacle = false; // if the boid should avoid obstacle instead of normal steering for (var i = 0; i < allObstacles.Length; i++) { var otherPos = allTranslations[allObstacles[i]]; // Obstacle position var distance = Vector3.Distance(pos.Value, otherPos.Value); if (distance >= boid.ObstacleAversionDistance) { continue; // This obstacle is not close } avoidObstacle = true; var obstacleSteering = pos.Value - otherPos.Value; if (obstacleSteering.Equals(float3.zero)) { obstacleSteering = movement.Heading; } obstacleSteering = math.normalizesafe(obstacleSteering); avoidanceHeading = otherPos.Value + obstacleSteering * boid.ObstacleAversionDistance - pos.Value; break; // We have found an obstacle to avoid } // Check if we are in pursuit and compute pursuit steering var pursuitSteering = float3.zero; if (target.TargetLocked) { pursuitSteering = math.normalizesafe(target.TargetPosition - pos.Value) * boid.PursuitWeight; } // Combine all steering factors and decide which one to use var targetSteering = math.normalizesafe(movement.Target - pos.Value) * boid.TargetWeight; var alignmentSteering = math.normalizesafe(alignmentVec) * boid.AlignmentWeight; var cohesionSteering = math.normalizesafe(cohesionPos - pos.Value) * boid.CohesionWeight; var separationSteering = math.normalizesafe(separationVec) * boid.SeparationWeight; var normalHeading = math.normalizesafe(targetSteering + alignmentSteering + cohesionSteering + separationSteering + pursuitSteering); var targetHeading = avoidObstacle ? avoidanceHeading : normalHeading; // Setting a new heading (shifted towards target heading) movement.Heading = math.normalizesafe(movement.Heading + deltaTime * boid.SteeringSpeed * (targetHeading - movement.Heading)); }).ScheduleParallel();