2

序幕

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

注意:如果您没有 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_orientation.htm。这是我的第二个基于 lua 的程序,所以我希望它是一个“简单”的问题。不过,我对 OOP 在其他几种语言(尤其是 Java)中的工作方式有一个基本的了解,因此我的程序员的“Spidey-Sense”告诉我,任何一个变量,例如t,都不是“足够本地化的” "(两个窗口都使用相同的变量),或者在创建新的 gui 对象时覆盖其中一个gui对象的某些引用。

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

local gui = {t, vpx, vpy}

...但它attempt to index ?在“gui.lua”(setmetatable(o, self))的第 6 行吐出一个错误,所以我尝试了(意识到我将无法从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 异常混乱™,但我认为它的要点是“你不能将对象方法设为本地”,因为我可以其他以类似于我在这里尝试过的方式在本地运行。

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

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对象在每个虚拟控制台窗口上绘制一个按钮,导致两个按钮被绘制在同一个虚拟控制台窗口。为什么?

4

1 回答 1

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
于 2018-10-27T08:26:06.010 回答