本文内容位于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);
}
}