游戏中角色对世界信息获取的方式:
- 轮询 (主动查找是否发生事件)
当多个角色需要进行轮询查找的时候,可以建立轮询中心来匹配信息,来节省多个角色重复轮询的消耗。
- 事件驱动 (等待事件发生触发)
Unity中的触发器类似于事件驱动方式
实现游戏感知(物体感知)前提:
- 需要拥有能被人感受的 触发器 ----Trigger类
- 保存触发器到事件管理器中
- 触发器位置
- 触发器触发范围
- 持续触发的触发器是否触发完毕(是否应该被移除)
- 检测是否被对应的感受器触发
- 触发与否需要执行的行为
- 更新持续触发的信息
- 需要拥有感知其他物体的 感知体(感知器)----Sensor类
- 保存感受器到事件管理器中
- 感受器类型
- 感受器向触发器发送消息/内容(例:这个攻击范围代表的攻击是什么效果的)
- 一般在游戏中具有多种感知方式,我们可以通过使用事件管理器来管理多种感知行为。 -----TriggerSystemManager类
- 所有物体上感知器的存储列表
- 所有物体上触发器的存储列表
- 需要被移除的感知器列表(例:释放出去的火焰魔法效果消失)
- 需要被移除的触发器列表(例:某目标身上的灼伤效果消失)
- 注册/添加触发器行为
- 注册/添加感受器行为
- 更新所有的触发器和感受器(例:将失效的效果添加进删除列表中进行删除)
- 继续执行剩下的触发器和感受器的行为(例:由更新可知,剩下的效果还没消失,继续执行效果)
Trigger类:
public class Trigger : MonoBehaviour {
//触发器所处位置
public Vector3 position;
//触发器范围(圆形触发器)半径
public int radius;
//添加到管理器中
public TriggerSystemManager manager;
//触发器是否去除
public bool toBeRemoved;
//判断触发器是否被对应感受器触发
public virtual bool isTouchingTrigger(Sensor sensor) { return false; }
//通过上面检测是否为对应感受器触发,是,执行行为,否,返回
public virtual void Try(Sensor sensor)
{
if (isTouchingTrigger(sensor))
sensor.Notify(this);
}
//更新触发器内部消息,例如触发器位置,时限等
public virtual void Updateme() { }
private void Awake()
{
//初始化触发器状态
toBeRemoved = false;
}
protected void Start () {
//存入管理中
manager = FindObjectOfType<TriggerSystemManager>();
}
}
Sensor类:
public class Sensor : MonoBehaviour {
protected TriggerSystemManager manager;
public enum SensorType
{
sight,
sound,
health
}
public SensorType sensorType;
private void Awake()
{
manager = FindObjectOfType<TriggerSystemManager>();
}
//负责通知消息
public virtual void Notify(Trigger trigger) { }
}
TriggerSystemManager类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TriggerSystemManager : MonoBehaviour {
//当前感知器列表
List<Sensor> currentSensors = new List<Sensor>();
//当前触发器列表
List<Trigger> currentTriggers = new List<Trigger>();
//记录当前时刻需要被移除的感知器,例如感知体死亡,需要移除感知器时
List<Sensor> sensorsToRemove;
//需要被移除的触发器,触发器已过期
List<Trigger> triggersToRemove;
// Use this for initialization
void Start () {
sensorsToRemove = new List<Sensor>();
triggersToRemove = new List<Trigger>();
}
private void UpdateTriggers()
{
//对于当前触发器列表中的触每个触发器
foreach(Trigger t in currentTriggers)
{
//如果t需要被移除
if (t.toBeRemoved)
{
//将t加入需要移除的触发器列表中(这是由于不能再foreach中直接移除,否则会报错)
triggersToRemove.Add(t);
}
else
{
t.Updateme();
}
}
foreach (Trigger t in triggersToRemove)
currentTriggers.Remove(t);
}
private void TryTriggers()
{
//对于当前感知器列表中的每个感知器s
foreach(Sensor s in currentSensors)
{
//如果s所对应的感知体还存在(没有因死亡而被销毁)
if(s.gameObject!=null)
{
foreach(Trigger t in currentTriggers)
{
//检查s是否在t的作用范围,并作出响应
t.Try(s);
}
}
else
{
sensorsToRemove.Add(s);
}
}
//删除需要删除的感知器
foreach(Sensor s in sensorsToRemove)
{
currentSensors.Remove(s);
}
}
// Update is called once per frame
void Update () {
//更新触发器内部状态
UpdateTriggers();
//迭代感知器和触发器,并做出行为
TryTriggers();
}
//用于注册触发器
public void RegisterTrigger(Trigger trigger)
{
print("registering trigger:" + trigger.name);
//将参数触发器t加入到当前触发器列表中
currentTriggers.Add(trigger);
}
//用于注册感知器
public void RegisterSensor(Sensor sensor)
{
print("registering sensor:" + sensor.name);
//将参数 感知器加入到当前感知器列表中
currentSensors.Add(sensor);
}
}
参考书籍:《unity3d人工智能编程精粹》 王洪源等著