xlua における C# オブジェクトの Lua 側操作手法

1. C# オブジェクトを Lua の userdata として扱う

デフォルトでは、C# のオブジェクトは Lua 側で userdata として表現され、実際のデータとメソッド呼び出しはすべて C# 側で処理されます。

function OperateVector3(vecA, vecB)
    vecA.x = 10
    print(type(vecA), vecA.x, vecA.y, vecA.z)
    print("vecA:", vecA, "vecB:", vecB)
    print("vecA + vecB:", vecA + vecB)
    vecA:Set(vecA.x - vecB.x, vecA.y - vecB.y, vecA.z - vecB.z)
    print(vecA)
    print(CS.UnityEngine.Vector3.Normalize(vecA))
end

print("----メタテーブル変更前----")
OperateVector3(CS.UnityEngine.Vector3(1, 2, 3), CS.UnityEngine.Vector3(7, 8, 9))

xlua.genaccessor を用いた直接フィールドアクセス

xlua.genaccessor を使うことで、C# オブジェクトのフィールドに Lua 側から直接アクセス可能になります。これは、オフセットと型サイズを指定して、マネージドメモリ上の値を読み書きする低レベルな方法です。

local readX, writeX = xlua.genaccessor(0, 8)   -- x フィールド: オフセット 0, float (8バイト)
local readY, writeY = xlua.genaccessor(4, 8)   -- y フィールド: オフセット 4
local readZ, writeZ = xlua.genaccessor(8, 8)   -- z フィールド: オフセット 8

local fieldReaders = {
    x = readX, y = readY, z = readZ
}
local fieldWriters = {
    x = writeX, y = writeY, z = writeZ
}

local instanceMethods = {
    Set = function(obj, newX, newY, newZ)
        print("Lua側 Vector3:Set 呼び出し")
        writeX(obj, newX)
        writeY(obj, newY)
        writeZ(obj, newZ)
    end
}

local vector3Meta = {
    __index = function(obj, key)
        if instanceMethods[key] then
            return instanceMethods[key]
        end
        local reader = fieldReaders[key]
        return reader and reader(obj)
    end,

    __newindex = function(obj, key, value)
        local writer = fieldWriters[key]
        if writer then
            writer(obj, value)
        else
            error("未定義フィールド: " .. tostring(key))
        end
    end,

    __tostring = function(obj)
        return string.format("vector3 { %f, %f, %f }", obj.x, obj.y, obj.z)
    end,

    __add = function(a, b)
        print("Lua側 Vector3 加算演算子")
        return CS.UnityEngine.Vector3(a.x + b.x, a.y + b.y, a.z + b.z)
    end
}

xlua.setmetatable(CS.UnityEngine.Vector3, vector3Meta)

print("----メタテーブル変更後----")
OperateVector3(CS.UnityEngine.Vector3(1, 2, 3), CS.UnityEngine.Vector3(7, 8, 9))

2. C# オブジェクトを Lua の table として再実装

別のアプローチとして、C# のクラスを完全に Lua 側のテーブルで置き換えることも可能です。これにより、コンストラクタや静的メソッド、インスタンスメソッドを Lua で自由に定義できます。

local LuaVector3Proto = {}
LuaVector3Proto.__index = LuaVector3Proto

function LuaVector3Proto:__tostring()
    return string.format("struct { %d, %d, %d }", self.x, self.y, self.z)
end

function LuaVector3Proto:SwapXY()
    self.x, self.y = self.y, self.x
end

-- インスタンス生成時のメタテーブル設定
xlua.setmetatable(CS.UnityEngine.Vector3, LuaVector3Proto)

-- クラス(静的)側の Lua 実装
local LuaVector3Class = {}

function LuaVector3Class.__call(_, x, y, z)
    print("LuaVector3: コンストラクタ呼び出し")
    local instance = { x = x, y = y, z = z }
    setmetatable(instance, LuaVector3Proto)
    return instance
end

setmetatable(LuaVector3Class, LuaVector3Class)

function LuaVector3Class.Print(target)
    print("LuaVector3: Print", type(target), target)
end

-- C# の Vector3 クラス全体を Lua 実装に差し替え
xlua.setclass(CS.UnityEngine, "Vector3", LuaVector3Class)

-- 使用例
local v = CS.UnityEngine.Vector3(1, 2, 3)
print(type(v), v)
CS.UnityEngine.Vector3.Print(v)
v:SwapXY()
print(v)

タグ: xlua lua C# Unity metatable

8月20日 19:18 投稿