将对象传递给Lua函数始终引用自身

问题描述 投票:1回答:2

我正在尝试在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.Xself.Yother.Xother.Y不同。我一直在关注Lua.org关于Object Oriented Programming的章节。

lua
2个回答
2
投票

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)

2
投票

正如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 ~= otherself是完全相同的表时,才能成功,即使房间的所有值都相同,它们也将不相等并且彼此相交。

© www.soinside.com 2019 - 2024. All rights reserved.