本帖最后由 吾奶常煽赵子龙 于 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
|