本文内容位于AI模型中的运动层。变成种主要涉及到Vehicle、AILocomotion和Steering类,它们是实现操控行为的基础
一、将AI对象抽象成一个质点——Vehicle类
Vehicle包括position、mass、velocity等信息,而速度随着所施加力的变化而变化。由于是物理实体,因此还要加上max_force和max_speed两个信息
Velocity的位置的计算方法如下:
- 确定每一帧的操控力(不得超过max_force)
- 除以mass,得到加速度
- 将加速度与原速度相加(不得超过max_speed)
- 根据速度和这一帧的时间,得到位置的变化
- 与原位置相加,得到新位置
在这个模型中,来自控制行为部分的控制信号只是一个向量——steering_force。当然也可以用更复杂的模型,本文不介绍。
在下面这个实现中,Vehicle是一个基类,其他所有可以动的游戏AI角色都由它派生而来。该实现封装了一些数据,用来描述被看作质点的"交通工具"。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Vehicle : MonoBehaviour { private Steering[] steerings;//这个AI包含的操控行为列表 public float maxSpeed = 10; public float maxForce = 100; //能施加到这个角色身上的力的最大值 public float sqrMaxSpeed; public float mass = 1; public Vector3 velocity; public float damping = 0.9f; //转向时的速度 public float computeInterval = 0.2f; //操控力的计算时间间隔,为了达到更高的帧率,操控力不需要每帧更新 public bool isPlanar = true; //是否在二维平面上,如果是,计算两个物体距离时,忽略y值的不同 private Vector3 steeringForce; protected Vector3 acceleration; private float timer; //计时器 protected void Start() { steeringForce = new Vector3(0, 0, 0); sqrMaxSpeed=maxSpeed*maxSpeed; timer = 0; steerings = GetComponents<Steering>(); } // Update is called once per frame void Update() { timer += Time.deltaTime; steeringForce = new Vector3(0, 0, 0); if(timer>computeInterval) { //将操控性为列表中的所有行为对应的操控力进行带权重的求和 foreach(Steering s in steerings) { if (s.enabled) steeringForce += s.Force() * s.weight; } steeringForce = Vector3.ClampMagnitude(steeringForce, maxForce); acceleration = steeringForce / mass; timer = 0; } } }
二、控制AI角色移动——AILocomotion
AILocomotion类是Vehicle的派生类,它能真正控制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函数,进行所需的初始化 } //物理相关操作在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.animation.Play("walk"); } }
三、各种操控性味的基类——Steering类
Steering类是所有操控行为的基类,包括操控行为共有的变量和方法,操控AI角色的寻找、逃跑、追逐、躲避、徘徊、分离、队列、聚集等都可由此派生。这样,我们就可以在C#脚本中方便地使用上述派生类来编程了。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Steering : MonoBehaviour { public float weight = 1; void Start() { } // Update is called once per frame void Update() { } public virtual Vector3 Force() { return new Vector3(0, 0, 0); } }