【问题标题】:Lua (CC) GUI class draws all components in the same window when told to draw them in separate windowsLua (CC) GUI 类在被告知在单独的窗口中绘制所有组件时在同一窗口中绘制它们
【发布时间】:2018-10-27 16:34:33
【问题描述】:

前奏

ComputerCraft 是 Minecraft (Forge) 的一个模组,它在游戏中添加了一个基于 lua 的原始计算机。使用这台计算机,人们可以编写程序以各种方式与 Minecraft 世界进行交互。 ComputerCraft 问题是否适用于 StackOverflow 之前已在其他问题中进行过辩论,但我相信它适用的,因为 mod 在很大程度上是关于编程的,而一些 ComputerCraft 专有 API 调用制作完成后,这个问题中没有任何概念不适用于其他与 ComputerCraft 无关的 lua 程序(当然,除非问题是由 ComputerCraft 本身的错误引起的)。可以在http://www.computercraft.info/wiki/Category:APIs 找到所用 API 的文档。

注意:如果您没有 ComputerCraft 经验,请不要惊慌;我相信这个问题可能与ComputerCraft完全无关,而是由我未能掌握的lua中的一些复杂的OOP引起的。我已经对我认为有必要解释我正在进行的专有调用的最重要方面的代码进行了注释。如果有什么不清楚的地方,请评论,我会澄清的。

如果您希望能够在没有 Minecraft 的情况下运行代码示例,可以使用名为 CCEmuRedux 的出色 ComputerCraft 模拟器。我已经在实际的 ComputerCraft 和 CCEmuRedux 上测试了我的代码,结果相同,尽管 CCEmuRedux 似乎不支持监视器。需要一台“高级”计算机才能看到颜色。

问题

在 ComputerCraft 1.75(和 CCEmuRedux @ ComputerCraft 1.79)中,给定以下类 gui,以及一个尝试使用 gui 在两个不同窗口中的每个窗口中绘制基本按钮的测试程序 类,两个按钮都在第二个窗口中绘制。从图形上看,guiTest.lua 的结果是https://i.imgur.com/llFDlYI.png,而我希望在窗口 1 中绘制第一个(橙色)按钮。虽然我有一些关于它为什么会这样行为的理论,我没有必要的 lua 经验来弄清楚如何解决它。这是 MWE。

代码示例

gui.lua

--Meta class
gui = {t, vpx, vpy}

function gui:new(t, title) -- I'm aware this constructor is not in keeping with the referenced Tutorialspoint article, it is of no consequence in this example
    local o = o or {}
    setmetatable(o, self)
    self.__index = self
    self.t = t
    local sX, sY = self.t.getSize() -- get the size of the virtual terminal and save it to vpx, vpy
    self.vpx = sX
    self.vpy = sY
    self.t.setCursorPos(1, 1) -- put cursor at the start of the virtual terminal
    self.t.write(tostring(title)) -- note that this WORKS, it prints one title per Window as seen in the screenshot
    return o
end

function gui:drawButton(x, y, sX, sY, colour)
    self.t.setCursorPos(x, y) -- set the cursor to the button's first x- and y-coords
    self.t.setTextColor(colours.black) -- set text colour to black
    self.t.setBackgroundColor(colour) -- set background colour to the colour of the button
    for iY = 1, sY do 
        for iX = 1, sX do
            self.t.write("#") -- print hashtags to represent the button until we reach sX and sY
        end
        self.t.setCursorPos(x, y + iY) -- move cursor a line down, and back to button's first x-coord
    end
    self.t.setCursorPos(self.vpx, self.vpy) -- get cursor out of the way so the screenshot will be prettier
end

guiTest.lua

dofile('gui.lua')

local w1 = window.create(term.current(), 2, 2, 22, 15)
local w2 = window.create(term.current(), 26, 2, 22, 15) -- creates virtual windows in a terminal, acting as terminals of their own
-- window.create() arguments: terminal object to create window on, x position, y position, x size, y size

local g1 = gui:new(w1, "Window 1") -- create gui object for the first window
local g2 = gui:new(w2, "Window 2") -- create gui object for the second window

g1:drawButton(5, 3, 3, 2, colours.orange) -- should draw in w1, draws in w2
g2:drawButton(10, 8, 4, 4, colours.green) -- should draw in w2, draws in w2

