/// <summary> /// Generates a flocking behavior based on parameters contained within FlockingData. /// </summary> public static void Flock(FlockingData flockingData, bool checkForBounds = true, bool checkForCollision = true) { // Change goal position within flock limits based on random frequency if (Random.Range(0, 100) < flockingData.positionGoalChangeFrequency) { flockingData.AdjustGoalWithinFlockRange(Random.Range(-flockingData.FlockLimits.x, flockingData.FlockLimits.x), Random.Range(-flockingData.FlockLimits.y, flockingData.FlockLimits.y), Random.Range(-flockingData.FlockLimits.z, flockingData.FlockLimits.z)); } for (int i = 0; i < flockingData.allUnits.Length; i++) { UnitData unitData = flockingData.allUnits[i]; Vector3 direction = Vector3.zero; Bounds bounds = new Bounds(flockingData.FlockGoal, flockingData.FlockLimits * 2); RaycastHit?nullableHitInfo = CheckForCollision(unitData.transform); // Rotate unit back towards the flock if they move outside of flock limit if (CheckFlockBounds(unitData.transform, bounds) && checkForBounds) { direction = flockingData.FlockGoal - unitData.transform.position; unitData.transform.rotation = Quaternion.Slerp(unitData.transform.rotation, Quaternion.LookRotation(direction), flockingData.unitRotationSpeed * Time.deltaTime); } // Rotate unit away to avoid obstacles else if (nullableHitInfo != null && checkForCollision) { RaycastHit hitInfo = (RaycastHit)nullableHitInfo; Debug.DrawRay(unitData.transform.position, hitInfo.point, Color.yellow); direction = Vector3.Reflect(unitData.transform.forward, hitInfo.normal); Debug.DrawRay(unitData.transform.position, direction, Color.red); unitData.transform.rotation = Quaternion.Slerp(unitData.transform.rotation, Quaternion.LookRotation(direction), flockingData.unitRotationSpeed * Time.deltaTime); } // Apply regular flocking behavior based on random frequency else { //direction = new Vector3(Random.Range(-flockingData.FlockLimits.x, flockingData.FlockLimits.x), // Random.Range(-flockingData.FlockLimits.y, flockingData.FlockLimits.y), // Random.Range(-flockingData.FlockLimits.z, flockingData.FlockLimits.z)); if (Random.Range(0, 100) < 1) { unitData.speed = Random.Range(flockingData.unitMinSpeed, flockingData.unitMaxSpeed); } if (Random.Range(0, 100) < flockingData.flockingFrequency) { direction = FlockDirection(unitData, flockingData); } } if (direction != Vector3.zero) { unitData.transform.rotation = Quaternion.Slerp(unitData.transform.rotation, Quaternion.LookRotation(direction), flockingData.unitRotationSpeed * Time.deltaTime); } unitData.transform.Translate(0, 0, Time.deltaTime * unitData.speed); } }
public static Vector3 FlockDirection(UnitData unitData, FlockingData flockingData) { Vector3 headingVector = Vector3.zero; Vector3 avoidanceVector = Vector3.zero; Vector3 direction = Vector3.zero; float groupSpeed = 0.01f; float minDistanceToGroup; int groupSize = 0; foreach (var unit in flockingData.allUnits) { if (unit.transform != unitData.transform) { // Check distance to each fish in flock minDistanceToGroup = Vector3.Distance(unit.transform.position, unitData.transform.position); if (minDistanceToGroup <= flockingData.maximumGroupDistance) { //Add that fish to group & add their position to group avg (flock center avg) headingVector += unit.transform.position; groupSize++; // Check if distance is bigger than minimum distance value if (minDistanceToGroup < flockingData.minimumUnitProximity) { // Include that unit's position to the avoidance vector (don't hit neighbours) avoidanceVector = avoidanceVector + (unitData.transform.position - unit.transform.position); } // TEMPORARY - Grab the speed of each unit and add it to the global speed value (determine global flock speed) groupSpeed = groupSpeed + unitData.speed; } } } if (groupSize > 0) { // Find the avg vector of the flock's center based on group size headingVector = headingVector / groupSize; // If a goal position is set, calculate the flock's heading based on goal location if (flockingData.FlockGoal != null) { headingVector = headingVector + (flockingData.FlockGoal - unitData.transform.position); } // Set the individual's speed to the avg group speed unitData.speed = groupSpeed / groupSize; // Determine the direction the fish wants to travel to (based on center of flock & avoidance of neighbours) direction = (headingVector + avoidanceVector) - unitData.transform.position; } return(direction); }
/// <summary> /// Create a new flock and assign the necessary behavior. /// </summary> public static void CreateFlock(FlockingData flockingData) { flockingData.FlockGoal = flockingData.FlockCenter.position; flockingData.allUnits = new UnitData[flockingData.numUnits]; for (int i = 0; i < flockingData.numUnits; i++) { Vector3 pos = flockingData.FlockGoal + new Vector3(Random.Range(-flockingData.FlockLimits.x, flockingData.FlockLimits.x), Random.Range(-flockingData.FlockLimits.y, flockingData.FlockLimits.y), Random.Range(-flockingData.FlockLimits.z, flockingData.FlockLimits.z)); flockingData.allUnits[i].transform = GameObject.Instantiate(flockingData.unitPrefab, pos, Quaternion.identity).transform; flockingData.allUnits[i].speed = Random.Range(flockingData.unitMinSpeed, flockingData.unitMaxSpeed); } }