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已经为触觉感知提供了事件管理器,所以在事件感知器中,不再需要编写触觉相关的代码。

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


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

  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. /// <summary>
  5. /// 这个类负责管理触发器的集合。它维护一个当前所有触发器的列表,当每个触发器被创建时,
  6. /// 都会向这个管理器注册自身,加入到这个列表中,事件管理器负责更新和处理所有的触发器,
  7. /// 并且当触发器已过期需要被移除时,从列表中删除它们。
  8. /// 事件管理器还维护了一个感知器列表,每个感知器被创建时,向这个管理器注册,加入到感知器列表中。
  9. /// </summary>
  10. public class TriggerSystemManager : MonoBehaviour {
  11.  
  12. //初始化当前感知器列表
  13. List<Sensor> currentSensors = new List<Sensor>();
  14. //初始化当前触发器列表
  15. List<Trigger> currentTriggers = new List<Trigger>();
  16. //记录当前时刻需要被移除的感知器,例如感知体死亡,需要移除感知器时;
  17. List<Sensor> sensorsToRemove;
  18. //记录当前时刻需要被移除的触发器,例如触发器已过期时;
  19. List<Trigger> triggersToRemove;
  20. void Start () {
  21. sensorsToRemove = new List<Sensor> ();
  22. triggersToRemove = new List<Trigger> ();
  23. }
  24.  
  25. private void UpdateTriggers() {
  26. foreach (Trigger t in currentTriggers) {
  27. if(t.toBeRemoved){
  28. triggersToRemove.Add(t);
  29. }else{
  30. t.Updateme();
  31. }
  32. }
  33.  
  34. foreach (Trigger t in triggersToRemove)
  35. currentTriggers.Remove (t);
  36. }
  37.  
  38. private void TryTriggers() {
  39. foreach (Sensor s in currentSensors) {
  40. //如果s所对应的感知体还存在(没有因死亡而被销毁)
  41. if(s.gameObject != null){
  42. foreach (Trigger t in currentTriggers){
  43. t.Try(s);
  44. }
  45. }else{
  46. sensorsToRemove.Add(s);
  47. }
  48. }
  49.  
  50. foreach (Sensor s in sensorsToRemove)
  51. currentSensors.Remove (s);
  52. }
  53.  
  54. void Update () {
  55. //更新所有触发器内部状态
  56. UpdateTriggers ();
  57. //迭代所有感知器和触发器,做出相应的行为
  58. TryTriggers ();
  59. }
  60.  
  61. //用于注册触发器
  62. public void RegisterTrigger(Trigger t) {
  63. currentTriggers.Add (t);
  64. }
  65.  
  66. //用于注册感知器
  67. public void RegisterSensor(Sensor s) {
  68. currentSensors.Add (s);
  69. }
  70. }
  1.  
  1. using UnityEngine;
  2. using System.Collections;
  3.  
  4. /// <summary>
  5. /// 这个类是所有触发器的基类,视觉触发器和听觉触发器都是它的派生类。
  6. /// </summary>
  7. public class Trigger : MonoBehaviour {
  8.  
  9. //保存管理中心对象
  10. protected TriggerSystemManager manager;
  11. //触发器的位置
  12. protected Vector3 position;
  13. //触发器的半径
  14. public int radius;
  15. //当前触发器是否需要被移除
  16. public bool toBeRemoved;
  17. //这个方法检查作为参数的感知器s是否在触发器的作用范围内
  18. //(或当前触发器是否能真正被感知器sensor感觉到),如果是,那么采取相应的行为。
  19. //这个方法需要在派生类中实现。
  20. public virtual void Try( Sensor sensor ) { }
  21. //这个方法更新触发器的内部状态,例如,声音触发器的剩余有效时间等。
  22. public virtual void Updateme() { }
  23. //这个方法检查感知器sensor是否在触发器的作用范围内
  24. //或当前触发器是否能真正被感知器s感觉到,如果是,返回true,如果不是,返回false
  25. //它被Try()调用;需要在派生类中实现。
  26. protected virtual bool isTouchingTrigger( Sensor sensor) {
  27. return false;
  28. }
  29.  
  30. void Awake () {
  31. //查找管理器并保存
  32. manager = FindObjectOfType<TriggerSystemManager> ();
  33. }
  34.  
  35. protected void Start () {
  36. //这时不需要移除,置为false
  37. toBeRemoved = false;
  38. }
  39.  
  40. void Update () {
  41. }
  42. }
  1.  
  1. using UnityEngine;
  2. using System.Collections;
  3. /// <summary>
  4. /// Sensor类是所有感知器的基类,视觉感知器和听觉感知器都是它的派生类。
  5. /// 这个类中包含了对感知器类型的枚举定义和变量,还保存了事件管理器。
  6. /// </summary>
  7. public class Sensor : MonoBehaviour {
  8.  
  9. protected TriggerSystemManager manager;
  10. public enum SensorType
  11. {
  12. sight,
  13. sound,
  14. health
  15. }
  16.  
  17. public SensorType sensorType;
  18.  
  19. void Awake () {
  20. //查找管理器并保存
  21. manager = FindObjectOfType<TriggerSystemManager> ();
  22. }
  23. void Start () {
  24. }
  25.  
  26. void Update () {
  27. }
  28.  
  29. public virtual void Notify (Trigger t) {
  30.  
  31. }
  32. }

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

标签: Unity3d

Powered by emlog  蜀ICP备18021003号-1   sitemap

川公网安备 51019002001593号