沙盘引擎 (SEngine)

创意诞生沙盘世界,不止如此想象!

用户工具

站点工具


侧边栏

scripting:extend:character_ai

这是本文档旧的修订版!


📚 CharacterAI(角色AI)

角色作为游戏世界内的关键交互,AI功能(移动、跟随、寻路等)是必不可少的,在引擎中默认提供了几种方式使用。

沙盘引擎默认只提供AI操控功能,不提供具体的AI逻辑实现,开发者应根据自身项目需求编写AI逻辑。

📒 关键代码

这里仅提供编写AI时可能用到的关键代码指南,具体API使用方法请参考《脚本开发及API》及下方【AI逻辑关系】解释。

  • character.Team 指定角色Team阵营(用于分辨敌我阵营)
  • character.AIDistance 指定逻辑检测距离
  • character.AITarget 读\写一个Entity类型的对象(Character\Vehicle\Model)
  • character.AIState 指定一个AI状态(和平、被动、中立、主动)
  • character.AIMove() 让角色移动到指定坐标位置
  • Distance() 测量两个坐标的距离
  • Rayline() 射线检测两个坐标能否穿透
  • ……

📒 编写逻辑示例

在实际开发过程中,大多数AI逻辑并非想象中那么复杂,更多只需要计时器事件+循环检测逻辑就可以实现多数需求。

比如可以新建一个计时器,每1秒循环执行一次(根据需求自行调整间隔时间,时间间隔越小,理论上越灵敏),在计时器事件内编写循环遍历代码,根据需求+上方AI关键代码组合,就可以实现许多AI逻辑。

//示例代码仅供逻辑参考,不保证实时开箱即用性
var npcs = [];
 
//Create NPCs
for(let i=0;i<50;i++)
{
    let veh = Character.Create(0, player.Entity.Pos, player.Entity.Angle);
    npcs.push(veh);
}
 
//Timer AIEvent
Timer.Create(() => {
    for(let i=0;i<npcs.length;i++)
    {
        let npc = npcs[i];
        if(chara.Health > 20)
        {
            npc.AIState = 2; //血量正常,范围内有敌人会攻击
        }else{
            npc.AIState = 1; //血量较低,改为被动,始终不会攻击
        }
 
        //遍历所有玩家,寻找玩家主动移动
        //这里写法只是举例,实际上会有【永远只跟随第一个找到的玩家】问题,这里可自行使用排序,优先攻击最近\血量最低的玩家筛选
        for(let j=0;j<Player.Count;j++)
        {
            let plr = Player.Find(j);
            if(plr.Entity != null && plr.Entity.Health > 0)
            {
                npc.AIMove(plr.Pos);
                break;
            }
        }
    }
}, 1, 0); //每1秒循环,循环无数次(0表示无数次)

📒 玩家同步流限制

沙盘引擎采用了【网络+单机】互相兼容的同步逻辑,所以无论在多数情况下仍然有网络同步流(Stream)的概念。

网络同步流,可以理解为以每个玩家为中心点,实时更新有效的网络同步范围(比如32x32),只有在这个合法距离内部分操作才得以生效,否则视为“离开此玩家控制展示范围”,部分操作不予生效。

例如:在《我的世界》中也存在类似的概念,只有玩家在指定(默认10)范围内,附近的僵尸、作物、生物才会进行生长和移动及计算,超过此距离将不再进行同步。

考虑到此概念,开发者在编写、使用AI系统时,应该根据实际应用情况注意同步流范围的问题。

比如有一个特大的城市地图,让NPC-A从城南寻路到城北,这将被弹出警告并忽略,因为这明显超出了玩家流、CPU合理计算范围,正确的做法应该是优先引用离玩家更近的NPC,或者使用强制位移同步参数(详情见character.MoveTo()方法解释)。

强制位移:实际上是将Character强制移动到能够AI寻路范围的最边缘位置,然后开始正常寻路移动。

📒 AI逻辑关系

沙盘引擎为AI逻辑提供了一些通用的控制代码,无论是简单还是复杂的AI整体逻辑,都应该通过这些基础AI代码来实现。

AI代码根据情况不同,应该使用不同的API进行调用,且不同代码执行后可能会有一些关系影响,下方将做一些示例规范及解释。

📘 常规方案及逻辑冲突

编写AI针对特定对象时,应该优先使用AITarget绑定对象方案(例如受到玩家攻击逃跑、主动攻击玩家),这将会更便捷的自动根据AIState逃跑\追随。

