Как говорит Дак, определенную вами таблицу Room вы также возвращаете как «новый» объект Room. Room:method(...)
- это сокращение синтаксиса для Room.method(Room, ...)
. Переменная self
, предоставляемая Lua, содержит ссылку на Room, когда вы используете сокращение, поэтому при вводе Room:new(5, 6, 3, 3)
то, что читает Lua, равно Room.new(Room, 5, 6, 3, 3)
с self = Room
. Когда мы тогда допустим self.width = 5
, то получится Room.width = 5
, и это не то, что мы хотели бы сделать.
Чтобы решить эту проблему, нам нужно создавать новый объект каждый раз, когда вызывается Room:new
. Мы создаем новую таблицу с именем obj и сохраняем в ней значения.
function Room:new(width, height, x, y)
local obj = {
Width = width,
Height = height,
X = x,
Y = y,
Left = x,
Right = x + width,
Top = y,
Bottom = y + height,
}
return obj
end
Это работает хорошо, теперь мы можем создавать комнаты. Но когда мы пытаемся сделать room1:Intersects(other)
, мы получаем ошибку: method Intersects not defined
. Возможно, мы определили новую комнату, но эта комната - не более чем простой стол. Вот где приходят метатаблицы. Проще говоря, метатаблицы определяют поведение таблицы. В нашем случае мы хотим, чтобы это содержало Lua для просмотра, когда он не может найти значение или метод в исходном списке. (См. глава 13 для получения дополнительной информации о метатаблицах.)
Давайте рассмотрим пример:Если бы этаж в каждой комнате был бы одинаковым, мы могли бы либо скопировать этот этаж каждому ребенку, либо указать Room.floor = "Carpet"
и вернуться к этому, когда кто-то спросит нас, каков этаж в определенной комнате. Чтобы вернуться к этому, мы используем метатаблицы и метод __index
. Этот метод - то место, которое Lua будет искать значения, которые он не может найти.
function Room:new(width, height, x, y)
local obj = {
...
}
setmetatable(obj, self) -- self here is equal to Room
self.__index = self
return obj
end
Снова мы создаем объект со всеми значениями, которые мы хотим сохранить для каждой комнаты отдельно. Во-вторых, мы допустим метатаблицу Room
для obj
. Третья строка говорит нам, что таблица Room должна использоваться, когда невозможно найти какие-либо методы. Использование self.__index = self
при создании объекта будет таким же, как при написании Room.__index = Room
при определении самой комнаты, но помогает, когда вы просто копируете один и тот же / похожий код каждый раз :)Если мы определили, что Room.floor = "Carpet"
, результатом print(room1.floor)
будет Carpet, как и ожидалось. Значение room1.floor
равно nil
, тогда Lua будет искать __index
в метатаблице room1, где он обнаружит, что Room может содержать то, что он искал. И действительно, Room.floor
определено, поэтому Луа видит в этом ответ.
Сводка
Вы также можете установить таблицу и использовать метод __index
, чтобы определить объект какты хотел сделать. Я думаю, что это также может быть более эффективным, так как вы не определяете каждую функцию отдельно для каждого объекта того же класса.
Кроме того, если вы хотите, чтобы Room:Intersects
выдавало false, когда вы делаетечто-то вроде room1:intersects(room1)
, просто добавьте self ~= other
. Это удастся, только если self
и other
- это одна и та же таблица, даже если все значения комнаты одинаковы, они не будут равны и пересекаются друг с другом.