实现群组行为需要三个力:分离力,队列力,聚集力
资料:http://www.red3d.com/cwr/boids/
根据牛顿第二定律可算出加速度: A(加速度)=F(力)/M(质量)这里的力就是前面三个力的合力.
下面我们用代码实现
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CrowAI : MonoBehaviour
{
public float speed = 3;
public Vector3 velocity = Vector3.forward;//当前速度
private Vector3 startVelocity;
public Transform target;
public Vector3 sumForce = Vector3.zero;//和力
public float m = 1;//质量
public float separationDistance = 3;
public List<GameObject> separationNeighbors = new List<GameObject>();//在separationDistance半径内的所有物体
public float separationWeight = 1;
public Vector3 separationForce = Vector3.zero;//分离的力
public float alignmentDistance = 6;
public Vector3 alignmentForce = Vector3.zero;//队列的力
public float alignmentWeight = 1;
public List<GameObject> alignmentNeighbors = new List<GameObject>();
public Vector3 cohesionForce = Vector3.zero;//聚集的力
public float cohesionWeight = 1;
public float checkInterval = 0.2f;//多久计算一次合力
public float animRandowTime = 2f;
private Animation ani;
private void Start()
{
target = GameObject.Find("Target").transform;
startVelocity = velocity;
InvokeRepeating("CalcForce", 0, checkInterval);
ani = GetComponentInChildren<Animation>();
Invoke("playAnim", Random.Range(0, animRandowTime));
}
void playAnim()
{
ani.Play();
}
//计算合力
void CalcForce()
{
sumForce = Vector3.zero;
separationForce = Vector3.zero;//分离的力
alignmentForce = Vector3.zero;//队列的力
cohesionForce = Vector3.zero;//聚集的力
separationNeighbors.Clear();
//返回一个Collider数组,以transform.position为中心以separationDistance为半径发射一个球体, 其中所有碰撞到球体都包含在这个数组中(包含它自己)。
Collider[] colliders = Physics.OverlapSphere(transform.position, separationDistance);
foreach (Collider c in colliders)
{
if (c != null && c.gameObject != this.gameObject)
{
separationNeighbors.Add(c.gameObject);
}
}
//计算分离的力
foreach (GameObject neighbor in separationNeighbors)
{
Vector3 dir = transform.position - neighbor.transform.position;
separationForce += dir.normalized.normalized/dir.magnitude;
}
if (separationNeighbors.Count > 0)
{
separationForce *= separationWeight;
sumForce += separationForce;
}
//计算队列的力
alignmentNeighbors.Clear();
colliders = Physics.OverlapSphere(transform.position, alignmentDistance);
foreach (Collider c in colliders)
{
if (c != null && c.gameObject != this.gameObject)
{
alignmentNeighbors.Add(c.gameObject);
}
}
Vector3 avgDir = Vector3.zero;
foreach (GameObject n in alignmentNeighbors)
{
avgDir += n.transform.forward;
}
if (alignmentNeighbors.Count > 0)
{
avgDir /= alignmentNeighbors.Count;
alignmentForce = avgDir - transform.forward;
alignmentForce *= alignmentWeight;
sumForce += alignmentForce;
}
//聚集的力
if ( alignmentNeighbors.Count > 0)
{
Vector3 center = Vector3.zero;
foreach (GameObject n in alignmentNeighbors)
{
center += n.transform.position;
}
center /= alignmentNeighbors.Count;
Vector3 dirToCenter = center - transform.position;
cohesionForce += dirToCenter;
cohesionForce *= cohesionWeight;
sumForce += cohesionForce;
}
//保持恒定飞行速度的力
Vector3 engineForce = velocity.normalized * startVelocity.magnitude;
sumForce += engineForce * 0.1f;
Vector3 targetDir = target.position - transform.position;
sumForce += (targetDir.normalized - transform.forward) * speed;
}
private void Update()
{ //A(加速度)=F(力)/M(质量)
Vector3 a = sumForce / m;
velocity += a * Time.deltaTime;
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(velocity), Time.deltaTime * 3);
transform.Translate(transform.forward * Time.deltaTime * velocity.magnitude, Space.World);
}
}
这样鸟群飞起来比较凌乱,就有了AI鸟群的感觉.
视觉感知
public class Soldier : MonoBehaviour {
public float ViewDistance = 5;
public float ViewAngle=120;
private Transform PlyerTrans;
void Start () {
PlyerTrans = GameObject.Find("Player").transform;
}
void Update () {
if (Vector3.Distance(transform.position, PlyerTrans.position) <= ViewDistance)
{
//取得方向
Vector3 playerDir = PlyerTrans.position - transform.position;
//取得两个向量夹角
float angle=Vector3.Angle(playerDir,transform.forward);
if (angle <= ViewAngle/2)
{
Debug.Log("在视野范围内");
}
}
}
}