基本操作行为
“操作行为”是指操作控制角色,让他们能以模拟真实的方式在游戏世界中移动,它的工作方式是通过产生一定大小和方向的操控力,使角色以某种方式移动。
“操控行为”包括一组基本“行为”。
- 对于单独的AI角色,基本的操控行为包括:
基本行为中的每一个行为,都产生相应的操控力,将这些操控力以一定的方式组合起来,就能得到更复杂的“行为”,从而实现更高级的目标。
- 对于组成小队或群体的多个AI角色,包括基本的组行为如下:
无论整个群体中有多少个体,对于每个个体,计算的复杂度都是有限的,通过这种简单的计算,就可以产生逼真的效果,采用这项技术,两个相似的鸟群,即便是飞过相同的路线,它们的行为也是不同的。
Unity3D操控行为编程的主要基类
编程中主要涉及三个主要基类分别是:
- Vehicle类 主要作用:将AI角色抽象成一个质点,方便进行物理运算。
- Steering类 主要作用:各种操控行为的基类,包含操控行为共有的变量和方法,由此衍生出各种行为的派生类。
- LocomotionController类 主要作用:用于挂载在物体上,控制AI角色移动。
Vehicle类
首先对AI角色做出抽象。它包括很宽泛能自主移动的任何AI角色。
在AI架构模型中,操控AI角色的基类Vehicle吧操作的对象抽象为一个质点,它包含位置(position)、质量(mass)、速度(speed)等信息,而速度随着所施加的力的变化而变化,由于速度意味着是实际物理实体,施加在其身上的力和能达到的速度都是有限制的,因此还需要最大力、和最高速度两个信息,除此之外还有包含一个当前力的朝向信息。
using UnityEngine;
namespace AI.Steering
{
/// <summary>
/// 运动体类
/// </summary>
public class Vehicle:MonoBehaviour
{
/// <summary>
/// 当前操控力:向量
/// </summary>
[HideInInspector]
public Vector3 currentForce;
/// <summary>
/// 最终的合力
/// </summary>
[HideInInspector]
public Vector3 finalForce;
/// <summary>
/// 当前角色所具有的操控行为类数组
/// </summary>
[HideInInspector]
public Steering[] steerings;
/// <summary>
/// 最大速度
/// </summary>
public float maxSpeed = 10;
/// <summary>
/// 转向速度
/// </summary>
public float rotationSpeed = 5;
/// <summary>
/// 质量
/// </summary>
public float mass = 1;
/// <summary>
/// 合力上限
/// </summary>
public float maxForce = 100;
/// <summary>
/// 操控力的计算间隔时间,为了达到更高的帧率,操控力不需要每帧更新
/// </summary>
public float computerInternal = 0.2f;
/// <summary>
/// 是否在二维平面上,如果是计算GameObject的距离时,忽略y值的不同
/// </summary>
public bool isPlane = false;
//初始化 得到当前这个AI角色身上所有属于操控行为类的列表
void Start()
{
steerings = GetComponents<Steering>();
}
/// <summary>
/// 计算出实际操控力:把当前所有影响对象力进行计算 求合力
/// 同时考虑质量等问题 返回最终的实际操控力
/// </summary>
public void ComputeFinalForce()
{
//每次计算最终合力 让合力归零
finalForce = Vector3.zero;
//1 求当前对象的实际操控力 也就是所有对当前对象产生影响的力 计算合力
for(int i = 0; i < steerings.Length; i++)
{
if (steerings[i].enabled)
{
finalForce += steerings[i].GetForce();
}
}
if (finalForce == Vector3.zero)
currentForce = Vector3.zero;
//2 是否是平面(忽略y轴影响)
if (isPlane)
finalForce.y = 0;
//把向量限制在一个特定的长度,不超过合力上限
finalForce = Vector3.ClampMagnitude(finalForce, maxForce);
//考虑质量最最终合力的影响 合力=合力/质量
finalForce /= mass;
}
//启用方法
private void OnEnable()
{
InvokeRepeating("ComputeFinalForce", 0, computerInternal);
}
//结束时取消调用函数
private void OnDisable()
{
CancelInvoke("ComputeFinalForce");
}
}
}
Steering类
所有操控类的基类,包含操控类行为共有的变量和方法,操控AI角色的寻找,逃跑…行为都可由此派生。这样我们就可以在Unity3D中的C#脚本中方便的使用上述派生类来编程。
using UnityEngine;
namespace AI.Steering
{
/// <summary>
/// 操控类:各种自动运动的 【共性】
/// </summary>
abstract public class Steering:MonoBehaviour
{
/// <summary>
/// 期望操控力
/// </summary>
[HideInInspector]
public Vector3 expectForce;
/// <summary>
/// 当前的运动对象(抽象为质点)
/// </summary>
[HideInInspector]
public Vehicle vehicle;
/// <summary>
/// 目标
/// </summary>
/// 通过监视【属性】窗口赋值
/// 也可以通过代码赋值
public Transform target;
/// <summary>
/// 速度
/// </summary>
public float speed = 5;
/// <summary>
/// 表示每个操控力的权重
/// </summary>
public int weight = 1;
/// <summary>
/// 计算各个操控行为对当前物体的影响 实际操控力
/// </summary>
abstract public Vector3 GetForce();
//初始化
protected void Start()
{
vehicle = GetComponent<Vehicle>();
if(speed == 0) speed = vehicle.maxSpeed;
}
}
}
LocomotionController类
LocomotionController类是Vehicle类的派生类,它能真正的控制AI角色的移动,包括计算每次移动距离,播放动画等。
using UnityEngine;
namespace AI.Steering
{
//实际操控运动
public class LocomotionController : Vehicle
{
//方法需要反复调用 需要持续判断当前运动
void Update()
{
Rotation();
Movement();
PlayAnimation();
}
/// <summary>
/// 转向:当前的操控方向
/// </summary>
public void Rotation()
{
if (currentForce != Vector3.zero)
{
//获得当前操控力的方向的值
var dir = Quaternion.LookRotation(currentForce);
//(如果不希望角色y轴旋转)固定y轴
//var dir = Quaternion.LookRotation(new Vector3(currentForce.x,0,currentForce.z));
//得到插值 圆滑的旋转
transform.rotation = Quaternion.Lerp(transform.rotation, dir,rotationSpeed * Time.deltaTime);
}
}
/// <summary>
/// 移动
/// </summary>
public void Movement()
{
//1 计算出当前操控方向和速度
currentForce += finalForce * Time.deltaTime;
currentForce = Vector3.ClampMagnitude(currentForce, maxSpeed);
//2 移动
transform.position += currentForce * Time.deltaTime;
//不希望角色起飞(固定y轴)
//transform.position += new Vector3((currentForce * Time.deltaTime).x,0, (currentForce * Time.deltaTime).z);
}
/// <summary>
/// 播放动画
/// </summary>
public void PlayAnimation(){ }
}
}