1

I'm creating some UI for a game I'm currently working on in C#, and want to expose everything down to Lua so that my artist can make small tweaks without needing to do anything in code. I'm using MoonSharp for integrating Lua scripts into my project.

Here is what I currently have for my UIElement wrapper class:

UIElement = {};
UIElement.__index = UIElement;

setmetatable( UIElement, {
 __index = function( self, key )
  local codeElement = rawget( self, "__codeElement" );
  local field = codeElement and codeElement[key];
  if type( field ) == "function" then
   return function( obj, ... )
    if obj == self then
     return field( codeElement, ... );
    else
     return field( obj, ... )
    end
   end;
  else
   return field;
  end
 end,
 __call = function( cls, ... )
  return cls.new( ... );
 end,
} );

function UIElement.new()
 local self = setmetatable( {}, UIElement );
 self.__codeElement = BLU_UIElement.__new();
 return self;
end

BLU_UIElement is my C# class which is exposed to Lua via the MoonSharp API. It works properly when working directly with the object, and has functions like SetPos, SetColor, etc.

UIElement is intended to be my "class" in Lua to wrap and extended my C# object.

When I instantiate a UIElement elsewhere in script and attempt to call a function (SetPos for example), it does correctly get into the __index function. However, the rawget call always returns nil. It doesn't seem specific to the BLU_UIElement either. I have already tried something very simple like adding a string ID value in the constructor and trying to rawget it in the __index function, but it also returns nil.

I'm assuming I'm just doing something incorrectly setting up the metastable on either the class or the object itself, but I'm not exactly sure where the problem lies. I've been looking here: http://lua-users.org/wiki/ObjectOrientationTutorial for an idea on what I'm doing wrong, but nothing jumps out on me.

I appreciate any guidance on this, I've been looking at this for a couple days without figuring it out, and searching online generally just shows similar code to what I am already doing.

2 Answers2

0

I got to admit that I am not entirely sure, what you are trying to achieve by writing your wrapper class in LUA and not C# and then expose that type but I noticed this:

For me NativeClass.__new() never worked out in MoonSharp like you are trying to do it at

self.__codeElement = BLU_UIElement.__new();

For this reason I create custom constructor-functions for my native classes and pass them to to the global namespace as delegates (Its type has to be registered though). It looks a lot like you would normally construct an object. Just without the new keyword:

In C#

public NativeClass{

   public static NativeClass construct()
   {
      return new NativeClass();
   }

}

Pass the static method as delegate to the script:

script["NativeClass"] = (Func<NativeClass>)NativeClass.construct;

Then you can create a new Instance like this in MoonSharp:

x = NativeClass()

EDIT: So didn't read that you have tried to do this with a string. Maybe you should consider not writing a wrapper class in LUA but in C# or is there a reason that forbids this?

  • I wanted to do the bulk of the work in Lua for two reasons. The big one is that it makes the UI system a little more modular, so if we change engines or languages, the only thing that needs to change is the one core class defined in C#, while all the other things defined in Lua remain unchanged. The second reason is that I am working with some non-coders on my team. They are open to editing Lua files and creating new classes or functions to fit their needs. However they get nervous when someone asks them to touch code. – ProfessionalKent May 15 '19 at 06:12
0

I had a friend who is far more experienced with Lua metatables than I am take a look. Posting the answer here in case it helps anyone else.

The issue was that I was trying to use the UIElement table as both the "class" table as well as the "object" metatable. When calling rawget inside the __index function, it was attempting to find things in the UIElement table instead of the self table created in UIElement.new(). Splitting these two into distinct tables (one for the class, one for the object metatable) fixed things.

Here is my updated and working code:

UIElement = {};
setmetatable( UIElement, {
    __call = function( cls, ... )
        return cls.new( ... );
    end,
} );

UIElement.objectMetaTable = {
    __index = function( self, key )
        local objectValue = rawget(self, key);
        if objectValue ~= nil then
            return objectValue;
        end

        local classValue = UIElement[key];
        if classValue ~= nil then
            return classValue;
        end

        local codeElement = rawget(self, "__codeElement");
        if codeElement then
            return codeElement[key];
        end
    end,
};

function UIElement.new()
    local newInstance = setmetatable( { id = "blah" }, UIElement.objectMetaTable );
    newInstance.__codeElement = BLU_UIElement.__new();
    return newInstance;
end