听觉感知可以用一个球星区域来模拟。另一种方法是党声音被创建时,为它加上一个强度属性,随着传播距离的增加,声音强度会衰减,而每个AI角色也有自己的听觉阈值,如果声音小于这个阈值,AI角色就听不到这个声音。
这种具有特定生命周期的触发器,都可以从下面的TriggerlimitedLifetime类派生出来
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TriggerLimitedLifetime : Trigger {
//触发器的持续时间
protected int lifetime;
public override void Updateme()
{
//持续时间倒计时,如果剩余持续时间小于等于0,那么将它标记为需要移除;
if (--lifetime <= 0)
{
toBeRemoved = true;
}
}
// Use this for initialization
void Start () {
base.Start();
}
// Update is called once per frame
void Update () {
}
}
例如,当武器开火时,在开火的位置会创建一个SoundTrigger,它的半径可以设置为与武器的声音大小成正比。此时,在一定范围内,具有声音感知器的感知体就能够“听到”这个声音,并做出反应。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SoundTrigger : TriggerLimitedLifetime
{
//判断感知体是否能够听到声音触发器发出的声音,如果能,通知感知器
public override void Try(Sensor s)
{
if (isTouchingTrigger(s))
{
s.Notify(this);
}
}
//判断感知体是否能听到声音触发器发出的声音
protected override bool isTouchingTrigger(Sensor sensor)
{
GameObject g = sensor.gameObject;
//如果感知器能够感知声音
if (sensor.sensorType == Sensor.SensorType.sound)
{
//如果感知体与声音触发器的距离在声音触发器的作用范围内,返回true;
if ((Vector3.Distance(transform.position, g.transform.position) < radius)){
return true;
}
}
return false;
}
// Use this for initialization
void Start () {
//设置该触发器的持续时间;
lifetime = 3;
//调用基类的Start()函数
base.Start();
//将这个触发器加入到管理器的触发器列表中
manager.RegisterTrigger(this);
}
// Update is called once per frame
void Update () {
}
private void OnDrawGizmos()
{
Gizmos.color = Color.blue;
Gizmos.DrawWireSphere(transform.position, radius);
}
}
为具有听觉的AI角色加上声音感知器,这个感知器时Sensor的派生类,用来感知由声音触发器出发的那些声音信息。
AIController类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SoundSensor : Sensor {
//定义感知体的听觉范围,这里并没有实际使用
public float hearingDistance = 30.0f;
private AIController controller;
// Use this for initialization
void Start () {
controller = GetComponent<AIController>();
//设置感知器类型为声音感知器
sensorType = SensorType.sound;
//向管理器注册这个感知器
manager.RegisterSensor(this);
}
// Update is called once per frame
void Update () {
}
public override void Notify(Trigger t)
{
//当感知体能够听到触发器的声音时被调用,做出相应行为,这里打印信息,并走向声音的位置
print("I hear some sound at" + t.gameObject.transform.position + Time.time);
controller.moveToTarget(t.gameObject.transform.position);
}
}
触觉感知可以交给U3D物理引擎来处理。
记忆感知:
为了让角色具有记忆,实现一个SenseMemory类,具有一个记忆列表,列表中保存了最近感知到的对象、感知类型、最后感知到该对象的时间以及还能在记忆中保留的时间。当一段时间没有感知到这个对象,这个时间超出了记忆时长时,就会将这个对象从记忆列表中删除。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SenseMemory : MonoBehaviour {
//已经在列表中?
private bool alreadyInList = false;
//记忆留存时间
public float memoryTime = 4.0f;
//记忆列表
public List<MemoryItem> memoryList = new List<MemoryItem>();
//此时需要从记忆列表中删除的项
private List<MemoryItem> removeList = new List<MemoryItem>();
//在记忆列表中寻找玩家信息
public bool FindInList()
{
foreach (MemoryItem mi in memoryList)
if (mi.g.tag == "Player")
return true;
return false;
}
//项记忆列表中添加一个项
public void AddToList(GameObject g,float type)
{
alreadyInList = false;
//如果该项已经在列表中,那么更新最后感知时间等信息
foreach(MemoryItem mi in memoryList)
{
if (g == mi.g)
{
alreadyInList = true;
mi.lastMemoryTime = Time.time;
mi.memoryTimeLeft = memoryTime;
if (type > mi.sensorType)
mi.sensorType = type;
break;
}
}
//如果不在列表中,新建项并加入列表
if (!alreadyInList)
{
MemoryItem newItem = new MemoryItem(g, Time.time, memoryTime, type);
memoryList.Add(newItem);
}
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
removeList.Clear();
//遍历所有项,找到那些超时需要“忘记”的项,删除;
foreach(MemoryItem mi in memoryList)
{
mi.memoryTimeLeft -= Time.deltaTime;
if (mi.memoryTimeLeft < 0)
{
removeList.Add(mi);
}
else
{
//对没被删除的项,画出一条线,表示仍然在记忆中
if (mi.g != null)
Debug.DrawLine(transform.position, mi.g.transform.position, Color.blue);
}
}
foreach(MemoryItem m in removeList)
{
memoryList.Remove(m);
}
}
public class MemoryItem
{
//感知到的游戏对象
public GameObject g;
//最近的感知时间
public float lastMemoryTime;
//还能留存在记忆中的时间
public float memoryTimeLeft;
//通过哪种方式感知到的该对象,视觉为1,听觉为0.66
public float sensorType;
public MemoryItem(GameObject objectToAdd, float time, float timeLeft, float type)
{
g = objectToAdd;
lastMemoryTime = time;
memoryTimeLeft = timeLeft;
sensorType = type;
}
}
}