void Awake() { //Instantiate Boids boidsList = new List <GameObject>(); statusList = new List <BoidStatus>(); hailList = HailController.hailList; // Get list from HailController for (int i = 0; i < boidCount; ++i) { for (int j = 0; j < boidCount; ++j) { GameObject newBoid = new GameObject(); newBoid.transform.parent = gameObject.transform; newBoid.name = "Boid No." + (i * boidCount + j).ToString(); GameObject instPrefab = Instantiate(boidPrefab); // insantiate the bird instPrefab.transform.parent = newBoid.transform; Vector3 startingPos = new Vector3(Random.Range(-2f, -2.5f), Random.Range(0.5f, 1f), Random.Range(-2f, -2.5f)); BoidStatus status = new BoidStatus(); status.position = startingPos + goalMarker.transform.position; newBoid.transform.position = status.position; newBoid.transform.localScale = new Vector3(.25f, .25f, .25f); status.boidObject = newBoid; boidsList.Add(newBoid); statusList.Add(status); } } direction = new Vector3[statusList.Count]; //hide the original prefab boidPrefab.SetActive(false); }
// Start is called before the first frame update void Start() { statusList = new List <BoidStatus>(); for (int i = 0; i < boidCount; ++i) { for (int j = 0; j < boidCount; ++j) { GameObject newBoid = new GameObject(); newBoid.transform.parent = gameObject.transform; newBoid.name = "Boid No." + (i * boidCount + j).ToString(); GameObject instPrefab = Instantiate(boidPrefab); instPrefab.transform.parent = newBoid.transform; Vector3 startingPos = new Vector3((float)j - 4, (float)-3, (float)i - 4); BoidStatus status = new BoidStatus(); status.position = startingPos + goalMarker.transform.position; newBoid.transform.position = status.position; status.boidObject = newBoid; status.boidObject.transform.localScale *= .25f; // status.velocity = new Vector3(0, 0, 0); statusList.Add(status); } } boidPrefab.SetActive(true); }
Vector3 calculate_separation(BoidStatus boid) { int neighbour_count = 0; //int terrain = 0; Vector3 point = boid.position; Vector3 velocity = new Vector3(0, 0, 0); for (int i = 0; i < statusList.Count; ++i) { if (statusList[i] != boid) { if (Vector3.Distance(statusList[i].position, point) < detection_radius) { velocity += statusList[i].position - point; neighbour_count++; } } } /*if(point.x >= 0 && point.z >= 0 && point.x < terrainMap.subdivision && point.z < terrainMap.subdivision) { * velocity.y += terrainMap.perlinHeight[(int)point.z , (int)point.x] - point.y; * terrain++; * Debug.Log("Got here"); * }*/ if (neighbour_count != 0) { velocity /= neighbour_count; velocity *= -1; } return(velocity.normalized); }
// Update is called once per frame void Update() { for (int i = 0; i < statusList.Count; ++i) { BoidStatus status = statusList[i]; status.velocity = Vector3.zero; Vector3 alignment = align_velocity(status); Vector3 cohesion = calculate_cohesion(status); Vector3 separation = calculate_separation(status); Vector3 leader = goalMarker.transform.position - status.position; Vector3 terrain_colide = Vector3.zero; float x = status.position.x; float z = status.position.z; if (x < 5f && z < 5f && x >= -5f && z >= -5f) { x = (x + 5f) * 25f; z = (z + 5f) * 25f; if (status.position.y <= terrainMap.perlinHeight[(int)x, (int)z] + 0.3f) { status.position.y += terrainMap.perlinHeight[(int)x, (int)z]; } } status.velocity += alignment * 1.5f + cohesion * 1.5f + separation * 6f + leader; status.velocity = status.velocity.normalized; status.position += status.velocity * .15f; //float noise_dist; //terrain collision prevention /*if(status.position.x < terrainMap.subdivision && status.position.z < terrainMap.subdivision && status.position.x >= 0 && status.position.z >= 0) { * status.position.y += terrainMap.perlinHeight[(int)status.position.x, (int)status.position.z]; * /* noise_dist = goalMarker.transform.position.y - terrainMap.perlinHeight[(int)status.position.x, (int)status.position.z]; * if((status.position.y * -1) >= noise_dist) { * status.position.y += terrainMap.perlinHeight[(int)status.position.x, (int)status.position.z]; * }*/ //if(status.position.y <= terrainMap.perlinHeight[(int)status.position.z, (int)status.position.x]) { //status.position.y += (terrainMap.perlinHeight[(int)status.position.z, (int)status.position.x] - status.position.y) + 0.1f; //Debug.Log("got here"); //} //} status.boidObject.transform.LookAt(status.position); status.boidObject.transform.Rotate(0, 90, 90); status.boidObject.transform.position = status.position; //status.boidObject.transform.forward = Vector3.Normalize(goalMarker.transform.position - status.boidObject.transform.position); statusList[i] = status; } }
// next 3 functions inspired by: https://gamedevelopment.tutsplus.com/tutorials/3-simple-rules-of-flocking-behaviors-alignment-cohesion-and-separation--gamedev-3444 Vector3 align_velocity(BoidStatus boid) { Vector3 point = boid.position; int neighbour_count = 0; Vector3 velocity = new Vector3(0, 0, 0); for (int i = 0; i < statusList.Count; ++i) { if (statusList[i] != boid) { if (Vector3.Distance(statusList[i].position, point) < detection_radius) { velocity += statusList[i].velocity; neighbour_count++; } } } if (neighbour_count != 0) { velocity /= neighbour_count; } return(velocity.normalized); }
// Update is called once per frame void Update() { for (int i = 0; i < statusList.Count; ++i) { BoidStatus status = statusList[i]; List <BoidStatus> neighbors = new List <BoidStatus>(); Vector3 V1 = new Vector3(0, 0, 0); // Separation Vector3 V2 = new Vector3(0, 0, 0); // Alignment Vector3 V3 = new Vector3(0, 0, 0); // Cohesion Vector3 V4 = new Vector3(0, 0, 0); // Predator Avoidance // Separation for (int j = 0; j < statusList.Count - 1; ++j) { BoidStatus nextStatus = statusList[j]; // Check every boid in list Vector3 boidVec = Vector3.Normalize(status.boidObject.transform.position - nextStatus.boidObject.transform.position); float distance = Vector3.Distance(status.boidObject.transform.position, nextStatus.boidObject.transform.position); // if distance is 0 then boids are the same if (distance <= separation && distance > 0) // Boids are too close { V1 = (boidVec / distance) * separation; status.boidObject.transform.position += V1 * Time.deltaTime * speed; neighbors.Add(nextStatus); // Keep track of all neighbors } else // They are an appropriate distance away { status.boidObject.transform.position += new Vector3(0f, 0f, 0f) * speed * Time.deltaTime; } } // Alignment V2 = BoidDir(status.position); // Cohesion Vector3 centerVec = new Vector3(0, 0, 0); V3 = new Vector3(0, 0, 0); for (int j = 0; j < neighbors.Count; ++j) { centerVec += goalMarker.transform.position; } centerVec = centerVec / neighbors.Count; if (centerVec.magnitude <= cohesion) { V3 = Vector3.Normalize(centerVec / centerVec.magnitude); status.boidObject.transform.position += V3 * Time.deltaTime * speed; } // Predator Avoidance, similar to separation -> separate from predator arrow Vector3 predVec = Vector3.Normalize(status.boidObject.transform.position - predator.transform.position); float pDistance = Vector3.Distance(predator.transform.position, status.boidObject.transform.position); if (pDistance <= predDistance && pDistance > 0) // change boids direction to keep away from predator { V4 = (predVec / pDistance) * predSeparation; direction[i] = Vector3.Normalize(V4 - status.boidObject.transform.position); predSeparation = predSeparation + .2f; } else // predator is far enough away { predSeparation = 1.0f; direction[i] = Vector3.Normalize(V2 - status.boidObject.transform.position); } // Check for hail hit for (int j = 0; j < hailList.Count; ++j) { Vector3 boid = status.boidObject.transform.position; Vector3 hail = hailList[j].hailObject.transform.position; if (Vector3.Distance(boid, hail) <= .1f && hail.y >= boid.y) { float hitDiff = Mathf.Abs(direction[i].y - hailList[j].fallingDirection.y); //direction[i] = Vector3.Normalize(new Vector3(direction[i].x, direction[i].z - hitDiff, direction[i].z)); status.boidObject.transform.position = Vector3.Normalize(new Vector3(direction[i].x, 0, direction[i].z));// When a piece of hail hits a boid then it falls to the same direction as the hail peice. } } status.boidObject.transform.up = direction[i]; status.boidObject.transform.position += direction[i] * Time.deltaTime * speed; statusList[i] = status; } }