Corona LUA和面向对象设计

7

我来自传统的游戏开发,使用面向对象编程(OOP)原则。据我所见,一旦你知道该怎么做,就可以使用LUA模拟这种方法。在一些代码发布中,我发现了如何使用director类以及创建具有new()函数等的文件。

我正在寻找一种管理武器的方法。我有一个玩家和一个对手,我希望有一个武器类,比如weaponCanon。我所做的是:

-- private vars here
local power
local canonSprite
local whatever

local someFunction = function()
...
end

-- Private stuff here
local weaponCanon = {}

weaponCanon.fire = function(atX, atY)
...
end

weaponCanon.reset = function()
...
end

return weaponCanon

然后在我的级别代码中,我只需要这样做:
local weaponCanon = require("weaponCanon")
weaponCanon.fire(100, 100)

这很有用,使我能够在编写武器时使用“私人”和“公共”思维方式。但问题是如果我想让玩家和对手都有一个大炮:

local playerWeapon = require("weaponCanon")
local opponentWeapon = require("weaponCanon")

这只是简单地返回同一个对象,而不是一个新实例对象。这意味着在对手武器位置上我只得到了一个weaponCanon。显然这不是我想要或需要的。
我们的游戏中有很多武器,只有每个文件的一个版本并且设置告诉我们它是否是对手的武器或玩家的武器,将会很好。另一种选择是复制每个文件并创建weaponPlayerCanon和weaponOpponentCanon,但是当我想要修改一个文件并且每次都必须更改2个或更多文件时,我感到非常沮丧。
如何让它返回实例对象以及LUA文件的结构是什么?
感谢任何帮助!
-d

我非常清楚:http://lua-users.org/wiki/LuaModuleFunctionCritiqued,这是我设计的灵感来源,但它并没有真正解决我的问题,除非我漏看了什么... - David Nelson
我认为你错过了的是你需要数据和逻辑。因此,你需要一个新函数来提供“表”来包含武器的设置,然后将其作为参数提供给fire函数,以便它会按照传递给它的表减少枪的能量值。 PIL的第15章应该会帮助你找到正确的方向 http://www.lua.org/pil/15.html。 - Jane T
你在这里所做的告诉我一些东西,我本来也打算最终测试一下,所以感谢你帮我省去了麻烦。 - jhocking
5个回答

7
如果以后您需要继承(即LaserCannon是Weapon的子类),那么您可能需要更深入地使用元表。
有很多库可以让您在Lua之上进行“oop”。您可以在这里看到一个非常好的列表:

http://lua-users.org/wiki/ObjectOrientedProgramming

我是middleclass的作者。使用我的库,您需要执行如下操作:

local Weapon = class('Weapon')

function Weapon:initialize(a,b,c)
  self.x,self.y,self.z = a,b,c
end

function Weapon:fire(x,y)
 ...
end

"LaserCannon" 很容易实现 - 只需向类传递第二个参数即可:
local LaserCannon = class('LaserCannon', Weapon)

function LaserCannon:initialize(a,b,c,d)
  self.w = d
  Weapon.initialize(self, a,b,c) -- superclass' constructor
end

function LaserCannon:foo()
 ...
end

您可以像这样使用它:

require 'middleclass' -- so you can use "class"
LaserCannon = require 'laser_cannon'

local playerWeapon = LaserCannon:new() -- a laser
local opponentWeapon = Weapon:new() -- a regular generic weapon

opponentWeapon:fire(100,200) -- typical use
playerWeapon:fire(100, 200) -- LaserCannon inherits fire from Weapon
playerWeapon:foo() -- LaserCannon-exclusive

这是使用middleclass编写的代码,这也是我喜欢它的原因,因为我是它的开发者。之前提到的页面上的其他库也提供类似的功能。

