基于 AzzyAI(RO 生命体/佣兵 AI 框架)让人工生命体AI自动寻找并攻击唯一魔物。
一、背景
什么是唯一猎手模式?
最近无聊在玩一款RO SF,工作生活之余对童年进行下回忆吧。这款SF中有一个魔物找茬的小游戏,玩家被传送到特定的地图,地图上有 N 种魔物,其中恰好有一种在全场只有 1 只,其余 N−1 种每种至少 2 只;谁先找到并击杀那只「落单」魔物谁获胜并获得奖励,所有玩家传出地图,开启下一轮找茬。
唯一猎手模式的目标:让生命体自动扫描视野内的怪物,找出数量唯一的类型(即视野中只有 1 只的怪),锁定并只攻击该类型。
思路
最开始考虑过用 YOLO 做屏幕截图目标检测:识别魔物位置后在游戏窗口上框选。后来放弃了——推理有延迟,魔物会移动,等框画好目标早已走开。
我玩的是炼金术士,自带人工生命体;私服默认的生命体 AI 比较呆,调配置时想到——为什么不直接用生命体来实现呢?
优点:
- RO 官方开放的 Lua 钩子,合法合规;类似 WoW 留给玩家写插件的接口,无需外挂、无需解包。
- 低延迟,可移植,不依赖魔物外观,换私服也能复用这套逻辑。
- 生命体 AI 逻辑可扩展到其他玩法,一键切换不同场景。
相关资料
二、第一版设计
核心函数:FindUniqueMonsterType
| |
逻辑很简单:遍历 GetActors(),按 V_HOMUNTYPE 计数,筛掉死亡/无效/回避列表中的怪,返回第一个 count=1 的类型。
新状态
新增 UNIQUE_HUNT_LOCKED_ST = 201,表示"已锁定唯一怪类型"。
两种触发方式
- 自动扫描 — 在
OnIDLE_ST()的默认选敌之前插入 - Alt+T 手动 — 在
OnFOLLOW_CMD()开头拦截
状态流转
| |
目标丢失(离开视野)→ 回到 UNIQUE_HUNT_LOCKED_ST 等待
目标死亡 → 清除锁定 → 回 IDLE_ST 重新扫描
三、第一轮 Bug:锁定与恢复逻辑
Bug 1:目标死亡后永久卡死
症状:杀死唯一怪后,生命体不再攻击任何怪物,只会跟随玩家。
原因:MOTION_DEAD 路径用了 GetUniqueHuntReturnState(),返回的是 UNIQUE_HUNT_LOCKED_ST(因为 UniqueHuntTargetType 没清零)。AI 继续锁定已死的类型,不再重新扫描。
修复:
| |
Bug 2:救援机制劫持状态
症状:玩家被攻击时,AI 跳出猎手模式去反击。
原因:AI 主循环的紧急处理器没有排除 UNIQUE_HUNT_LOCKED_ST。
| |
同样的修复也应用到了距离检查:
| |
Bug 3:锁定残留导致永不重新扫描
症状:锁定被意外打断后,状态回到 IDLE_ST,但 UniqueHuntTargetType 仍然非零。OnIDLE_ST 中的自动扫描条件 UniqueHuntTargetType == 0 不成立,扫描被跳过。结果 AI 走默认选敌,攻击普通怪。
原因:自动扫描条件太严格——要求 UniqueHuntTargetType == 0。
修复:每次进 IDLE_ST 都重新扫描,不管是否已有锁定:
| |
四、第二轮 Bug:缺函数导致崩溃
症状
| |
原因
打唯一猎手补丁后(USER_AI_UNIQ_PATCH)的 AI_main.lua 在 DoAutoBuffs 中调用了 GetSOwnerSecondaryBuffSkill(),但 AzzyAI 原版的 AzzyUtil.lua 里没这个函数。补丁附带了一个 “Secondary Buff Skill” 系统,用于处理额外的主人 Buff 技能,但忘记加对应实现。
修复
在 Const_.lua 中增加一个安全桩函数:
| |
五、第三轮 Bug:“看到打不到"的死循环
症状
日志显示以下循环无限重复:
| |
怪明明在视野内(19 格),生命体就是打不到。
如何查看日志
日志通过 RO 客户端内置的 TraceAI 函数输出,写入游戏安装目录下的 AI/TraceAI.txt 文件:
- 开启日志:游戏内输入
/traceai命令,再次输入则关闭 - 日志位置:
[RO安装目录]/AI/TraceAI.txt - 注意事项:日志文件没有自动清理机制,会持续增长,建议调试完后关闭
/traceai - 部分官服/台服客户端(如 twRO)可能不支持
/traceai命令,需改用 AzzyAI 自带的logappend系统,查看USER_AI/data/下的日志
根因分析
RO 的移动范围限制。AzzyAI 有三层距离检查,逐级限制了生命体的行动范围:
| |
生命体能"看到"20 格的怪,但被限制在 14 格内移动。15-20 格的唯一怪就进入了「看到→锁定→追不上→退回→再锁定」的死循环。
修复
统一将所有限制扩展到 20 格:
| 层 | 文件 | 修改 |
|---|---|---|
| Config | H_Config.lua | StationaryMoveBounds: 14→20, MobileMoveBounds: 9→20 |
| Config | M_Config.lua | 同上 |
| Default | Defaults.lua | 同上 |
| 硬编码 | AzzyUtil.lua | Move() 中 dis2owner > 14 → 20 |
| 校验 | AI_main.lua | OnMOVE_CMD() >15→>20, OnMOVE_CMD_ST() >15→>20 |
六、最终架构
需要哪些文件?
唯一猎手模式基于 AzzyAI 完整框架运行,以下文件缺一不可:
核心文件(必须):
AI.lua/AI_M.lua— 入口文件,加载所有模块AI_main.lua— 核心状态机(唯一猎手改动在此)Const_.lua— 常量和全局变量(唯一猎手改动在此)AzzyUtil.lua— 工具函数库(改了距离限制)Defaults.lua— 默认配置值(改了移动范围默认值)H_Config.lua/M_Config.lua— 用户配置(改了移动范围参数)H_SkillList.lua/M_SkillList.lua— 技能映射表H_Tactics.lua/M_Tactics.lua— 战术配置H_Extra.lua/M_Extra.lua— 额外行为Stubs.lua— 兼容桩函数H_Avoid.lua— 回避列表A_Friends.lua— 好友系统H_PVP_Tact.lua/M_PVP_Tact.lua— PVP 战术
非必需(不参与 AI 运行):
AzzyAIConfig.exe— 可视化配置工具,改参数可以直接编辑 .lua 文件Documentation.pdf— 用户手册Mob_ID.lua— Mob ID 记录文件(目前为空)twRO.lua— twRO 服务器特殊适配
唯一猎手改动涉及的文件:
| 文件 | 修改内容 |
|---|---|
Const_.lua | 新增状态常量、全局变量、桩函数 |
AI_main.lua | ~150 行改动,新增 4 个函数 + 15+ 处状态机路径修改 |
H_Config.lua / M_Config.lua | 移动范围参数 14→20 |
AzzyUtil.lua | 硬编码距离检查 14→20 |
Defaults.lua | 默认值同步 |
H_Avoid.lua | 可选:添加要忽略的怪物类型 |
改动的函数
| 函数 | 作用 |
|---|---|
FindUniqueMonsterType() | 扫描视野,返回唯一怪类型 |
OnUNIQUE_HUNT_LOCKED_ST() | 锁定状态处理:搜索目标 → 追击/跟随 |
GetUniqueHuntReturnState() | 恢复函数:死亡→IDLE,丢失→LOCKED |
GetUniqueHuntReturnStateFromFollow() | 同上,但非猎手模式回 FOLLOW |
状态机路径
| |
配置说明
在 Const_.lua 中修改:
| |
H_Avoid.lua 中添加要忽略的怪物类型:
| |
七、经验总结
状态机的退出点是最容易出 Bug 的地方。 15+ 个 CHASE_ST/ATTACK_ST 的退出点都需要考虑 UniqueHunter 的情况。漏掉一个就可能导致永久卡死。
RO 的移动系统和视野系统不一致。 GetActors() 能看到 18-20 格,但移动限制只有 14 格。这个"视野-移动鸿沟"是"看到打不到"的根本原因。而且 AzzyUtil.lua 里还有一个硬编码的 14 格限制,配置改得再大也没用——必须同时改这个硬编码。
MyAvoid 的双重作用。 它既控制紧急回避(
os.exit()),又控制 Unique Hunter 的排除列表。同一个表在不同上下文中有不同含义,需要小心使用。恢复函数的设计至关重要。 “目标死亡"和"目标丢失"是两种完全不同的情况,需要不同的恢复路径:死亡→重新扫描,丢失→继续等待。用一个
GetUniqueHuntReturnState()来处理所有退出点会混淆这两种情况。
八、附录
日志速查表
| 日志 | 含义 |
|---|---|
UNIQUE_HUNT: AUTO-LOCKED type X | 自动扫描锁定类型 X |
UNIQUE_HUNT: LOCKED type X | Alt+T 手动锁定类型 X |
UNIQUE_HUNT: CANCELLED by Alt+T | Alt+T 取消锁定 |
UNIQUE_HUNT: Stale lock reset | 残留锁被清除,重新扫描 |
UNIQUE_HUNT: Found target ID dist=N | 在视野内发现目标,距离 N |
UNIQUE_HUNT: No target in sight | 目标不在视野,跟随主人 |
UNIQUE_HUNT: type X unreachable | 类型 X 的位置不可到达 |
安装步骤
- 先安装 AzzyAI — 将完整
USER_AI/目录复制到[RO安装目录]/AI/USER_AI/ - 再打补丁 — 将
USER_AI_UNIQ_PATCH/下所有文件复制到[RO安装目录]/AI/USER_AI/覆盖 - 重启游戏(或
@refresh) - 游戏内输入
/traceai开启日志 - 查看
[RO安装目录]/AI/TraceAI.txt确认UNIQUE_HUNT: AUTO-LOCKED输出 - 在
H_Avoid.lua中可添加要跳过的怪物类型