目录
组行为
组行为的复杂性来源于个体间的交互,并遵守一些简单的规则。
模仿群体行为需要下面几种操控行为
- 分离:避免个体在局部过于拥挤的操控力
- 队列:朝向附近同伴的平均朝向的操控力
- 聚集:向附近的同伴的平均位置移动的操控力
检测附近的AI角色
从上面的几种操控行为可以看出,每种操控行为都取决于附近的同伴。为了实现组行为,首先需要检测当前AI角色附近的其他AI角色,这要用一个雷达脚本来实现。
一般来说,角色的领域由一个距离和一个角度来定义。有时为了简化,用一个圆来定义。
1using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Radar : MonoBehaviour
{
private Collider[] colliders;//碰撞体的组数
private float timer = 0;//计时器
public List<GameObject> neighbors;
public float checkInterval = 0.3f;//设置检测的时间间隔
public float detectRadius = 10f;//设置邻域半径
public LayMask layersChecked;//设置检测哪一层的游戏对象
void Start()
{
neighbors = new List<GameObject>();
}
void Update()
{
timer += Time.deltaTime;
if(timer > checkInterval)
{
neighbors.Clear();
colliders = Physics.OverlapSphere(transform.position, detectRadius, layersChecked);//查找当前AI角色邻域内的所有碰撞体
for(int i = 0; i < colliders.Length; i++)//对于每个检测到的碰撞体,获取Vehicle组件,并且加入邻居列表钟
{
if (colliders[i].GetComponent<Vehicle>())
neighbors.Add(colliders[i].gameObject);
}
timer = 0;
}
}
}
分离
分离的目的是使角色与周围其他角色保持一定距离。实现时,为了计算分离行为所需的操控力,需要搜索指定领域内的其他邻居,然后对每个邻居计算与角色的距离向量,进行一系列处理得到操控力
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SteeringForSeparation : Steering
{
public float comforDistance = 1;//可接受的距离
public float multiplierInsideComfortDistance = 2;//当AI角色与邻居距离过近时的惩罚因子
public override Vector3 Force()
{
Vector3 steeringForce = new Vector3(0, 0, 0);
foreach(GameObject s in GetComponent<Radar>().neighbors)//遍历这个AI角色的邻居列表中的每个邻居
{
if ((s != null) && (s != this.gameObject))
{
Vector3 toNeighbor = transform.position - s.transform.position;//计算当前AI角色与邻居s之间的距离
float length=toNeighbor.magnitude;
steeringForce += toNeighbor.normalized / length;//计算这个邻居引起的操控力
if (length < comforDistance)
steeringForce *= multiplierInsideComfortDistance;
}
}
return steeringForce;
}
}
队列
队列的目的是保持AI角色的运动朝向与邻居一致
通过迭代所有邻居,可以求出AI角色平均朝向向量以及速度平均向量,得到想要的朝向,然后减去AI当前朝向,就可以得到队列操控力
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SteeringForAlignment : Steering
{
public override Vector3 Force()
{
Vector3 averageDirection = new Vector3(0, 0, 0);//当前AI角色的邻居平均朝向
int neighborCount = 0;
foreach(GameObject s in GetComponent<Radar>().neighbors)//遍历邻居
{
if ((s != null) && (s != this.gameObject))
{
averageDirection += s.transform.forward;
neighborCount++;
}
}
if(neighborCount > 0)
{
averageDirection/=(float)neighborCount;//求出平均朝向向量
averageDirection -= transform.forward;//求出操控向量
}
return averageDirection;
}
}
聚集
聚集的目的是使多个AI角色聚到一起。实现时,迭代所有邻居求出AI角色位置的平均值,然后利用靠近行为,将这个平均值作为目标位置。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SteeringForCohesion :Steering
{
private Vector3 desiredVelocity;
private Vehicle m_vehicle;
private float maxSpeed;
void Start()
{
m_vehicle = GetComponent<Vehicle>();
maxSpeed = m_vehicle.maxSpeed;
}
public override Vector3 Force()
{
Vector3 steeringForce = new Vector3(0, 0, 0);//操控向量
Vector3 centerOfMass = new Vector3(0, 0, 0);//AI角色的所有邻居的质心
int neighborCount = 0;
foreach (GameObject s in GetComponent<Radar>().neighbors)//遍历邻居
{
if ((s != null) && (s != this.gameObject))
{
centerOfMass += s.transform.position;
neighborCount++;
}
}
if (neighborCount > 0)
{
centerOfMass /= (float)neighborCount;//求出平均位置向量
desiredVelocity = (centerOfMass - transform.position).normalized * maxSpeed;//与其速度为邻居平均位置与当前位置之差
steeringForce = desiredVelocity - m_vehicle.velocity;
}
return steeringForce;
}
}