我正在尝试在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关于面向对象编程的章节。
Room是在顶部定义的单个表,对 Room 的所有引用都作用于同一个表而不是不同的对象。
下面的解决方案创建一个名为"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 读取的内容与self = Room
Room.new(Room, 5, 6, 3, 3)
。当我们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
的元表。第三行告诉我们,当找不到任何方法时,应使用房间表。
在创建对象期间使用self.__index = self
与定义 Room 本身时编写Room.__index = Room
相同,但当您每次都复制相同/相似的代码时会有所帮助:)
如果我们定义了该Room.floor = "Carpet"
,print(room1.floor)
的结果将是预期的 Carpet。room1.floor
的值是nil
,然后 Lua 将在 room1 的元表中查找一个__index
,在那里它发现 Room 可能包含它正在寻找的内容。事实上,Room.floor
是被定义的,所以这就是Lua认为的答案。
总结
您还可以设置一个 metable 并使用__index
方法根据需要定义对象。我认为它也可能更有效的内存效率,因为您不会为同一类的每个对象单独定义每个函数。
另外,如果您想在执行类似room1:intersects(room1)
之类的操作时Room:Intersects
给出 false,只需添加self ~= other
即可。只有当self
和other
是完全相同的表时,这才会成功,即使房间的所有值都相同,它们也不会相等并且相互交叉。