游戏 Lua 脚本 - 使用协程还是轮询?

Yel*_*_13 1 lua logitech logitech-gaming-software

我开始学习如何使用 Logitech 软件针对不同的游戏配置文件使用 Lua 脚本。

首先我尝试使用 onevent (我知道它不是很先进)并创建了这个攻击组合脚本

function OnEvent(event, arg) 
    if event == "MOUSE_BUTTON_PRESSED" and arg == 1 then --set flag for mb1
        mb1_pressed = true
    elseif event == "MOUSE_BUTTON_RELEASED" and arg == 1 then --set flag for mb1=false
        mb1_pressed = false
    end
end

if mb1_pressed then --using flags to determine whether to start attack or not
    repeat
        presskey("A")
        Sleep(50)
        releasekey("A")
        Sleep(100)
        --if MB1 is release, it will also break script. if i only tap mb1, this will only execute the first line of attack without the rest below
        if not (**argument**, can be MB1/ismouse1) then break end
        presskey("S")
        Sleep(50)
        releasekey("")
        Sleep(120)
        presskey("A")
        Sleep(50)
        releasekey("A")
        Sleep(200)
        if not (**argument**, can be MB1/ismouse1) then break end --if MB1 is release, it will also break script. this point will prevent script from looping from start if mb1 release
    until not (**argument**, i use ismouse1) --end the loop of script
end
Run Code Online (Sandbox Code Playgroud)

所以我试图将其绑定到我的logiech鼠标的G6按钮(使用mouse_button_press == 6)使用MB6设置标志有效,但结束循环/中断循环不能由MB6触发

经过对罗技支持的 SDK/Lua 论坛的一些研究,似乎我的脚本有问题

  1. 当脚本执行循环序列时,不能使用/检测标志作为参数
  2. IsMouseButtonPressed(读取窗口按键)可以在适当位置或参数中使用
  3. Windows 仅检测 MB1-5,因此无法绑定到 G6(注册为第 6 个按钮)

我读到使用 couroutine.yield() 或轮询可用于停止循环中的重复脚本。但我在网上找不到初学者教程。

抱歉问了这个新手问题!

hug*_*omg 5

我对 Logitech 鼠标一无所知,因此我将尝试使用简化的纯 Lua 示例来解释事情。让我们将自动攻击脚本建模为循环,交替打印“A”和“B”。“A”对应循环的第一部分(按下并释放 A),“B”代表第二部分(按下并释放 S 和 A)。

function autoattack()
    while true do
        print("A")
        print("B")
    end
end

autoattack()
Run Code Online (Sandbox Code Playgroud)

到目前为止我们还可以,但是循环显然会永远运行,我们需要添加一种方法来停止它。我认为你想做的是:

local autoattacking = false

function autoattack()
    autoattacking = true
    while true do
        print("A")
        if not autoattacking then break end
        print("B")
        if not autoattacking then break end
    end
end

function stop_autoattack()
    autoattacking = false
end

autoattack()    
stop_autoattack()
Run Code Online (Sandbox Code Playgroud)

但是,由于自动攻击是一个无限循环,因此 stop_autoattack 永远不会运行,并且自动攻击标志永远不会更新。我们该如何解决这个问题?

轮询

如果我们可以调用一些代码来查看是否应该停止循环,而不是调用函数并设置标志来停止循环,该怎么办?

function continue_autoattack()
    print("continue autoattacking? y/n")
    return (io.read("*l") == "y")
end 

function autoattack()
    while true do
        print("A")
        if not continue_autoattack() then break end
        print("B")
        if not continue_autoattack() then break end
    end
end

autoattack()
Run Code Online (Sandbox Code Playgroud)

在你的鼠标中,这可能意味着使用某种 isKeyPressed 函数(如果它在 API 中可用)。同样重要的是要注意,自动攻击循环仍然是一个无限循环 - 只是我们对其进行了更改,以便它可以控制其停止条件。

协程

如果我们想让代码在循环之外停止循环,我们将需要一种方法来一次一步地运行自动攻击循环。这是一个例子:

local state = 1
function autoattack_step()
    if state == 1 then
        print("A")
        state = 2
    elseif state == 2
        print("B")
        state = 1
    elseif state == 3
        print("STOPPED")
        --state remains as 3
    else
        error("bad state") -- defensive programming; I hate if/elseif without an else
    end
end

function stop_autoattack()
    state = 3
end

autoattack_step()
autoattack_step()
autoattack_step()
stop_autoattack()
autoattack_step()
Run Code Online (Sandbox Code Playgroud)

由于我们打破了自动攻击循环,因此我们现在有机会在调用 autoattack_step 之间调用 stop_autoattack。要在鼠标脚本中执行此操作,我认为 stop_autoattack 可以进入“释放按钮”处理程序,但我不知道将 autoattack_step 调用放在哪里。也许 API 包含类似于 Javascript 中的 setTimeout 或 setInterval 的内容。

至于协程,它们从何而来?您是否注意到我们需要进行一些实质性的代码重构才能将循环分解为 autoattack_step 的单步块?协程是一项 Lua 功能,可让您使用循环编写代码,同时仍然能够“一次一步”运行它们。当协程达到 coroutine.yield 时,它会返回到其调用者。问题是,当您再次调用 coroutine.resume 时,协程将从停止处继续执行,而不是像普通函数那样返回到开头。

 local autoattacking = true

 autoattack = coroutine.create(function()
     while true do
         print("A")
         coroutine.yield()
         if not autoattacking then break end            
         print("B")
         coroutine.yield()
         if not autoattacking then break end
     end
 end)

 function stop_autoattack()
     autoattacking = false
 end

 coroutine.resume(autoattack)
 coroutine.resume(autoattack)
 coroutine.resume(autoattack)
 stop_autoattack()
 coroutine.resume(autoattack)
 coroutine.resume(autoattack)
Run Code Online (Sandbox Code Playgroud)

很多时候,协程可以让代码更具可读性,而无需使用大量显式的“状态”变量。不过,我们仍然需要一些“更高级别”的代码来调用 coroutine.resume,就像我们需要一些更高级别的代码来调用 autoattack_step 一样。