如果AITarget功能不能满足需求,或者有更高级的自定义需求,可以在使用AITarget的同时,搭配使用character.AIMove()功能。

值得注意的是,无论是AITarget还是character.AIMove()功能,在使用其中一个时,都会自动重置清空另外一个记录(绑定AITarget后将不再执行AIMove,反之亦然,使用AIMove时将自动清空AITarget属性)。

📘 AIState状态机制

除了使用AITarget\AIMove等方式移动外,AIState也是十分关键的AI逻辑代码,这将直接控制当前Character的“性格和想法”。

此状态机制需要AITarget\AIDistance的搭配使用,如只是使用AIMove()坐标的方式,则没有对应的功能效果。

ID 说明 机制
0 和平(忽略Team) 无任何行为,默认正常寻路移动,不会主动攻击或逃脱
1 被动(忽略Team) 默认正常寻路移动,不会主动攻击,在无AITarget时不会主动逃脱
如果任何敌对实体攻击了自身,将自动切换为AITarget=攻击者
如有AITarget则将尝试反向远离此目标,直到两者距离>=AIDistance
2 中立 默认正常寻路移动,如果任何敌对实体攻击了自身,将自动切换为AIState=3AITarget=攻击者,否则不会主动攻击或逃脱
3 主动 如果AITarget不为空,将主动跟随攻击
如果AITarget为空,则主动攻击任何在AIDistance范围内的实体

注意:以上有关【中立、主动】状态下,均遵循对方.Team != 自身.Team前提条件,如果目标Team相同则会忽略。

如确实有关于【中立】情况下Team相同攻击也触发逻辑的需求,可以在Character相关攻击事件进行条件判断并手动修改Team和AIState

或者,可以先更换character.Team,然后再触发AI逻辑)。

📘 AIDistance距离机制

在使用AITarget+AIState方式进行对象跟随时,AIDistance将在不同状态时起到不同作用。

AIDistance数值在不同AIState下代表不同的限制含义。

AIDistance默认值:5.0。

注意:使用AIMove()方式跟随时,大部分AIDistance功能将不具备效果,只有绑定AITarget才有意义。

AIState状态 机制作用
0(和平) 当两者距离<=AIDistance时,角色将始终保持安全距离,暂停跟随
1(被动) 当两者距离<=AIDistance * ai_character_escapedistance(GameRule) 时,角色将始终保持安全距离,反向逃离
此处的ai_character_escapedistance是一个游戏规则设定,详情参考游戏规则文档
2(中立) 当两者距离<=AIDistance时,角色将始终保持安全距离,暂停跟随
3(主动) 当两者距离<=AIDistance时,角色将视为追击距离,主动追击
超过追击距离时则站立在原地,主动攻击距离取决于手持武器的攻击距离

特别注意:引擎默认只提供相对基础可扩展的方式,类似AITarget绑定后不会自动解绑(除非调用AIMove()自动重置或对象销毁),开发者要根据项目自行管理AITarget周期,避免意料之外的情况。

📘 AITarget和AIMove的使用

AITarget适合针对某个特定目标(角色、载具、模型)时使用,同时搭配AIDistance+AIState可以自动实现追击\逃跑\中立等状态切换。

除此之外,对于更高级的需求或自定义AI逻辑,以及只想让NPC前往某个坐标而不需要其他功能,那么可以使用AIMove()函数来实现向坐标移动

📘 AIMoveState枚举

在编写AI跟随逻辑时,有时需要设定目标角色是如何前往的(自动、行走、奔跑),这需要开发者通过AIMoveState来指定预设,默认情况下是自动Auto

AIMoveState状态 说明
0(自动) 根据距离情况自动选择,当距离目标超过ai_character_movestate_autodistance(GameRule)时,将会变为奔跑状态
由于AIState=1被动的反向逃离特殊性,被动状态下将永远奔跑,除非手动设置AIMoveState=1
1(行走) 永远使用行走状态,无论距离多远
2(奔跑) 永远使用奔跑状态,无论距离多远

📒 其他补充

由于各类游戏玩法的需求不同,沙盘引擎默认只提供基础的AI操控逻辑代码。

实际上编写一个生动且智能化的AI并非很容易的事情,尽管【计时器循环】的入口事件起点很简单,但具体怎样安排循环周期的代码?这个需要根据项目玩法提前设计完善。

scripting/extend/character_ai.1693814449.txt.gz · 最后更改: 2023/09/04 16:00 由 bibiboxs