`
cqphper
  • 浏览: 23243 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
最近访客 更多访客>>
社区版块
存档分类
最新评论

(转)LUA脚本实现角色AI的新方法(一、二)

    博客分类:
  • Lua
阅读更多
在用Lua写AI脚本这一块,这是我从网上找到的唯一一篇文章,而且写的非常好,读后受宜菲浅!让我少走很多弯路。但文章源出处找不到了,在这里对这篇文章的作者表示非常的感谢!

LUA实现角色AI的新方法


怪物AI只需要提供3种条件集:

1. 无目标的条件集

2. 有目标的条件集

3. 无论是有目标还是无目标, 都必须检测的条件集. 或者叫做定时器条件集.


假设目前我们为游戏提供如下类型的怪物表现: [来自ai_define.lua]

--ai类型 : 废材型 表现为被打也不还手, 继续随机走动

ai_useless = { no_target = as_idle,    has_target = as_forget, tick = as_tick}


--ai类型 : 标准型 表现为被打了就还手并追击, 自己不会主动搜敌

ai_standard = { no_target = as_idle, has_target = as_chase , tick = as_tick}


--ai类型 : 进攻型 表现为保持警戒搜敌,有目标就追击

ai_attacker = { no_target = as_guard, has_target = as_chase , tick = as_tick}


--ai类型 : 胆小型 表现为被打了就反方向逃跑了

ai_flee   = { no_target = as_idle, has_target = as_flee , tick = as_tick}


--ai类型 : 机智型 表现为保持警戒搜敌, 如果目标靠近, 就躲远点继续攻击,

用于高级远程攻击怪

ai_smart = { no_target = as_guard,has_target = as_around, tick = as_tick}


--ai类型 : 巡逻型 表现为两点间巡逻, 并保持警戒搜敌, 有目标就追击

ai_patrol   = { no_target = as_patrol, has_target = as_chase, tick = as_tick}


然后写出如下两个AI运行的核心函数

function handle_state(c, state) --处理条件集(state), 参数c表示角色对象

      if state==nil then return end

      for con, event in state do    --遍历条件集中的所有条件

           if con==1 then           

                 event(c)               --永为真的条件, 必然执行事件

else

                 local r = con(c)

                 if r~=0 then event(c, r) end –条件满足, 执行事件

           end    

      end

end

function ai_loop(c) --每个角色都会循环执行的ai函数

   local ai_t = GetAIType(c)    --取出角色的ai类型定义, 例如废材,标准

    local t    = GetChaTarget(c) --取出当前角色的目标

      if t~=nil then

        handle_state(c, ai_t.has_target) --有目标的处理

    else

          handle_state(c, ai_t.no_target) --无目标的处理

    end

   handle_state(c, ai_t.tick) –无论有目标还是无目标, 都要进行的处理

end


打完收功, AI就实现完毕了. 除了这两个总计21行的lua函数, 剩下的事情应该交给脚本策划了……虽然很残忍, 但是很清晰, 下面我们来看看脚本策划应该实现的部分:
1. 组合条件集
2. 编写具体的条件以及条件产生的事件


下面我们来看看脚本策划应该实现的部分:
1. 组合条件集
2. 编写具体的条件以及条件产生的事件

第一步 : 组合条件集

--AI状态条件集 : 各种AI通用的tick

--无论有无目标, 角色都会进入这些条件检查

--只要角色回到了出生点附近, 则清除'回家'的标志, 当角色被置上此标志时, 是不搜敌的

as_tick = {}

as_tick[aic_near_spawnpos] = ai_event_clear_gohome


--AI状态条件集 : 忘记目标

as_forget = {}

as_forget[1] = ai_event_clear_target  --( 条件为1表示必然执行 )


--AI状态条件集 : 警戒

as_guard = {}

as_guard[aic_seek_target] = ai_event_find_target


--AI状态条件集 : 休息(随机移动)

as_idle = {}

as_idle[aic_rand_8_1] = ai_event_rand_move


--AI状态条件集 : 巡逻

--如果回到巡逻起点, 则开始在起点休息

--如果休息结束, 应该开始巡逻了, 则开始巡逻

--如果抵达巡逻目标点, 则在目标点开始休息

--巡逻的同时, 保持警戒

--巡逻内部子状态的说明:

--patrol_state = 0 表示可以开始前往巡逻目标点

--patrol_state = 1 正在前往目标点的路上

--patrol_state = 2 表示可以回到起始点

--patrol_state = 3 表示正在从回到起始点的路上


as_patrol = {}

as_patrol[aic_patrol_begin]   = ai_event_patrol_begin

as_patrol[aic_patrol_arrive]  = ai_event_patrol_end_idle

as_patrol[aic_patrol_return]  = ai_event_patrol_return

as_patrol[aic_patrol_back_ok] = ai_event_patrol_start_idle

as_patrol[aic_seek_target]    = ai_event_find_target


--AI状态条件集 : 追击

--如果距离出生点太远, 则往回走

--如果目标已经超出视野, 则清除目标

as_chase = {}

as_chase[aic_at_spawn_toofar]= ai_event_go_home

as_chase[aic_target_outofsight] = ai_event_clear_target

as_chase[aic_update_target]   = ai_event_update_target

as_chase[1]                    = ai_event_use_skill


第二步. 编写具体的条件以及条件满足后产生的事件
这里的代码较多, 列举几个例子说明问题就好了, 总的原则是来自c/c++的接口函数我们称之为sdk, 脚本策划自己实现的函数我们称之为2次封装, 这些条件函数有的是各种AI都可以使用的, 有的是某种AI专用的. 来自[ai_condition.lua]
-------------------------------------通用条件列表-----------------------------------

--条件 : 角色位于出生点

function aic_at_spawnpos(c)

      local x, y = GetChaSpawnPos(c)

if is_near_pos(c, x, y, 100)==1 then

          return 1

    end

    return 0

end


--条件 : 距离出生点太远

function aic_at_spawn_toofar(c)

      local chase_r = GetChaChaseRange(c)

      local x, y = GetChaSpawnPos(c)

      local now_x, now_y = GetChaPos(c)

   local dis = (now_x - x) * (now_x - x) + (now_y - y) * (now_y - y)

   if dis > chase_r * chase_r then

          return 1

   end

   return 0

end


--条件 : 目标超出视野

function aic_target_outofsight(c)

      local t = GetChaTarget(c)               --取出当前角色的目标

      local vision = GetChaVision(c)         --取出角色的视野

      if is_near(c, t, vision)==0 then       --目标距离已经太远

        return 1

    end

    return 0

end


--条件 : 发现目标

function aic_seek_target(c)

      if is_moving_back(c)==1 then return 0 end

      local t = find_target(c, 0) --没有目标, 则寻找一个, 没有找到则t为空

    if t~=nil then

          return t

    end

    return 0

end

……



下面代码来自[ai_event.lua]
--事件 : 发现目标

function ai_event_find_target(c, t)

      SetChaTarget(c, t)

     ai_event_use_skill(c)

end

--事件 : 随机移动

function ai_event_rand_move(c)

      birth_rand_move(c, 600) –在周围6米的范围内随机移动

end

--事件 : 开始新一轮的巡逻

function ai_event_patrol_begin(c)

local px, py = GetChaPatrolPos(c) --取出巡逻点

    ChaMove(c, px, py)

    SetChaPatrolState(c, 1) --修改巡逻标记, 设置为移动中

end

--事件 : 回出生点

function ai_event_go_home(c)

      clear_target(c)      --清除目标

    local x, y = GetChaSpawnPos(c)

    ChaMoveToSleep(c, x, y)

    set_moving_back(c, 1)

      LG("ai_debug", "event go home", GetChaPatrolState(c))

end

--事件 : 清除目标

function ai_event_clear_target(c)

      clear_target(c)  --清除目标

end

--事件 : 目标更新(仇恨度相关检查)

function ai_event_update_target(c)

      local t = GetChaTarget(c) --取出当前角色的目标

      local tNew = GetChaFirstTarget(c) --通过伤害判断取得优先目标

   if tNew~=nil and tNew~=t then

       clear_target(c)         --清除原目标

       SetChaTarget(c, tNew)   --设置新目标

        return 1

   end

   return 0


end


--事件 : 对目标使用技能

function ai_event_use_skill(c)

      local t = GetChaTarget(c)     --取出当前角色的目标

      local skill_id = select_skill(c) --怪物按照比率选择自己的技能

     ChaUseSkill(c, t, skill_id)     --向目标移动并使用技能

end


--事件 : 清除回家的状态

function ai_event_clear_gohome(c)

      set_moving_back(c, 0)

      if GetChaTypeID(c)==41 then

           LG("ai_debug", "event clear gohome", GetChaPatrolState(c))

      end

end
……

当需要扩充新的ai类型, 就重复以上的步骤:
1. 组合条件集
2. 编写具体的条件以及条件产生的事件

结束!


对于游戏中怪物的运行逻辑而言, 主要是检测出怪物此时的状态, 然后确定该状态下需要检测哪些行为条件, 每当符合一个行为条件, 就执行一个事件, 所以在实现AI的时候, 可以认为 状态 = 条件集合. 按照这种思路, 结合LUA独特的语法特点, 就可以把AI实现为如下的形式.
分享到:
评论

相关推荐

    一个用lua写的游戏脚本实例

    "游戏脚本设计与实现" ...从提供的游戏脚本实例中,我们可以了解到游戏脚本设计的基本结构、游戏任务的设计、游戏机制的实现、游戏逻辑的编写、lua语言在游戏脚本中的应用、游戏脚本的优化和游戏脚本的测试等知识点。

    SpringBoot+Redis执行lua脚本的方法步骤

    SpringBoot+Redis 执行 Lua 脚本的方法步骤 以下是 SpringBoot+Redis 执行 Lua 脚本的方法步骤的知识点总结: 1. 背景:在开发中,我们需要一次性操作多个 Redis 命令,但是这些操作不具备原子性,而 Redis 的事务...

    lua脚本问文件哈哈哈

    lua脚本问文件哈哈哈lua脚本问文件哈哈哈lua脚本问文件哈哈哈lua脚本问文件哈哈哈lua脚本问文件哈哈哈lua脚本问文件哈哈哈lua脚本问文件哈哈哈lua脚本问文件哈哈哈lua脚本问文件哈哈哈lua脚本问文件哈哈哈lua脚本问...

    json转lua格式脚本

    json转lua脚本,全自动转换 快速读取

    LUA脚本|LUA脚本支持库

    LUA脚本支持库|LUA脚本支持库

    基于Lua脚本语言的嵌入式UART通信的实现

    "基于Lua脚本语言的嵌入式UART通信的实现" 本文提出了一种基于Lua脚本语言的解决方案,旨在提高IED装置对各种类型串口数据报文帧格式的适应性。该方案将具体串口报文规约的组建和解析交给Lua脚本进行处理,使设计者...

    Lua脚本支持库

    Lua脚本支持库

    Lua 脚本

    Lua 是一个小巧的脚本语言。作者是巴西人。该语言的设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。 Lua脚本可以很容易的被C/C++代码调用,也可以反过来调用C/C++的函数,这使得Lua在应用...

    Java学习资料-Spring Boot - 结合 Redis 使用 Lua脚本

    高并发场景:在高并发场景下,使用 Lua 脚本可以实现原子性操作,避免并发问题。 复杂业务逻辑:对于复杂的业务逻辑,使用 Lua 脚本可以简化代码,提高可读性和可维护性。 事务处理:使用 Lua 脚本可以实现事务处理...

    lua server 实现了lua脚本处理服务端逻辑+数据库

    《终极网络服务端编程》的lua server 实现了lua脚本处理服务端逻辑+数据库 lua服务端 c++版服务端客户端(netserver,netclient) lua服务端 就是 c++ netserver项目的lua脚本化版 运行效果 ![image]...

    freeswitch LUA 脚本reference

    freeswitch LUA 脚本reference lua 脚本reference lua 开发脚本指南

    delphi调用lua脚本的一个例子

    delph调用lua脚本,在delphixe3下通过

    用CEGUI和lua脚本实现的登录界面

    用CEGUI结合lua脚本写的一个小的游戏界面 这个压缩包里面后缀名为lua是脚本信息 layout是界面,cpp和.h里面是具体怎么在window窗口上显示 登录界面和功能 CEGUI在编译前要把工作目录什么的都赔好!!

    android Lua脚本 文件

    运行Lua脚本语句 运行Lua脚本文件 调用 Android API

    Lua脚本代码实例

    项目实现的Lua脚本代码,有需要的朋友可以参考

    LUA 脚本入门教程(WORD版)

    本资源是一个关于LUA脚本语言的入门教程,旨在帮助读者快速掌握LUA语言的基础知识和语法。该教程由Roberto Ierusalimschy编写,www.luachina.net翻译。 一、LUA语言概述 LUA是一种轻量级的脚本语言,广泛应用于...

    FCEUX Lua 脚本的模拟 退火实现自动搜索 NES 输入序列_lua_代码_下载

    FCEUX Lua 脚本的模拟退火实现自动搜索 NES 输入序列。 用法 准备 FCEUX。 打开一个ROM。 打开 Lua 脚本。 停止 Lua 脚本。 打开 TAS 编辑器。 运行 Lua 脚本。 请注意,脚本不会按下 START 按钮。请手动按下开始...

    LUa脚本编辑器

    LUa脚本编辑器

    lua脚本解密助手(unluac_2021_12_06.jar)

    适用于lua脚本的解密

Global site tag (gtag.js) - Google Analytics