一个高效简洁AI脚本系统的实现
-陈瑶文 怪物脚本示例
;红熊王见到人就扑上来砍一定几率对玩家释放烈爪狂击(588)和震地攻击(562),血少于50%时释放兽王怒吼603后沿固定路线逃跑,逃跑时挨打不会停不会还手; [common] [commonend] ;注意物理攻击技能优先级应该比远程技能优先级高 [skill] 0:if(AIS_IsCanSkill(0)=1){AIS_ToSkill(0);AIS_SetTimes(-1);AIS_SetPRI(30);};如果能使用0号技能则释放次数不限普通攻击 1:if(AIS_GetAIState()=SATTACK&AIS_Rand()<30&AIS_IsCanSkill(588)=1){AIS_ToSkill(588);AIS_SetTimes(-1);AIS_SetPRI(32);};在攻击状态30%的机率释放588号技能(烈爪狂击)次数不限 2:if(AIS_GetAIState()=SATTACK&AIS_Rand()<20&AIS_IsCanSkill(562)=1){AIS_ToSkill(562);AIS_SetTimes(5);AIS_SetPRI(33);};在攻击状态20%的机率释放562号技能(震地攻击)次数5 3:if(AIS_GetAIState()=SATTACK&AIS_GetHP()<50&AIS_IsCanSkill(603)=1){AIS_ToSkill(603);AIS_SetTimes(1);AIS_SetPRI(39);};在血少于一半后释放兽王怒吼603次数1 4:if(AIS_GetHP()<50){AIS_ToSpeak(66,2);AIS_SetBaseAIType(1,2,15);AIS_SetPatrolID(0);AIS_StartPatrol();AIS_SetTimes(1);AIS_SetPRI(37);} [skillend] [beskill] [beskillend] [damage] [damageend]
分段结构(一)
脚本按状态配置各个脚本 在AI逻辑里由状态逻辑调用脚本段 common段各个状态都有可能调用的脚本段 其余段一般都对应某个状态或者只是一个名字便于调用方索引。 分段结构(二)每个段里面可配几个“条件节点-执行动作”对 条件节点包含一个或多个条件表达式的OR/AND形式 执行动作列表里包含一几个要执行的动作 条件表达式以及执行动作可调用的方法是有限的预配置的AI脚本系统接口 每个“条件节点-执行动作”对有个数字序号以及可配置的优先级最多执行次数。
脚本预处理对每一段调用一个段解析 每个段里面每一行构造一个“条件节点-执行动作”对。 一个段里面的“条件节点-执行动作”对按优先级构造列表。条件节点中的条件表达式以及OR/AND操作符构造一颗条件树 条件树其实是一颗中缀表达式二叉树 脚本调用当怪物的AI调用脚本时先根据状态其实只是标 识值参数找到对应段。 取出该段的“条件节点-执行动作”对列表。 挨个取出条件节点的条件树求值如果为TRUE则判断该节点还能执行几次如果不能执行了那么跳过要不执行对应的动作列表。执行完后设置还可执行次数。 条件树求值沿着最深左子树向上 如果左子节点加上父节点的操作符可以断定为TRUE则继续上行 否则走右子节点重复第一步。 AIS接口
INT AIS_GetBuffState( const Obj_Character *pChar, INT param0, INT param1, INT param2 ); INT AIS_GetAIState( const Obj_Character *pChar, INT param0, INT param1, INT param2 ); INT AIS_GetHP( const Obj_Character *pChar, INT param0, INT param1, INT param2 ); INT AIS_GetMP( const Obj_Character *pChar, INT param0, INT param1, INT param2 ); INT AIS_Rand( const Obj_Character *pChar, INT param0, INT param1, INT param2 ); INT AIS_GetEBuffState( const Obj_Character *pChar, INT param0, INT param1, INT param2 ); INT AIS_GetEMP( const Obj_Character *pChar, INT param0, INT param1, INT param2 ); INT AIS_GetEHP( const Obj_Character *pChar, INT param0, INT param1, INT param2 ); INT AIS_GetEMenPai( const Obj_Character *pChar, INT param0, INT param1, INT param2 ); INT AIS_GetELevel( const Obj_Character *pChar, INT param0, INT param1, INT param2 ); INT AIS_IsCanSpeak( const Obj_Character *pChar, INT param0, INT param1, INT param2 ); INT AIS_IsHasEnemy( const Obj_Character *pChar, INT param0, INT param1, INT param2 ); INT AIS_GetESex( const Obj_Character *pChar, INT param0, INT param1, INT param2 );
注册AIS接口函数typedef INT (*PLAISFunc)( const Obj_Character *pChar, INT param0, INT param1, INT param2 ); #define REGISTERAISFUNC(funcname) {#funcname,PLAISFunc(funcname)}, struct AISFuncMap { CHAR* funcname; PLAISFunc Func; }; AISFuncMap g_AISFuncTable[] = { REGISTERAISFUNC(AIS_GetBuffState) REGISTERAISFUNC(AIS_GetAIState) REGISTERAISFUNC(AIS_GetHP) REGISTERAISFUNC(AIS_GetMP) //…..省略 };
|