NLua - 如何通过Lua为userdata添加新函数?

我正在尝试为在C#中定义的Player类定义新函数。在我的项目(一个游戏引擎)中,Lua的目的是为诸如玩家之类的实体定义自定义行为。

然而,当我在Lua文件上执行DoFile(fileName)时,它会崩溃并显示以下异常:

"field or property 'Idle' does not exist"

它具体指向以下代码块的第一行:

function Player:Idle()
    self:Turn()
    self.StateType = StateType.Stand
    self.MoveType = MoveType.Idle
    self.Vel = Vector2.Zero
    if self.Anim.Number ~= 0 and (self.Anim.Number ~= 5 or self:GetAnimTime() == 0) then
        self:ChangeAnim(0)
    end
end

它似乎有问题写Player:Idle。但是,我还尝试将其写为Player.Idle,我遇到了相同的问题。这是我如何在C#中处理为玩家加载脚本的方式:

        state["Player"] = this;

        // Load the states
        for (int j = 0; j < stateFiles.Length; j++)
        {
            string fp = filePath + @"\" + stateFiles[j];

            // If it's common, then if it doesn't exist, use the default.
            if (stateFiles[j] == "common.lua" && !File.Exists(fp))
                fp = Path.Combine("Content", "common.lua");

            // Now load it
            var f = state.DoFile(fp);
        }

我将Player全局设置为this,因为这是玩家类,所以任何新函数都需要在Player的上下文中声明。我做错了什么?


编辑

我已解决之前的错误,但我仍然没有得到我想要的结果。似乎不可能直接这样做;但是我已经阅读过它,在Lua 5.2中,可以通过通过debug.setuservalue将表与userdata对象相关联的方式进行类似的操作。但是,当我尝试使用它时,该表仍然为空(尽管userdata不为空)。

这是我尝试的内容(C#):

        state.NewTable("Player");
        state["tmp"] = this;
        state.DoString("a = debug.setuservalue(tmp, Player)");
        var t = state["Player"]; // Shows that Player is an empty table
        var a = state["a"]; // Shows that a was indeed set.

换句话说,我想能够在脚本中使用self以引用userdata,以及自定义函数,例如Idle()Turn()

如何实现我想要的行为?

点赞
用户2858170
用户2858170

从 Lua 参考手册: https://www.lua.org/manual/5.3/manual.html#2.1

Userdata 值不能在 Lua 中创建或修改,只能通过 C API 进行操作。这确保了主程序拥有的数据的完整性。

因此,您不能将函数添加到 userdata,但可以通过将 userdata 包装在自己的 Lua 表中来编写自己的扩展接口。

假设您可以通过调用 Player() 创建 Player userdata 值的简单示例。

WrappedPlayer = {}
WrappedPlayer.usrdata = Player()
function WrappedPlayer:Idle()
  self.usrdata:Turn()
  self.usrdata.MoveType = MoveType.Idle
end

然后,您可以简单地调用 WrappedPlayer:Idle()

当然,您可以进一步改进此方法,并在将 Player 表重写为自己的表并添加一些元表魔法之后,您只需创建自己的 Player 对象:

local myPlayer = Player()
myPlayer:Idle()

只需阅读一些关于 Lua 面向对象编程的教程即可了解如何操作。

还有一个非常简单的解决方法:

function SetPlayerIdle(player)
  player:Turn()
  -- 还有其他操作
end
2018-03-10 22:25:44