能否从宿主程序执行单个Lua语句?

我正在尝试将基于Lua的脚本系统嵌入我的游戏引擎中。

我希望脚本可以具备阻塞和非阻塞命令,例如:

character.walkTo(24, 359);  // 直到角色到达之前一直阻塞
c = 35; // 非阻塞,执行继续进行到下一个语句

由于“walkTo”需要在超过一个执行帧的时间内“活动”,我希望能够从Java主机中逐条运行语句,而不是整个函数。

这是因为真正的多线程是过度的,不需要。如果我只能执行一条语句,并保持执行状态“暂停”直到下一条语句执行,我就可以通过检查主机中的命令是否完成来实现阻止命令,如果完成,继续到下一条语句,否则等待下一个帧迭代。

有没有办法从Java宿主中使用LuaJ(或任何其他Lua api)逐条执行1条语句,或者我必须使用lex和yacc开发自己的脚本引擎?

欢迎任何好主意,谢谢!

点赞
用户3125367
用户3125367

看起来你缺少异步模式。如果在 (24,359) 处出现 character 后,必须执行 c=35 一次,那么正确的方法是将 function() c=35 end 作为第三个参数传递给 walk 方法,你的引擎(执行实际的“走路”)会在适当的时候调用该回调函数。

character.walkTo(24, 359, function ()
    c = 35
end)

否则,walk 可能会将步行计划安排到引擎中并立即进行 yield,在正确的事件上恢复。在这种情况下,您必须设置脚本工作协程(在主状态中无法 yield)。

script = coroutine.wrap(function ()
    character.walkTo(24, 359) -- 将 yield 并留下可调用的全局变量 'script'
    c = 35
end)
script() -- 第一次恢复
-- 在此 script 结束

...

-- 此包装器可以在 Lua 或宿主语言中实现

function character.walkTo(x, y)
    engine.startActualWalkingTo(x, y)
    coroutine.yield() -- 委托给主机进行 yield
    -- coroutine.resume() 将在这里继续运行
end

...

-- 引擎代码中的某个位置(这里是伪代码)

for event in eventLoop do
    if character.x == endPoint.x and character.y == endPoint.y then
        script() -- 在 c=35 处再次恢复
    end
end

您可以随时使用 script=nil 取消脚本。

yield() 有一些限制,请参阅手册。

2014-12-22 16:47:33
用户1909094
用户1909094

以下是我的准确解决方案:

-- test.lua --

onLookAt = function()
    character:walkTo(234, 35)
    print("到达了!")
end

-- LuaTest.java --

public static void luaTest() {
    Globals g = JsePlatform.standardGlobals();
    g.load(new CoroutineLib());

    g.set("character", CoerceJavaToLua.coerce(new Character(g)));
    g.load(Gdx.files.internal("test.lua").reader(), "test.lua").call();
    g.load("co = coroutine.wrap(onLookAt)").call();
    g.get("co").call();
    try {
        // 模拟脚本暂停时经过的时间
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    g.get("co").call();
}

public static class Character{
    Globals g;

    public Character(Globals g){
        this.g = g;
    }

    public void walkTo(int x, int y) {
        System.out.println("开始走路。");
        g.yield(LuaValue.NONE);
    }
}

-- 输出 --

开始走路。

(2秒后)

到达了!

有一件事情你应该非常小心:

  • 如果你想完成这个任务,请不要使用Java的ScriptEngine接口。 ScriptEngine接口不提供获取隐式分配的Globals实例的API,而你需要这个实例来进行yield,而且新建一个Globals实例并使用它进行yield是毫无意义的。
2014-12-24 04:01:17