🎁
🏆 导航菜单
🎪 扩展内容
🎯 沙盘引擎文档
🕹️ 文章及书籍&教程
🎖️ 外部的链接
🎁
🏆 导航菜单
🎪 扩展内容
🎯 沙盘引擎文档
🕹️ 文章及书籍&教程
🎖️ 外部的链接
这是本文档旧的修订版!
角色作为游戏世界内的关键交互,AI功能(移动、跟随、寻路等)是必不可少的,在引擎中默认提供了几种方式使用。
沙盘引擎默认只提供AI操控功能,不提供具体的AI逻辑实现,开发者应根据自身项目需求编写AI逻辑。
这里仅提供编写AI时可能用到的关键代码指南,具体API使用方法请参考《脚本开发及API》及下方【AI逻辑关系】解释。
character.Team
指定角色Team阵营(用于分辨敌我阵营)character.AIDistance
指定逻辑检测距离character.AITarget
指定一个Entity
类型的对象(角色、载具、模型对象等),会自动跟随目标移动character.AIState
指定一个AI状态(和平、被动、中立、主动)character.AIMove
() 让角色移动到指定坐标位置Distance()
测量两个坐标的距离Rayline()
射线检测两个坐标能否穿透
在实际开发过程中,大多数AI逻辑并非想象中那么复杂,更多只需要计时器事件+循环检测逻辑
就可以实现多数需求。
比如可以新建一个计时器,每1秒循环执行一次(根据需求自行调整间隔时间,时间间隔越小,理论上越灵敏),在计时器事件内编写循环遍历代码,根据需求+上方AI关键代码组合,就可以实现许多AI逻辑。
//示例代码仅供逻辑参考,不具有实时开箱即用性 Timer.Create(() => { for(let i=0;i<Character.Count;i++) { let chara = Character.Find(i); if(chara.Health > 20) { chara.AIState = 2; //血量正常,范围内有敌人会攻击 }else{ chara.AIState = 1; //血量较低,改为被动,始终不会攻击 } //遍历所有玩家,寻找玩家主动移动 //这里写法只是举例,实际上会有【永远只跟随第一个找到的玩家】问题,这里可自行使用排序,优先攻击最近\血量最低的玩家筛选 for(let j=0;j<Player.Count;j++) { let plr = Player.Find(j); if(plr.Entity != null && plr.Entity.Health > 0) { chara.Target = plr; //或使用chara.MoveTo(plr.Pos); break; } } } }, 1, 0); //每秒循环1次,循环无数次(0表示无数次)
沙盘引擎采用了【网络+单机】互相兼容的同步逻辑,所以无论在多数情况下仍然有网络同步流(Stream)的概念。
网络同步流,可以理解为以每个玩家为中心点,实时更新有效的网络同步范围(比如32x32),只有在这个合法距离内部分操作才得以生效,否则视为“离开此玩家控制展示范围”,部分操作不予生效。
例如:在《我的世界》中也存在类似的概念,只有玩家在指定(默认10)范围内,附近的僵尸、作物、生物才会进行生长和移动及计算,超过此距离将不再进行同步。
考虑到此概念,开发者在编写、使用AI系统时,应该根据实际应用情况注意同步流范围的问题。
比如有一个特大的城市地图,让NPC-A
从城南寻路到城北,这将被弹出警告并忽略,因为这明显超出了玩家流、CPU合理计算范围,正确的做法应该是优先引用离玩家更近的NPC,或者使用强制位移同步参数(详情见character.MoveTo()
方法解释)。
强制位移:实际上是将Character强制移动到能够AI寻路范围的最边缘位置,然后开始正常寻路移动。
沙盘引擎为AI逻辑提供了一些通用的控制代码,无论是简单还是复杂的AI整体逻辑,都应该通过这些基础AI代码来实现。
AI代码根据情况不同,应该使用不同的API进行调用,且不同代码执行后可能会有一些关系影响,下方将做一些示例规范及解释。
编写AI针对特定对象时,应该优先使用AITarget
来绑定对象(例如受到玩家攻击逃跑、主动攻击玩家),这将会更自动便捷的根据AIState
逃跑\追随。
如果AITarget
功能不能满足需求,或者有更高级的自定义需求,可以在使用AITarget
的同时,搭配使用character.AIMove()
功能。
值得注意的是,无论是AITarget
还是character.AIMove()
功能,在使用任何一个时,都会自动重置清空另外一个记录(绑定AITarget
后将不再执行AIMove
,反之亦然,使用AIMove
时将自动清空AITarget
属性)。
除了使用AITarget\AIMove
等方式移动外,AIState
也是十分关键的AI逻辑代码,这将直接控制当前Character的“性格和想法”。
ID | 说明 | 机制 |
---|---|---|
0 | 和平(忽略Team) | 无任何行为,默认正常寻路移动,不会主动攻击或逃脱 |
1 | 被动(忽略Team) | 默认正常寻路移动,不会主动攻击,在无AITarget 时不会主动逃脱如果任何敌对实体攻击了自身,将自动切换为 AITarget=攻击者 如有 AITarget 则将尝试反向远离此目标,直到大于AIDistance 距离(但不会主动重置AITarget ) |
2 | 中立 | 默认正常寻路移动,如果任何敌对实体攻击了自身,将自动切换为AIState=3 且AITarget=攻击者 ,否则不会主动攻击或逃脱 |
3 | 主动 | 如果AITarget 不为空,将主动跟随攻击如果 AITarget 为空,则主动攻击任何在AIDistance 范围内的实体 |
注意:以上有关【中立、主动】状态下,均遵循对方.Team != 自身.Team
前提条件,如果目标Team相同则会忽略。
如确实有关于【中立】情况下Team相同攻击也触发逻辑的需求,可以在
Character相关攻击事件
进行条件判断并手动修改Team和AIState
。或者,可以先更换
character.Team
,然后再触发AI逻辑)。
由于各类游戏玩法的需求不同,沙盘引擎默认只提供基础的AI操控逻辑代码。
实际上编写一个生动且智能化的AI并非很容易的事情,尽管【计时器循环】的入口事件起点很简单,但具体怎样安排循环周期的代码?这个需要根据项目玩法提前设计完善。
例如AIState1
且AITarget玩家角色
的情况下,这个AI角色理论上会一直尝试逃离玩家(哪怕只是之前运行过一次),这就可能需要开发者在合适的时机手动代码重置下,从而避免可能不希望看到的“一直逃离玩家角色”。
例如:制作一款打猎游戏,一个AI猪猪(
AIState=1
),当被玩家角色攻击一次后,就会自动绑定AITarget=玩家角色
,这时因为是AIState=1
所以便会开始反向逃离玩家角色,这时玩家可能没有打算追下去,当AI猪猪逃离到AIDistance
最大距离后,理论上就不应该再逃跑下去了(因为可能已经离玩家很远了,没有浪费计算的意义),这时理应通过Distance()
等代码判断一下两者距离,如果满足安全距离条件,就应该手动代码重置AITarget=null
,而不是让AI猪猪一直逃跑下去。同时,这也是符合世界正常逻辑的,AI猪猪逃跑到一定安全距离后就应该停下来,等待玩家角色再次靠近危险距离后,再自动默认重复AI逻辑即可。
有关主动状态(AIState=3)也是同理,当AI杀手追击玩家或角色时,如果拉开了一定安全距离后就不应该再一直追下去了,而应该在合适的实际手动重置杀手角色.AITarget=null
。