IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    监视 Lua 对象的修改

    云风的 BLOG发表于 2024-06-11 10:12:07
    love 0

    我正在制作的游戏 demo 中,所有对象逻辑上都存在于二维空间,但在 Ant Engine 中通过 3d 渲染方式绘制出来。

    我希望有一组简便的 API 方便我控制这些对象的渲染,只是控制它们的位置以及在 Y 轴上的旋转量。Ant Engine 是用场景组件来控制 entity 渲染时的空间状态,但场景节点使用的是 3d 空间的 SRT 即缩放、旋转、位移。而我只需要控制其中的两个坐标轴上的空间位置以及一个旋转轴上的旋转量,直接修改 SRT 太不方便了。而且,使用引擎时,还需要每帧标记被修改过的场景组件对应的 entity ,这也很麻烦。

    在 ECS 结构下,最简单的方式是为这些 entity 创建一个额外的组件,里面有 x y r 三个值。通过一个 system 把它们转换到场景节点在 3d 空间下的 SRT 组件中。但如果每帧都全部转换一次显得多余,毕竟大部分 entity 不是每帧都会发生变化的。

    我用了一个简单的 Lua 技巧来方便开发,下面便是代码:

    local monitor = {}
    
    local function new_type()
        local changes = {}
        local function touch(obj, k, v)
            local raw = obj.__raw
            changes[raw] = true
            raw[k] = v
            raw.__newindex = obj
        end
        local function new (obj)
            obj.__index = obj
            obj.__newindex = touch
            changes[obj] = true
            return setmetatable({ __raw = obj }, obj)
        end
        local function next_raw(t, key)
            local nkey, nvalue = next(t, key)
            if nkey then
                nkey.__newindex = touch
                return nkey, nvalue
            end
        end
        local function pairs()
            if next(changes) then
                local t = changes
                changes = {}
                return next_raw, t
            else
                return next, changes
            end
        end
        return { pairs = pairs, new = new }
    end
    
    local types = setmetatable ({}, {
        __index = function(self, name)
            local t = new_type()
            self[name] = t
            return t
        end })
    
    function monitor.new(typename)
        return types[typename].new
    end
    
    function monitor.pairs(typename)
        return types[typename].pairs()
    end
    
    TEST = true
    
    if TEST then
        local a = monitor.new "test" { x = 1, y = 2 }
        local b = monitor.new "test" { x = 10, y = 20 }
    
        local function flush()
            print "====="
            for obj in monitor.pairs "test" do
                print(obj.x, obj.y)
            end
        end
    
        a.x = -1
        flush()
        b.y = -20
        flush()
        local c = monitor.new "test" { x = 0, y = 0 }
        flush()
    else
        return monitor
    end
    

    从最后的 test 代码可见:我们可以通过 monitor.new "typename" {}创建一个逻辑上有 x y 坐标的 lua 对象,它并不需要是 ECS 的组件,在和 ecs 结合使用的时候,可以把 eid 也放进对象里(在后面遍历的时候,可以对应到 ecs 中的 entity )。当我们后续修改这些对象时,会把修改过的对象标记在内部一张表中。

    通过 for obj in monitor.pairs "typename" 可以遍历所有最近修改过(及新创建)的对象。



沪ICP备19023445号-2号
友情链接