我一直在研究你的库,非常喜欢它。我可能很快就会转换过去。我已经进行了一些测试,但遇到了一个巨大的问题。我正在使用它来为Corona SDK编写代码,并遇到了这样一种情况:当我需要设置onCollision函数时,我会遇到问题。通常我只需执行以下操作:self.canonBall.collision = onWeaponCollision self.canonBall:addEventListener ("collision", self.canonBall )然而,onWeaponCollision不起作用。是否有一种方法可以在您的库中实现此功能?阅读更多: https://dev59.com/XVjUa4cB1Zd3GeqPP0mi - David Nelson

5
我猜你正在尝试使用源文件建模一个类。这意味着你应该有一个函数来创建该类的新实例,除非你希望它们共享所有状态。
大致如下(未经测试):
local WeaponCannon = {}
WeaponCannon.__index = WeaponCannon
function WeaponCannon:new()
  return setmetatable({}, self)
end

function WeaponCannon:fire(x, y)
  -- Do something via the self reference and arguments (x, y)
end

return WeaponCannon

在您的调用代码中(同样未经过测试):
require('WeaponCannon')
local playerWeapon = WeaponCannon:new()
local opponentWeapon = WeaponCannon:new()

非常感谢大家的回复,我觉得所有的解决方案都很棒。这个解决方案让我找到了我想做的事情。谢谢! - David Nelson
嘿,在这个例子中,你如何使用私有变量/函数?或者除非我使用库,否则不存在吗? - David Nelson
如果它们是不带状态的实用函数,只需将它们放在源文件中,不必返回它们。您未在表中公开的任何局部变量也应为私有。例如:function f() local a = 5; return { getA = function() return a end, setA = function(new_a) a = new_a end } end。局部变量a仅通过getAsetA公开。 - ponzao

1

虽然您为武器对象创建了一个新表,但您并没有创建新变量。在模块顶部声明的任何变量都是静态变量(即所有类实例共享的变量)。要创建仅适用于该对象的变量,您需要在表中创建它们,例如:

weaponCannon = {}
weaponCannon.power = 10

无论如何,您只需创建一个对象,您需要一个“构造函数”来创建表:

function new()
  local weaponCannon = {}
  weaponCannon.power = 10
end


顺便提一下,还有两件事情与您的答案没有直接关系,但可以对您的代码进行非常有用的修改。首先,使用冒号而不是句号来调用方法中的函数将允许您在方法内部使用“self”关键字,例如:

function weaponCannon:fire()
  --this is only a test
  print(self.power)
end

那么

local playerWeapon = require("weaponCanon")
playerWeapon:fire()

其次,您实际上可以将显示对象用作表格,而无需创建空表,然后将显示对象放入其中:
weaponCannon = display.newImage("cannon.png")
weaponCannon.power = 10

请注意,如果这样做,您将无法设置元表。我发现这种方法看起来更合乎逻辑,而且我自己也不喜欢使用元表,但这是您的决定。

0
这里没有对象 - 你只有一堆全局数据。你真的需要创建实例。
function NewWeapon(arg)
    return {
        fire = function(self, atX, atY) 
            print(self.var)
        end,
        var = arg,
    }
end

NewWeapon(3):fire(1, 2)
NewWeapon(7):fire(3, 5)

0

我喜欢ponzao的回答。但是我想对它进行改变:

local WeaponCannon = {}

function WeaponCannon:new()
  local instance = {}
  setmetatable(instance, {__index = WeaponCannon})
  -- setup your new instance here
  return instance
end

function WeaponCannon:fire(x, y)
  -- Do something via the self reference and arguments (x, y)
end

return WeaponCannon

在你的调用代码中:

local WeaponCanon = require('WeaponCannon')
local playerWeapon = WeaponCannon:new()
local opponentWeapon = WeaponCannon:new()

我所做的更改:

  • 创建本地实例变量以允许在返回之前进行设置
  • 更紧凑的设置元表的方式
  • 在调用代码时使用类的变量

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接