沙盘引擎 (SEngine)

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

用户工具

站点工具


侧边栏

scripting:extend:character_ai

📚 CharacterAI

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

沙盘引擎默认只提供基础AI操控功能,关于具体的世界内AI逻辑,开发者应根据自身项目需求编写。

📒 关键代码

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

  • character.Group 指定角色阵营(用于分辨敌我阵营)
  • character.AIDistance 指定逻辑检测距离
  • character.AITarget 读\写一个Entity类型的跟随对象(Character\Vehicle\Model
  • character.AITargetPos 读\写一个Vector类型的跟随坐标(与character.AITarget二选一)
  • character.AIState 指定一个AI状态(和平、被动、中立、主动),这也是设定角色AI的关键
  • character.AIForceMode设置角色AI是否为专注模式(即设定Target后不会被自动重置或改变,对象销毁除外)
  • ……

📒 编写逻辑示例

在《沙盘引擎》的AI开发过程中,AI部分逻辑我们将其进行了简化处理,大多数AI逻辑并非想象中那么复杂,通常只需要【计时器事件+循环检测逻辑】就可以实现多数相关需求。

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

注意:AI逻辑编写时需要相对严谨一些,提前预想到不同情况下的处理预判。

//Test Code
var npcs = [];
 
//Create NPCs
function OnPlayerComplete( player )
{
    let chara = Character.Create(0, Vector(81, 1, 28), 0);
	player.SetEntity(chara);
 
    for(let i=0;i<10;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.IsSpawned)
            {
                npc.AITarget = plr.Entity;
                break;
            }
        }
    }
}, 1, 0); //每1秒循环,循环无数次(0表示无数次)

📒 玩家同步流限制

AI寻路逻辑是一个性能昂贵的操作,所以在多数情况下需要基于玩家+网络同步流(Stream)概念。

网络同步流,可以理解为每个真实玩家(并非角色)的实时观察位置,许多网络同步\AI寻路相关的操作,只有在以此位置为中心的规范距离内才得以生效,否则视为“离开此玩家的实际控制\同步范围”,部分操作不予生效。

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

在实际AI操作过程中,比如部分大的城市地图,让NPC-A从城南寻路到城北,这期间可能有非常复杂的路程情况及干扰,这将被弹出警告并忽略寻路请求,因为这明显超出了玩家流、CPU合理计算范围,正确的做法应该是优先引用离玩家更近的NPC,或提前设置好AIState,利用此机制等待玩家靠近自动执行相关逻辑

📒 AI逻辑关系

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

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

📘 常规方案及逻辑冲突

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

如果AITarget功能不能满足需求,或者有针对自定义坐标的AI需求,可以使用AITargetPos进行针对坐标的AI指令。

值得注意的是,无论是AITarget还是AITargetPos方法,两者在根本上都属于【设定寻路目标】的功能作用,真正起到AI行为逻辑的是AIState属性

无论使用以上哪种方法(实体或Vector),AI逻辑上都认定它们是一个【目标】,例如AIState==1AITargetPos设置坐标的情况下,NPC会尝试逃离反向逃离这个目标位置(坐标点)。

📘 AIState状态机制

除了正常使用AITarget\AIMove等方式指定目标外,AIState是十分关键的AI逻辑行为代码,这将直接控制Character“性格和想法”

此状态机制会和AIDistance搭配使用,在不同状态\行为下的数值可能需要开发者动态调整。

特别注意:每当目标死亡\离开范围,角色将停止跟随并重置AITarget(除非强制模式AIForceMode)。

ID 状态 机制 AIDistance Group影响
-1 静态 无任何移动行为,不会进行任何寻路
如果存在AITarget(&Pos)并且在【手持物品】可攻击范围内,将在原地尝试面向目标攻击
距离内执行 同组忽略
0 和平(默认) 无其他行为,默认正常寻路(跟随)移动,不会主动攻击或逃脱(即使被攻击) 距离内跟随
1 逃离 默认站在原地,不会主动攻击,如果存在AITarget(&Pos)则主动反向逃离目标,否则将会反向逃离所有非同组的附近对象
(如果任何敌对Group实体攻击了自身,将自动切换AITarget=Enemy
逃离至距离外
检测附近逃离目标范围
同组忽略
2 中立 默认正常寻路移动(或站在原地),如果任何敌对Group实体攻击了自身,将自动切换AIStateAITarget=Enemy(因此开始执行“主动”行为)
具体被攻击后切换的逻辑,将由AINeutral决定反击或逃离
3 敌对 如果AITarget不为空,将主动跟随AITarget并在AIDistance范围时攻击
反之,则默认正常寻路移动,即时空闲状态时,也会自动攻击任何在AIDistance范围内的实体
(如果中途任何敌对Group实体攻击了自身,将自动切换AITarget=Enemy
距离内追击
检测附近敌对目标范围
同组忽略

注意:以上AI状态均遵循【同组Group影响==】机制(self.Group != target.Group),如果两者Group相同则会忽略(认为是友军),==将不会触发行为,并自动清除AITarget

因此,如果希望实现某些NPC跟随并攻击指定Character,一定要将两方的character.Group设定为不同的值,它们才会变成“非友军”。

如果确实有关于【中立】情况下Group相同也触发攻击的需求,可以在合适的地方提前更换character.Group,然后再默认触发AI逻辑)。

📘 AITarget和AITargetPos的使用

AITarget适合针对某个特定目标(角色、载具、模型)时使用,适合有针对性目标实例的情况

AITargetPos优先权更高(在值不为null时),对于更高级的自定义需求,以及只想让NPC前往某个坐标,那么可以使用AITargetPos属性来实现向坐标移动

两种方式均可搭配AIDistance+AIState以实现追击\逃跑\中立等功能,通常更推荐AITarget的方式。

📘 AIMoveState枚举

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

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

📒 其他补充

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

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

注:一个相对复杂的AI控制代码块,成百上千行的控制代码是正常的。

scripting/extend/character_ai.txt · 最后更改: 2024/04/05 16:11 由 bibiboxs