AI角色对环境信息的感知

作者:追风剑情 发布于:2015-6-7 13:00 分类:Unity3d

     在游戏中,AI角色可以通过两种方式获得游戏世界的信息——轮询和事件驱动。

1、严格来说,感知系统并不算是游戏AI的一部分,但是,它的实现质量直接关系到AI系统的好坏,因此,对感知系统拥有良好的理解,将会非常有利于构建更强大的AI系统。

2、在游戏中,感知的开销可能会很大,因此,许多情况下,感知不能也不需要在每帧中进行。

3、视线查询(Line-of-Sight),在Unity3D中,Raycast调用可以实现视线查询,遗憾的是速度相对较慢,当场景中有大量物体时进行调用,或调用过于频繁时,开销很大。

触发器

   触发器这个概念是与事件驱动系统相对应的,触发器是AI角色能对其做出反应的任何“刺激源”,是它们触发了AI角色感兴趣的事件。例如,听觉或视觉刺激,例如枪声、爆炸、临近的敌人或尸体,也可能由游戏中的非AI角色产生。许多触发器具有这样的特性,即当游戏实体进入触发器所在的范围内时,这个触发器就会被触发。触发器范围一般是以触发器为中心的一个区域,在二维游戏中通常是圆形或矩形,在三维游戏中通常是球体、立方体或圆柱体的。

在游戏设计中,触发器是非常常见的,可以用它们创建各种事件和行为。

  • 当玩家射击时,一个声音触发器就被添加到场景中,这样,周围的AI角色会注意到枪声,决定是否逃避或赶来参与战斗。
  • 当玩家打倒一个护卫,护卫倒在地下时,相应的视觉触发器使得其他靠近的AI角色对尸体做出反应,决定避开这个区域或上前查看等。
  • 门的手柄可以是一个接触触发器,当玩家触碰到它时,门就会打开。
  • AI角色沿着昏暗的走廊走向某个地方,地面是对压力敏感的,这样随着AI角色的走动,触发器发出脚步声的回响。
  • 在雪地上行走的角色会留下脚印,当角色被击中而逃走时可能会留下血迹,这些都可以是视觉触发器,AI角色可以沿着脚印或血迹追逐角色。

如果只考虑模拟人的感觉,那么上面提到过的触发器似乎已经够了,味觉和嗅在游戏中很少使用,而且也可以模拟听觉感知的方式实现。但是游戏中还有一些其他种类的触发器。例如:

  • 时间相关的触发器。游戏角色可能会需要在6点回家吃饭,或者晚上7点后怪物出现的几率增大,又或者游戏进行一定时间后,发生某种剧情。另外,还有每隔一段时间就需要执行的触发,比如刷新怪物等。
  • 来自输入接口的触发器。例如,玩家按下Esc键,会触发过场动画等。
  • 当玩家开采资源或建造单位到达一个值后触发,发生某些事情。
  • 某一个单位发生事件后触发,比如,死亡、被攻击、升级、释放一项技能、购买物品等。
  • 单位进入或离开特定区域时触发。例如,进入水域时,要播放游泳的动画,发生高度突变时,播放攀爬动画等,或者进入突袭区时,做出某种反应等。
  • 指定单位的生命值在某个值以上或以下时触发,可以用于设定剧情。

由于每个AI角色的特点和能力不同,AI角色可以自己决定对哪些触发器做出反应,而忽略另一些触发器,例如,可能有些AI角色是聋的,无法对声音做出反应,或者听觉能力较弱,只能对很近的声音做出反应等。

常用感知类型的实现

游戏中最常用的感知类型是视觉和听觉。对于视觉,需要配对的视觉触发器和视觉感知器;为了实现听觉,需要配对的声音触发器和声音感知器。总的来说,游戏中有多个触发器以及多个感知器,可以通过一个管理中心——事件管理器,统一对它们进行管理。

另外,游戏中还常常需要模拟人的记忆。例如,如果玩家为了躲避AI角色射击,向右跨一步,躲到墙的后面,如果这时AI角色马上就忘了玩家,重新进入巡逻状态,那就太不真实了。为此,感知系统还要包括一个记忆感知器。

触觉感知

     触觉感知可以交给Unity3D的物理引擎来处理。通过为一个游戏物体加上碰撞体,并选中Inspector面板中的isTrigger属性,就可以把它标记为“触发器”。触发器不受物理引擎的控制,当触发器和另一个Collider发生碰撞时(其中至少有一个附加了Rigidbody组件),会发出3个触发信息,分别是OnTriggerEnter(当碰撞体Collider进入trigger触发器时调用),OnTriggerExit(当碰撞体Collider停止触发trigger时调用),OnTriggerStay(当碰撞体Collider接触trigger触发器时,这个函数将在每帧被调用)。在这3个函数中编写相应的代码,就可以实现触觉感知了。

