【发布时间】: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