LUACN论坛

 找回密码
 加入我们

QQ登录

只需一步,快速开始

搜索
热搜: YJWOW MagicStone BoL
查看: 2017|回复: 77

【Ni辅助保姆级高级教程】七、自动寻路

  [复制链接]
发表于 2024-6-24 12:33:39 | 显示全部楼层 |阅读模式
本帖最后由 吾奶常煽赵子龙 于 2024-6-26 02:44 PM 编辑

上一篇讲了用Ni如何实现地图传送,这一篇来讲讲如何实现在指定范围内挂机(主要是讲讲实现的思路)。

要实现范围内挂机,需要解决两个问题:自动打怪和自动寻路。

自动打怪
自动打怪其实很好解决,启动一个职业输出脚本就行了。遇到怪就自动攻击,脱战后再来个范围拾取(如果有范围拾取道具)或者挨个拾取都行。


使用Ni自动挨个拾取可以参考以下代码:
[Lua] 纯文本查看 复制代码
if ni.player.ismoving() then
    return;
end

-- 找到距离最近的可以拾取的尸体目标
local guid;
local distance;
for k, v in pairs(ni.objects) do
    if type(k) == "string" and type(v) == "table" then
        if ni.unit.islootable(k) then
            local dis = v:distance();
            if distance == nil or distance > dis then
                distance = dis;
                guid = k;
            end
        end
    end
end

-- 如果找到了可以拾取的目标
if guid then
    if distance > 2 then
        -- 如果距离过远,就向目标移动
        ni.player.moveto(guid);
    else
        -- 和目标交互(摸尸体)
        -- 某些战利品拾取是会有绑定的提示,再加一个弹框确认就行了
        ni.player.interact(guid);
    end
end

反复执行上面的代码,就可以完成自动拾取了。

难点是自动寻路。自动寻路的常见场景的有两种:一种是范围内挂机(比如野外挂机),一种是按照指定的路线走(比如下副本)。

自动寻路-范围内挂机(简单版)
假设我们已经采集到了某个地图一个多边形区域的坐标(坐标采集后续会分享一个写好的脚本,也可以自动写,使用local x, y, z = ni.unit.location("player")获取到玩家当前的坐标,然后保存到文件里就可以了,很简单。):
[Lua] 纯文本查看 复制代码
local coordinates = {
    {x = -7100.4248046875, y = -2305.0605468751, z = -268.41726684571},
    {x = -6708.1333007813, y = -1999.5262451172, z = -270.07910156251},
    {x = -6304.890625,         y = -1451.8521972661, z = -269.25357055664},
    {x = -6456.2153320313, y = -984.38262939453, z = -274.24288940431},
    {x = -6867.6162109376, y = -552.15948486328, z = -270.82815551758},
    {x = -7323.6733398438, y = -430.13583374023, z = -271.99499511719},
    {x = -7603.9833984375, y = -575.53540039063, z = -261.47396850586},
    {x = -7919.5205078125, y = -1008.5536499023, z = -272.46188354492},
    {x = -7969.7573242188, y = -1685.6810302734, z = -270.69784545898},
    {x = -7465.0893554688, y = -2134.4899902344, z = -271.49932861328},
};


大概形状如下:

思路也很简单,根据角色当前的坐标,随机生成下一个要移动的坐标就行了:
[Lua] 纯文本查看 复制代码
function RandomCoordinate()
    -- 获取玩家当前的坐标
    local x, y, z = ni.unit.location("player")

    -- 随机生成500码范围内的x轴增量
    local dx = math.random(1, 500)

    -- 随机生成500码范围内的y轴增量
    -- 防止生成的dx和dy都特别小(比如当前坐标是(100,500), 生成的新坐标是(101,5002)这种距离非常近的)
    -- 则可以稍微处理下:当生成的dx太小时,生成dy的范围就调大一些
    -- 当然,也可以不做任何处理,和生成x一样生成即可。
    local dy;
    if dx < 250 then
        dy = math.random(250, 500)
    else
        dy = math.random(1, 500)
    end

    -- 随机生成正负的dx
    if math.random(2) == 1 then
        dx = -dx
    end
    
    -- 随机生成正负的dy
    if math.random(2) == 1 then
        dy = -dy
    end

    -- 返回新的坐标(z轴不用处理)
    return {x = x + dx, y = y + dy, z = z}
end


上面的方法很简单,就是根据当前坐标随机生成一个周围一定范围内的坐标。

但是有个问题,就是新的坐标不能超出上面给定的范围,要不然挂着挂着角色都不知道跑哪里去了。那么,如何才能保证这个新的坐标在上面给定的范围内呐?

这里可以用Pnpoly算法(对算法感兴趣的可以自己去搜),算法原理:从一个目标点引出一条射线,统计这条射线与多边形的交点个数。如果有奇数个交点,则说明目标点在多边形内,若为偶数个交点,则在外。


