/// <summary> /// Assigning the target group and the fringe group /// </summary> private void UpdateActiveGroups(List <Boid> boidList) { targetGroup.Clear(); fringeGroup.Clear(); if (boidList == null || boidList.Count == 0) { return; } //find the nearest boid and form the target group from it and its neighbours Boid nearestBoid = BoidUtility.GetNearest(boidList, Position); targetGroup.Add(nearestBoid); foreach (var boid in nearestBoid.Neighbours) { targetGroup.Add(boid); } //form the fringe group from the neighbours of the target group that arent in the target group foreach (var boid in targetGroup) { foreach (var neighbour in boid.Neighbours) { if (!targetGroup.Contains(neighbour) && !fringeGroup.Contains(neighbour)) { fringeGroup.Add(neighbour); } } } }
/// <summary> /// Vector away from the average heading of the target group. /// We use a timer to track how long we have been attempting an attack, and based on this we /// decide how strongly we should be withdrawing. /// </summary> private Vector3 CalculateAttemptWithdrawal(List <Boid> boids, float currentWithdrawalWeight) { if (boids == null || boids.Count == 0) { return(Vector3.zero); } return((Position - BoidUtility.GetAveragePosition(boids)).normalized * currentWithdrawalWeight); }
/// <summary> /// Vector towards the average position of the targets. /// By directing to the center of the group, we can achieve maximum /// fragmentation of the group and hopefully futher isolate target boids. /// </summary> private Vector3 CalculateGroupDisruption(HashSet <Boid> targets, float currentWithdrawalWeight) { if (targets == null || targets.Count == 0) { return(Vector3.zero); } return((BoidUtility.GetAveragePosition(targets) - Position).normalized * (1f - currentWithdrawalWeight)); }
/// <summary> /// Vector towards the the average position of the fringes. /// By directing to the position of the fringe group, we act to cut off /// the target group from reconnecting with the main flock. /// </summary> private Vector3 CalculateFlockSeparation(HashSet <Boid> fringes, float currentWithdrawalWeight) { if (fringes == null || fringes.Count == 0) { return(Vector3.zero); } return((BoidUtility.GetAveragePosition(fringes) - Position).normalized * (1f - currentWithdrawalWeight)); }
/// <summary> /// Vector towards the nearest target. /// This is the actual attack of a particular boid. As we get closer to a boid, /// our tendency to attack it increases. /// </summary> private Vector3 CalculateAttackEagerness(HashSet <Boid> targets, float currentWithdrawalWeight) { if (targets == null || targets.Count == 0) { return(Vector3.zero); } var toNearestBoid = BoidUtility.GetNearest(targets, Position).Position - Position; var weight = 1f - Mathf.Clamp01(toNearestBoid.sqrMagnitude / attackRadiusSqr); //weight nearer boids most heavily return(toNearestBoid.normalized * weight * (1f - currentWithdrawalWeight)); }
/// <summary> /// Vector away from other predators. /// This prevents the predator from targeting boids that are also /// being targeted by other predators. /// </summary> private Vector3 CalculateIndependence(List <BoidPredator> predators) { HashSet <BoidPredator> others = new HashSet <BoidPredator>(predators); others.Remove(this); if (others.Count > 0) { var toNearestPred = BoidUtility.GetNearest(others, Position).Position - Position; var weight = 1f - Mathf.Clamp01(toNearestPred.sqrMagnitude / viewRadius); //weight nearer predators most heavily return(-toNearestPred.normalized * weight); } return(Vector3.zero); }
private bool CheckForTargetCapture() { if (targetGroup == null || targetGroup.Count == 0) { return(false); } var nearestBoid = BoidUtility.GetNearest(targetGroup, Position); if ((nearestBoid.Position - Position).sqrMagnitude <= captureRadiusSqr) { nearestBoid.Kill(); killCount++; return(true); } return(false); }
private void OnDrawGizmosSelected() { if (!showDebug) { return; } //Targets Gizmos.color = Color.red; ///draw average position of target group var avgTargetPos = BoidUtility.GetAveragePosition(targetGroup); if (avgTargetPos != Vector3.zero) { Gizmos.DrawSphere(avgTargetPos, 0.33f); } ///draw position of each target foreach (var b in targetGroup) { Gizmos.DrawWireSphere(b.Position, 0.25f); } //Fringes Gizmos.color = Color.yellow; ///draw average position of fringe group var avgFringePos = BoidUtility.GetAveragePosition(fringeGroup); if (avgFringePos != Vector3.zero) { Gizmos.DrawSphere(avgFringePos, 0.33f); } ///draw position of each fringe boid foreach (var b in fringeGroup) { Gizmos.DrawWireSphere(b.Position, 0.25f); } //Withdrawal float currentWithdrawlWeighting = Mathf.InverseLerp(withdrawalStartTime, withdrawalEndTime, timeSinceAttackStarted); if (currentWithdrawlWeighting <= 0.5f) { Gizmos.color = Color.Lerp(Color.green, Color.yellow, currentWithdrawlWeighting * 2); } else { Gizmos.color = Color.Lerp(Color.yellow, Color.red, 2 * currentWithdrawlWeighting - 1f); } Gizmos.DrawCube(Position + Vector3.up * 0.2f, Vector3.one * 0.4f); var separation = flockSeparation * separationWeightNorm; var disruption = groupDisruption * disruptionWeightNorm; var eagerness = attackEagerness * eagernessWeightNorm; var withdrawal = attemptWithdrawal * withdrawalWeightNorm; var independence = huntIndependence * independenceWeightNorm; Gizmos.color = Color.white; Gizmos.DrawLine(Position, Position + separation * 5); Gizmos.color = Color.cyan; Gizmos.DrawLine(Position, Position + disruption * 5); Gizmos.color = Color.magenta; Gizmos.DrawLine(Position, Position + eagerness * 5); Gizmos.color = Color.blue; Gizmos.DrawLine(Position, Position + withdrawal * 5); Gizmos.color = Color.black; Gizmos.DrawLine(Position, Position + independence * 5); }
/// <summary> /// Flock Cohesion /// </summary> private Vector3 CalculateCohesion(List <Boid> neighbours) { return((Vector3.Lerp(BoidUtility.GetAveragePosition(neighbours), Position, 0.5f) - Position).normalized); }
/// <summary> /// Velocity Alignment /// </summary> private Vector3 CalculateAlignment(List <Boid> neighbours) { return(BoidUtility.GetAverageHeading(neighbours, Position, controller.ViewRadiusSqr, VelocityNormalized)); }