我正在尝试在Lua中创建一个具有功能的对象(“房间”),以检查另一个提供的房间是否与它相交。
我遇到一个问题,无论我作为参数传入的内容总是看起来与我调用该函数的对象相同。
Room = {
Width = 0,
Height = 0,
X = 0,
Left = 0,
Right = 0,
Top = 0,
Bottom = 0
}
function Room:new(width, height, x, y)
self.Width = width
self.Height = height
self.X = x
self.Y = y
self.Left = x
self.Right = self.X + self.Width
self.Top = y
self.Bottom = self.Y + self.Height
return self
end
function Room:Intersects(other)
print("Checking for intersection...")
print("Self X: ", self.X)
print("Self Y: ", self.Y)
print("Other X: ", other.X)
print("Other Y: ", other.Y)
return other.Left < self.Right and self.Left < other.Right and other.Top < self.Bottom and self.Top < other.Bottom
end
room1 = Room:new(5, 6, 3, 3)
room2 = Room:new(10, 16, 5, 9)
intersects = room1:Intersects(room2)
print("Intersects: ", intersects)
输出:
$ lua FunctionTest.lua
Checking for intersection...
Self X: 5
Self Y: 9
Other X: 5
Other Y: 9
Intersects: true
我期望self.X
,self.Y
与other.X
和other.Y
不同。我一直在关注Lua.org关于Object Oriented Programming的章节。
Room是在顶部定义的单个表,所有对Room的引用都作用于该same表,而不是不同的对象。
下面的解决方案创建一个名为'obj'的表,并在每次调用NewRoom()时返回此唯一表。这种方法与您参考的指南的第16.4章大致相似。
function NewRoom(width, height, x, y)
local obj = {
Width = width,
Height = height,
X = x,
Y = y,
Left = x,
Right = x + width,
Top = y,
Bottom = y + height,
}
function obj:Intersects(other)
print("Checking for intersection...")
print("Self X: ", self.X)
print("Self Y: ", self.Y)
print("Other X: ", other.X)
print("Other Y: ", other.Y)
return other.Left < self.Right and self.Left < other.Right and other.Top < self.Bottom and self.Top < other.Bottom
end
return obj
end
room1 = NewRoom(5, 6, 3, 3)
room2 = NewRoom(10, 16, 5, 9)
intersects = room1:Intersects(room2)
print("Intersects: ", intersects)
正如Dahk所说,您定义的Room表是您还作为“新” Room对象返回的表。Room:method(...)
是Room.method(Room, ...)
Lua提供的self
变量在使用速记时包含对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在原始列表中找不到值或方法时查找的对象。 (有关元表的更多信息,请参见chapter 13。)
让我们看一个例子:如果每个房间的地板都相同,我们可以将该地板复制给每个孩子,或者当有人问我们某个房间的地板是什么时,我们让Room.floor = "Carpet"
返回到该位置。回到这一点,我们使用元表和__index
方法。这种方法是Lua将查找找不到的值的地方。
__index
同样,我们使用要为每个房间分别存储的所有值来创建对象。其次,我们让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
的元表。第三行告诉我们在找不到任何方法时应使用Room表。在创建对象期间使用obj
与在定义Room本身时编写self.__index = self
相同,但是在每次仅复制相同/相似代码时会有所帮助:)如果我们已定义Room.__index = Room
,则Room.floor = "Carpet"
的结果将是预期的Carpet。 print(room1.floor)
的值为room1.floor
,那么Lua将在room1的元表中查找nil
,在该表中它发现Room可能包含要查找的内容。实际上,已经定义了__index
,因此Lua认为这就是答案。
您还可以设置一个可界对象,并使用Room.floor
方法来定义所需的对象。我认为这可能还可以提高内存效率,因为您不必为同一个类的每个对象分别定义每个函数。
[此外,在执行__index
之类的操作时,如果您想让Room:Intersects
为假,则只需添加room1:intersects(room1)
即可。仅当self ~= other
和self
是完全相同的表时,才能成功,即使房间的所有值都相同,它们也将不相等并且彼此相交。