大家好,这里是七七。
本文想要会用只看本文即可,若想要彻底理解加以从本系列的前些文章开始看
今天要为大家介绍的例子是Unity中的排队过洞AI,为了让人群有序地通过窄洞,而不是挤在一起,我们需要用到两种操控性为,分别是靠近和避开障碍
话不多说,先看效果
排队过窄洞
下面为大家介绍集体步骤
一
新建一个场景,建一个plane和两大块墙,这两块墙中间有一条窄路,为墙加上“obstacle”标签,并创建一个新layer,编号是9,名称也可以是“obstacle”,墙有“obstacle”标签,并在“obstacle”层
二
创建目标物体
三
创建空物体,来生成多个AI角色,为其添加脚本如下
1. using System.Collections; 2. using System.Collections.Generic; 3. using UnityEngusing 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<SteeringForArrive>().target = target; } } } 5. public class GenerateBotsForQueue : MonoBehaviour 6. { 7. public GameObject botPrefab; 8. public GameObject target; 9. public int botCount; 10. public float minX = 27f; 11. public float maxX = 30f; 12. public float minZ = -5.0f; 13. public float maxZ = 5.0f; 14. public float Yvalue = 1.026003f; 15. void Start() 16. { 17. Vector3 spawnPosition; 18. GameObject bot; 19. for (int i = 0; i < botCount; i++) 20. { 21. spawnPosition = new Vector3(Random.Range(minX, maxX), Yvalue, Random.Range(minZ, maxZ));//随机产生一个生成位置 22. bot = Instantiate(botPrefab, spawnPosition, Quaternion.identity) as GameObject; 23. bot.GetComponent<SteeringForArrive>().target = target; 24. } 25. } 26. 27. 28. }
四
创建一个小球作为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<CharacterController>(); theRigidbody = GetComponent<Rigidbody>(); 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<Animation>().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<Vehicle>(); 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<Vehicle>(); 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<GameObject> neighbors; public float checkInterval = 0.3f;//设置检测的时间间隔 public float detectRadius = 10f;//设置邻域半径 public LayerMask 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; } }
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<Vehicle>(); 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<Vehicle>().velocity.magnitude < velocity.magnitude)) { m_vehicle.velocity *= 0.8f; break; } } } return new Vector3(0, 0, 0); } }
五
摆放好场景,为脚本设置参数,为小球和墙体预设添加rigibody并设置好重量,就可以顺利运行了