尝试的解决方案

不管怎样,我一直在关注 Lua OOP 配方@@https://www.tutorialspoint.com/lua/lua_object_oriented.htm。这是我的第二个基于 lua 的程序,所以我希望它是一个“简单”的问题。不过,我对 OOP 在其他几种语言(尤其是 Java)中的工作方式有一个基本的了解,因此我的程序员的“Spidey-Sense”告诉我,要么是一些变量,比如 t , 不是“足够本地化”(两个窗口都使用相同的变量),或者当创建新的 gui 对象时,其中一个 gui 对象中的某些引用被覆盖.

因此,我尝试将表 gui 设为本地,以确保它不会被覆盖:

local gui = {t, vpx, vpy}

...但它在“gui.lua”的第 6 行 (setmetatable(o, self)) 上吐出了错误 attempt to index ?,所以我尝试了(意识到我将无法从 gui 外部访问该函数.lua,因为它是本地的):

local function gui:drawButton(x, y, sX, sY, colour)

...导致guiTest.lua:1: bios.lua:14 [string "gui.lua"]:17:'(' expected。第 17 行是上面代码标签中gui:drawButton() 的定义。在我公认的有限的 ComputerCraft 经验中,这种格式不正确的错误消息通常意味着 lua 解释器或 CraftOS 是 Exceptionally Confused™,但我认为它的要点是“你不能将对象方法设为本地”,因为我 可以以类似于我在这里尝试的方式将其他函数本地化。

window.create() 或一般使用窗口 API 都不是问题,因为在使用单独的监视器而不是在同一监视器上使用单独的窗口时会发生同样的事情。本质上:

dofile('gui.lua')

local w = window.create(term.current(), 2, 2, 22, 15)
local m = peripheral.wrap('top') -- m becomes the Monitor physically on top of the ComputerCraft Computer

local gw = gui:new(w, "Window") -- create gui object for the Window
-- m is a terminal object, just like w, so we can still do
local gm = gui:new(m, "Monitor") -- create gui object for the Monitor

gw:drawButton(5, 3, 3, 2, colours.orange) -- should draw in w, draws in m
gm:drawButton(10, 8, 4, 6, colours.green) -- should draw in m, draws in m

也许有一种方法可以将函数存储为局部变量,类似于

local gui:printFoo = function() print("foo") end 
self:printFoo() -- prints "foo"...?

...或者更可能的是,我完全错过了这个问题。

结论

为了简化一个长问题,定义两个 gui 对象,一个用于两个虚拟控制台窗口中的每一个,并尝试使用它们各自的 在每个虚拟控制台窗口上绘制一个按钮gui 对象,导致两个按钮被绘制在同一个虚拟控制台窗口上。为什么?

【问题讨论】:

    标签: oop lua computercraft


    【解决方案1】:

    是的,Lua 中的 OOP 对于 Lua 初学者来说很难,尽管他们非常了解 OOP 语言(如 Java)。

    --Meta class
    gui = {}  -- class is a global variable, no default properties exist
    
    function gui:new(t, title)   -- t = window, self = your class "gui"
        local o = {}   -- creating NEW object
        setmetatable(o, self)  -- link the object with the class
        self.__index = self
        o.t = t        -- save window into object (not into class)
        local sX, sY = t.getSize() -- get the size of the virtual terminal
        o.vpx = sX  -- save window's properties into object (not into class)
        o.vpy = sY
        t.setCursorPos(1, 1)
        t.write(tostring(title)) 
        return o
    end
    
    function gui:drawButton(x, y, sX, sY, colour)  -- self = object
        ....
    end
    

    【讨论】:

    • 是的,这确实很神奇!那么我是否正确地推测我正在以迂回的方式为类的对象设置 default 值,其效果与例如gui = {t = m} 的效果大致相同?我将如何从 gui 的另一个方法中调用 gui 的方法(例如,调用gui:drawButton() 的构造函数)?还是self:drawButton()吗?
    • 是的,类中的属性是该类的每个对象的默认属性。在类方法内部(例如构造函数new),你的对象是o,所以它是o:drawButton()。内部对象方法(如drawButton),你的对象是self,所以它是self:otherMethod()
    猜你喜欢
    • 2023-02-09
    • 2020-09-05
    • 2011-08-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-01
    • 2012-11-04
    相关资源
    最近更新 更多