Prelude
ComputerCraft - это мод для Minecraft (Forge), который добавляет в игру грубый компьютер, основанный на луа. Используя этот компьютер, можно писать программы для взаимодействия с миром Minecraft различными способами. Вопрос о том, применим ли вопрос ComputerCraft к StackOverflow, ранее обсуждался и в других вопросах, но я считаю, что применимо , так как этот мод, по большей части, предназначен для программирования, и хотя некоторые вызовы проприетарного API ComputerCraft являются В этом вопросе нет понятия, которое не было бы применимо к другим, не связанным с ComputerCraft программам lua (если, конечно, проблема не вызвана ошибкой в самом ComputerCraft). Документацию по используемым API можно найти по адресу http://www.computercraft.info/wiki/Category:APIs.
Примечание: Не пугайтесь, если у вас нет опыта работы с ComputerCraft; Я считаю, что эта проблема может быть совершенно не связана с ComputerCraft, а вместо этого вызвана некоторой запутанностью ООП в lua, которую я не смог понять. Я прокомментировал код, где я счел необходимым объяснить наиболее важные аспекты запатентованных звонков, которые я делаю. Если что-то неясно, пожалуйста, прокомментируйте, и я уточню.
Если вы хотите иметь возможность запускать примеры кода без Minecraft, есть отличный эмулятор ComputerCraft, который называется CCEmuRedux. Я проверил свой код на обоих компьютерах ComputerCraft и CCEmuRedux с одинаковыми результатами, хотя CCEmuRedux, похоже, не поддерживает мониторы. Для просмотра цветов необходим «продвинутый» компьютер.
Задача
В ComputerCraft 1.75 (и CCEmuRedux @ ComputerCraft 1.79) задан следующий класс gui и тестовая программа, которая пытается нарисовать элементарную кнопку в каждом из двух разных окон, используя gui класс, обе кнопки нарисованы во втором окне. Графически результат guiTest.lua равен https://i.imgur.com/llFDlYI.png,, в то время как я ожидаю, что первая (оранжевая) кнопка будет нарисована в окне 1. Хотя у меня есть некоторые теории относительно того, почему она так себя ведет У меня нет необходимого опыта Луа, чтобы понять, как это исправить. Это 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, поэтому я ожидаю, что это будет "легкой" проблемой. У меня есть более чем базовое понимание того, как ООП работает на нескольких других языках (в частности, на Java), и поэтому «Spidey-Sense» моего программиста говорит мне, что любая из переменных, например t , недостаточно «локально» (одна и та же переменная используется обоими окнами) или какая-либо ссылка в одном из объектов gui перезаписывается при создании нового объекта gui .
Поэтому я попытался сделать таблицу gui локальной, чтобы она не перезаписывалась:
local gui = {t, vpx, vpy}
... но он выдал ошибку attempt to index ?
в строке 6 "gui.lua" (setmetatable(o, self)
), поэтому я попытался (понимая, что не смогу получить доступ к функции извне 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 является Исключительно Смущенным ™, но я предполагаю, что суть в том, что «вы не можете сделать метод объекта локальным», как я может сделать другие функции локальными аналогично тому, что я пробовал здесь.
Это не проблема с 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 , приводит к тому, что обе кнопки рисуются в одном и том же окне виртуальной консоли.Почему?