大家好,这里是七七。
本文想要会用只看本文即可,若想要彻底理解加以从本系列的前些文章开始看
今天要为大家介绍的例子是Unity中的排队过洞AI,为了让人群有序地通过窄洞,而不是挤在一起,我们需要用到两种操控性为,分别是靠近和避开障碍
话不多说,先看效果
排队过窄洞
下面为大家介绍集体步骤
新建一个场景,建一个plane和两大块墙,这两块墙中间有一条窄路,为墙加上“obstacle”标签,并创建一个新layer,编号是9,名称也可以是“obstacle”,墙有“obstacle”标签,并在“obstacle”层
创建目标物体
创建空物体,来生成多个AI角色,为其添加脚本如下
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GenerateBotsForQueue : MonoBehaviour
{
public GameObject botPrefab;
public GameObject target;
public int botCount;
public float minX = 27f;
public float maxX = 30f;
public float minZ = -5.0f;
public float maxZ = 5.0f;
public float Yvalue = 1.026003f;
void Start()
{
Vector3 spawnPosition;
GameObject bot;
for (int i = 0; i < botCount; i++)
{
spawnPosition = new Vector3(Random.Range(minX, maxX), Yvalue, Random.Range(minZ, maxZ));//随机产生一个生成位置
bot = Instantiate(botPrefab, spawnPosition, Quaternion.identity) as GameObject;
bot.GetComponent().target = target;
}
}
}
创建一个小球作为AI角色,为其添加以下脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AILocomotion : Vehicle
{
private CharacterController controller; //AI�Ľ�ɫ������
private Rigidbody theRigidbody;
private Vector3 moveDistance;//AI��ɫÿ�ε��ƶ�����
void Start()
{
controller = GetComponent();
theRigidbody = GetComponent();
moveDistance = new Vector3(0, 0, 0);
base.Start();//���û����start��������������ij�ʼ��
}
//������ز�����FixedUpdate�и���
void FixedUpdate()
{
velocity += acceleration * Time.fixedDeltaTime;//�����ٶ�
if (velocity.sqrMagnitude > sqrMaxSpeed) //��������ٶ�
velocity = velocity.normalized * maxSpeed;
moveDistance = velocity * Time.fixedDeltaTime;
if (isPlanar)
{
velocity.y = 0;
moveDistance.y = 0;
}
if (controller != null)//����Ѿ�ΪAI��ɫ���ӽ�ɫ����������ô���ý�ɫ������ʹ���ƶ�
controller.SimpleMove(velocity);
//�����ɫ��û��ɫ��������ҲûRigidbody
//����Rigidbody����Ҫ�ɶ���ѧ�ķ�ʽ�������ƶ�
else if (theRigidbody == null || !theRigidbody.isKinematic)
transform.position += moveDistance;
else //��Rigidbody���ƽ�ɫ���˶�
theRigidbody.MovePosition(theRigidbody.position+moveDistance);
if(velocity.sqrMagnitude>0.00001)//���³�������ٶȴ���һ����ֵ��Ϊ�˷�ֹ������
{
Vector3 newForward = Vector3.Slerp(transform.forward, velocity, damping * Time.deltaTime);
if(isPlanar)
newForward.y = 0;
transform.forward = newForward;
}
//�������߶���
gameObject.GetComponent().Play("walk");
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SteeringForCollisionAvoidanceQueue : Steering
{
public bool isPlanar;
private Vector3 desiredVelocity;
private Vehicle m_vehicle;
private float maxSpeed;
private float maxForce;
public float avoidanceForce;
public float MAX_SEE_AHEAD ;
private GameObject[] allColliders;
private int layerid;
private LayerMask layerMask;
void Start()
{
m_vehicle = GetComponent();
maxSpeed = m_vehicle.maxSpeed;
isPlanar = m_vehicle.isPlanar;
maxForce = m_vehicle.maxForce;
if (avoidanceForce > maxForce)
avoidanceForce = maxForce;
allColliders = GameObject.FindGameObjectsWithTag("obstacle");
layerid = LayerMask.NameToLayer("vehicles");
layerMask = 1 << layerid;
}
public override Vector3 Force()
{
RaycastHit hit;
Vector3 force = new Vector3(0, 0, 0);
Vector3 velocity = m_vehicle.velocity;
Vector3 normalizedVelocity = velocity.normalized;
if (Physics.Raycast(transform.position, normalizedVelocity, out hit, MAX_SEE_AHEAD ,layerMask))
{
Vector3 ahead = transform.position + normalizedVelocity * MAX_SEE_AHEAD;
force = ahead - hit.collider.transform.position;
force *= avoidanceForce;
if (isPlanar)
force.y = 0;
}
return force;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SteeringForArrive : Steering
{
public bool isPlanar = true;
public float arrivalDistance = 0.3f;
public float characterRadius = 1.2f;
public float slowDownDistance;
public GameObject target;
private Vector3 desiredVelocity;//预期速度
private Vehicle m_vehicle;//获得被操控的AI角色
private float maxSpeed;
void Start()
{
m_vehicle = GetComponent();
maxSpeed = m_vehicle.maxSpeed;
isPlanar = m_vehicle.isPlanar;
}
public override Vector3 Force()
{
Vector3 toTarget = target.transform.position - transform.position;
Vector3 desiredVelocity;
Vector3 returnForce;
if (isPlanar)
toTarget.y = 0;
float distance = toTarget.magnitude;
if (distance > slowDownDistance)
{
desiredVelocity = toTarget.normalized * maxSpeed;
returnForce = desiredVelocity - m_vehicle.velocity;
}
else
{
desiredVelocity = toTarget - m_vehicle.velocity;
returnForce = desiredVelocity - m_vehicle.velocity;
}
return returnForce;
}
void OnDrawGizmos()
{
Gizmos.DrawWireSphere(target.transform.position, slowDownDistance);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Radar : MonoBehaviour
{
private Collider[] colliders;//碰撞体的组数
private float timer = 0;//计时器
public List neighbors;
public float checkInterval = 0.3f;//设置检测的时间间隔
public float detectRadius = 10f;//设置邻域半径
public LayerMask layersChecked;//设置检测哪一层的游戏对象
void Start()
{
neighbors = new List();
}
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())
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().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;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SteeringForQueue : Steering
{
public float MAX_QUEUE_AHEAD;
public float MAX_QUEUE_RADIUS;
private Collider[] colliders;
public LayerMask layersChecked;
private Vehicle m_vehicle;
private int layerid;
private LayerMask layerMask;
void Start()
{
m_vehicle = GetComponent();
layerid = LayerMask.NameToLayer("vehicles");
layerMask = 1 << layerid;
}
public override Vector3 Force()
{
Vector3 velocity = m_vehicle.velocity;
Vector3 normalizedVelocity = velocity.normalized;
Vector3 ahead = transform.position + normalizedVelocity * MAX_QUEUE_AHEAD;
colliders = Physics.OverlapSphere(ahead, MAX_QUEUE_RADIUS, layerMask);
if (colliders.Length > 0)
{
foreach (Collider c in colliders)
{
if ((c.gameObject != this.gameObject) && (c.gameObject.GetComponent().velocity.magnitude < velocity.magnitude))
{
m_vehicle.velocity *= 0.8f;
break;
}
}
}
return new Vector3(0, 0, 0);
}
}
摆放好场景,为脚本设置参数,为小球和墙体预设添加rigibody并设置好重量,就可以顺利运行了