代码很简单:
游客,如果您要查看本帖隐藏内容请回复


代码就上面几行就可以判断新的坐标点是否在给定的范围内了。下图是测试了100个随机点的效果(绿点在范围内,红点在范围外):


自动寻路-按照指定的路线走(简单版)

这种情况也比较简单,首先是提前采集要走的路线坐标集合。然后在自动寻路的时候,根据当前坐标,在路线集合中寻找距离最近的坐标并移动到这个坐标,然后依次移动到下一个坐标就可以了。

有个小问题就是在行走中途,有可能去打怪导致路线偏移了,比如距离最近的是A点和B点,我们在AB点中间(假设为C点),但是不在AB的连线上(ABC是一个三角形),这个时候如果直接从C点移动到B点,有可能出现卡位的情况,最好的处理方式是先回到AB的连线上,然后再移动到B点,这样卡位的几率低一些,算法如下:
游客,如果您要查看本帖隐藏内容请回复



卡位的简单处理

前面给了两种场景的简单版算法。有一个共同的问题就是有可能出现卡位。当出现卡位了,一个简单处理方式就是尝试向一个随机生成的坐标移动,这样多次尝试随机移动来解除卡位。

那么又如何检测是否卡位了呐?这里提供一种思路:记录最近移动的点(比如最近10个、20个点),然后检查这些点的总移动距离是否小于某个阈值,如果小于了指定的阈值那么就表示卡位了。

附件是一个坐标点录制脚本,支持多种录制方式(具体见脚本配置)。支持生成lua文件和json文件。录制文件保存在addon\Settings\下面。

附json文件的加载方式:
[Lua] 纯文本查看 复制代码
-- 判断point是否在polygon内部
-- polygon:坐标范围集合
-- point:新生成的坐标
function isInnerCoordinate(polygon, point)
    local j = #polygon
    local oddNodes = false

    for i = 1, #polygon do
        if (polygon[i].y < point.y and polygon[j].y >= point.y or polygon[j].y < point.y and polygon[i].y >= point.y) then
            if (polygon[i].x + (point.y - polygon[i].y) / (polygon[j].y - polygon[i].y) * (polygon[j].x - polygon[i].x) < point.x) then
                oddNodes = not oddNodes
            end
        end
        j = i
    end

    return oddNodes
end







本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?加入我们

x
回复

使用道具 举报

发表于 2024-6-24 15:11:16 | 显示全部楼层
给力,移动这里当初也折腾过很久。
有些地方不知道如何判断,例如icc里面的电梯和一些楼梯。到最后也没弄出全自动icc是遗憾哈
回复 支持 反对

使用道具 举报

发表于 2024-6-24 15:20:04 | 显示全部楼层
感谢大佬的分享 辛苦了 非常感谢
回复 支持 反对

使用道具 举报

发表于 2024-6-24 15:41:49 | 显示全部楼层
虽然看不懂 先感谢大佬
回复 支持 反对

使用道具 举报

发表于 2024-6-24 16:05:34 | 显示全部楼层
谢谢!大佬高产啊,让我看看你藏了撒!
回复 支持 反对

使用道具 举报

发表于 2024-6-24 16:08:22 | 显示全部楼层
学习使人进步,伸手费我来了
回复 支持 反对

使用道具 举报

发表于 2024-6-24 16:09:01 | 显示全部楼层
贾维斯117 发表于 2024-6-24 03:11 PM
给力,移动这里当初也折腾过很久。
有些地方不知道如何判断,例如icc里面的电梯和一些楼梯。到最后也没弄 ...

大佬,ni有没有可能实现一种载具攻击方式,用A技能打了10-20次,在用一次b技能,在重复。
回复 支持 反对

使用道具 举报

发表于 2024-6-24 16:33:28 | 显示全部楼层
老爷们 发表于 2024-6-24 04:09 PM
大佬,ni有没有可能实现一种载具攻击方式,用A技能打了10-20次,在用一次b技能,在重复。

打20次A,再打1次B,再打20次A。可以用一个变量来count来统计,每次使用技能前判断count是否>20,不是则使用A并且count+1。
>20则使用B,并且count=0重新进入循环
回复 支持 反对

使用道具 举报

发表于 2024-6-24 18:19:11 | 显示全部楼层
感谢大佬的无私奉献,学习使人进步,伸手费我来了

回复 支持 反对

使用道具 举报

发表于 2024-6-24 22:15:31 | 显示全部楼层

感谢大佬的无私奉献
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 加入我们

本版积分规则

小黑屋|手机版|Archiver|LUACN论坛

GMT+8, 2026-11-13 08:45 AM , Processed in 0.024623 second(s), 6 queries , Gzip On, Redis On.

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表