Boids 可以理解为类似鸟群的东西,就是多个个体之间的相互作用。在游戏开发中经常会用到。例如 RTS 游戏,控制一个坦克战队,如何保持行进方向的一致性,以及坦克之间互相有一定有间隔,又不会间隔太大,这里就可以使用 Boids 相关的理论来实现。
在众多这方面的文章中,基本上会涉及到三个方面,跟随 、分离 、聚合 。跟随,就是说整个群体有一个行进的大方向。分离,则是个体与个体之间有一定的间隔,不至于发生碰撞。而聚合,就是个体不能离群体太远,不能脱离群体。
对于上面提到的三个方面,简单来说,就是一个力的叠加。
跟随 一个鸟群往哪个方向飞,可以假设有一只领头的鸟,其他的鸟跟随这只鸟的方向。知道领头的鸟的方向,知道自己当前的飞行方向,就可以计算出应该向中个方向施加一个力,可以使自己的方向,偏向于领头的鸟的方向。
分离 分离,是要保证个体之间不要离的太近,不要发生碰撞。先考虑两个物体的情况,假设要使物体 A 远离物体 B,只要从 B,向 A 施加一个推力,就可以将 A 推离 B。那如果 A 要同时和 B 与 C 保持距离呢?一样的,只需要从 B 和 C 分别向 A 施加一个推力,这两个的合力,就是 A 远离 B 和 C 的方向。同理,不管 A 要与多少个物体保持距离,只需要从每个物体出发,向 A 的方向施加一个力,就可以将 A 推开。
上面只考虑了 A 远离其他物体的情况,如果每一个物体都要与其他物体保持距离呢?一样的,只需要从每一个其他物体,向自己的方向施加一个力,这个合力,就是自己运动的方向。
聚合 为了保证个体不脱离群体,还需要一个聚合力。就是将个体自身,推向群体中心的力。
分离的力和聚合的力一定程度上抵消,从而达到个体之间即保持了距离,又保证了每一个体不脱离群体。
下面的代码是我的一个小游戏项目中的,其中只用到了分离和聚合。只要懂了原理,就可以根据具体的情况灵活变通,达到自己想要的效果即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 using UnityEngine;using System.Collections.Generic;using System.Collections;public class GamePlayFlock : MonoBehaviour { public static List <GamePlayFlock > flockList = new List<GamePlayFlock>(); public static void StartAllFlock ( ) { for (int i = 0 ; i < flockList.Count; ++i) { flockList[i].StartFlock(); } } public float avoidForce; public float randomForce; public float toCenterForce; public float randomFreq; public Vector3 centerPos; public float borderDist; public float avoidDist; public Vector2 borderRect; private Vector3 randomVelocity; private bool started = false ; public void InitFlock (Vector3 centerPos, float xBorderDist,float yBorderDist, float avoidDist ) { this .centerPos = centerPos; this .borderDist = xBorderDist; this .borderRect = new Vector2(xBorderDist, yBorderDist); this .avoidDist = avoidDist; flockList.Add(this ); } private void StartFlock ( ) { StartCoroutine(UpdateRandomVelocity()); started = true ; } private IEnumerator UpdateRandomVelocity ( ) { while (true ) { randomVelocity = Random.insideUnitSphere * randomForce; randomVelocity.z = 0 ; float wait = randomFreq + Random.Range(-randomFreq / 2.0f , randomFreq / 2.0f ); yield return new WaitForSeconds (wait ) ; } } private void Update ( ) { if (!started) { return ; } Vector3 avoidVelocity = Vector3.zero; float dist = 0 ; foreach (GamePlayFlock flock in flockList) { if (flock.transform != transform) { Vector3 otherPos = flock.transform.position; Vector3 dir = transform.position - otherPos; dist = dir.magnitude; if (dist < avoidDist) { float f = 1.0f - (dist / avoidDist); if (dist > 0 ) { avoidVelocity += (dir / dist) * f * avoidForce; } } } } Vector3 toCenterDir = centerPos - transform.position; Vector3 centerVelocity = Vector3.zero; float xDist = Mathf.Abs(toCenterDir.x); float yDist = Mathf.Abs(toCenterDir.y); if (xDist > borderRect.x) { float f = xDist / borderRect.x - 1.0f ; centerVelocity.x = (toCenterDir.x / xDist) * f * toCenterForce; } if (yDist > borderRect.y) { float f = yDist / borderRect.y - 1.0f ; centerVelocity.y = (toCenterDir.y / yDist) * f * toCenterForce; } Vector3 velocity = Vector3.zero; velocity += avoidVelocity; velocity += randomVelocity; velocity += centerVelocity; transform.position += velocity * Time.deltaTime; } }