因此,Unity3D已经为触觉感知提供了事件管理器,所以在事件感知器中,不再需要编写触觉相关的代码。

灵活应用触觉感知可以实现许多事件,比如显示信息、自动门的开启、生命值供给器、武器供给器等。


==========================感知系统框架代码=============================

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
/// <summary>
/// 这个类负责管理触发器的集合。它维护一个当前所有触发器的列表,当每个触发器被创建时,
/// 都会向这个管理器注册自身,加入到这个列表中,事件管理器负责更新和处理所有的触发器,
/// 并且当触发器已过期需要被移除时,从列表中删除它们。
/// 事件管理器还维护了一个感知器列表,每个感知器被创建时,向这个管理器注册,加入到感知器列表中。
/// </summary>
public class TriggerSystemManager : MonoBehaviour {

	//初始化当前感知器列表
	List<Sensor> currentSensors = new List<Sensor>();
	//初始化当前触发器列表
	List<Trigger> currentTriggers = new List<Trigger>();
	//记录当前时刻需要被移除的感知器,例如感知体死亡,需要移除感知器时;
	List<Sensor> sensorsToRemove;
	//记录当前时刻需要被移除的触发器,例如触发器已过期时;
	List<Trigger> triggersToRemove;
	
	void Start () {
		sensorsToRemove = new List<Sensor> ();
		triggersToRemove = new List<Trigger> ();
	}

	private void UpdateTriggers() {
		foreach (Trigger t in currentTriggers) {
			if(t.toBeRemoved){
				triggersToRemove.Add(t);
			}else{
				t.Updateme();
			}
		}

		foreach (Trigger t in triggersToRemove)
			currentTriggers.Remove (t);
	}

	private void TryTriggers() {
		foreach (Sensor s in currentSensors) {
			//如果s所对应的感知体还存在(没有因死亡而被销毁)
			if(s.gameObject != null){
				foreach (Trigger t in currentTriggers){
					t.Try(s);
				}
			}else{
				sensorsToRemove.Add(s);
			}
		}

		foreach (Sensor s in sensorsToRemove)
			currentSensors.Remove (s);
	}

	void Update () {
		//更新所有触发器内部状态
		UpdateTriggers ();
		//迭代所有感知器和触发器,做出相应的行为
		TryTriggers ();
	}

	//用于注册触发器
	public void RegisterTrigger(Trigger t) {
		currentTriggers.Add (t);
	}

	//用于注册感知器
	public void RegisterSensor(Sensor s) {
		currentSensors.Add (s);
	}
}

using UnityEngine;
using System.Collections;

/// <summary>
/// 这个类是所有触发器的基类,视觉触发器和听觉触发器都是它的派生类。
/// </summary>
public class Trigger : MonoBehaviour {

	//保存管理中心对象
	protected TriggerSystemManager manager;
	//触发器的位置
	protected Vector3 position;
	//触发器的半径
	public int radius;
	//当前触发器是否需要被移除
	public bool toBeRemoved;
	//这个方法检查作为参数的感知器s是否在触发器的作用范围内
	//(或当前触发器是否能真正被感知器sensor感觉到),如果是,那么采取相应的行为。
	//这个方法需要在派生类中实现。
	public virtual void Try( Sensor sensor ) { }
	//这个方法更新触发器的内部状态,例如,声音触发器的剩余有效时间等。
	public virtual void Updateme() { }
	//这个方法检查感知器sensor是否在触发器的作用范围内
	//或当前触发器是否能真正被感知器s感觉到,如果是,返回true,如果不是,返回false
	//它被Try()调用;需要在派生类中实现。
	protected virtual bool isTouchingTrigger( Sensor sensor) {
		return false;
	}

	void Awake () {
		//查找管理器并保存
		manager = FindObjectOfType<TriggerSystemManager> ();
	}

	protected void Start () {
		//这时不需要移除,置为false
		toBeRemoved = false;
	}

	void Update () {
	
	}
}

using UnityEngine;
using System.Collections;
/// <summary>
/// Sensor类是所有感知器的基类,视觉感知器和听觉感知器都是它的派生类。
/// 这个类中包含了对感知器类型的枚举定义和变量,还保存了事件管理器。
/// </summary>
public class Sensor : MonoBehaviour {

	protected TriggerSystemManager manager;
	public enum SensorType
	{
		sight,
		sound,
		health
	}

	public SensorType sensorType;

	void Awake () {
		//查找管理器并保存
		manager = FindObjectOfType<TriggerSystemManager> ();
	}
	
	void Start () {
	
	}

	void Update () {
	
	}

	public virtual void Notify (Trigger t) {

	}
}

========================== End ====================================

标签: Unity3d

Powered by emlog  蜀ICP备18021003号-1   sitemap

川公网安备 51019002001593号