Joseph Weissman
Joseph Weissman

Reputation: 5717

Can inheritance be used with Codea?

Is it possible to use inheritance with Codea? While I am fairly new to Lua, but from some quick Googling, it looks like the methods to do inheritance and polymorphism are a bit "involved". Is there any technique that can be used safely with Codea's Lua-hosting engine?


Here is a simple runnable test I am trying to get working. My superclass:

Superklass = class()

function Superklass:init(x,y)
    self.x = x
    self.y = y
end

function Superklass:debug()
    print(string.format("(%d, %d)", self.x, self.y))
end

A subclass:

Ship = class()

function Ship:init(x, y)
    -- you can accept and set parameters here
    print('ship:init() called')

    self = Superklass(x,y) -- ???
    print('attempting to call self:debug()')
    self:debug() -- works! prints  
    print('ok!')
end

function Ship:draw()
    print('ship:draw() called')
    
    print('attempting to call self:debug()')
    self:debug()
    print('ok')
end

And the program entrypoint:

-- initial setup
function setup()
    ship = Ship(HEIGHT/2, WIDTH/2)
end

-- called once every frame
function draw()
    ship:draw()
end

Here's the output from running that:

ship:init() called
attempting to call self:debug()
(384, 375)
ok!
ship:draw() called
attempting to call self:debug()
error: [string "Ship = class()..."]:16: attempt to call method 'debug' (a nil value)
Pausing playback

I am sure this is incredibly naive -- but I would love a tip in the direction of something that might work in the context of Codea.

Upvotes: 0

Views: 556

Answers (2)

Joseph Weissman
Joseph Weissman

Reputation: 5717

Just to give a working solution (class inheritance in Codea) and point out a few of the pitfalls. First, I should note that Codea loads classes in tab order, so the tabs for the superclasses have to be prior to the tabs for the subclasses. I would recommend looking through this thread on the Codea forums. It discusses the technique below, and also gives some insight into the underlying mechanics.

First, we define a superclass AbstractSprite whose constructor takes an (x,y) coordinate. It provides a method debug to echo this coordinate to the console.

AbstractSprite = class()

function AbstractSprite:init(x,y)
    self.position = vec2(x,y)
    
    print("new AbstractSprite created")
end

function AbstractSprite:debug()
    print(string.format("(%d,%d)", self.position.x, self.position.y))
end

Next, we define an implementing class Ship, which implements a custom debug that calls super, demonstrating how to move up the class hierarchy.

Ship = class(AbstractSprite)

function Ship:init()
    AbstractSprite.init(self)
    print("new Ship created")
end

function Ship:debug()
    print("I am a ship, calling my superclasses' methods!")
    AbstractSprite.debug(self)
end

The program entrypoint. We create both a Ship and a 'raw' AbstractSprite, calling debug on each:

function setup()
    ship = Ship()
    ship:debug()
    
    asteroid = AbstractSprite(150, 200)
    asteroid:debug()
end

And console output:

new AbstractSprite created
new Ship created
I am a ship, calling my superclasses' methods!
(0,0)
new AbstractSprite created
(150,200)

Upvotes: 3

kikito
kikito

Reputation: 52641

Disclaimer: I'm the author of middleclass, an OO-lib for Lua.

The closure-based you pointed out on the first example is a nice intellectual exercise, but it has little practical value. In Lua, object orientation is better achieved with tables (as the example pointed out by your second example suggests) - it gives equivalent functionality at a faster rate. The only differences are syntactical.

There are lots of libraries that use the standard table-based approach to do OO in Lua. You may find a list here:

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

Using middleclass, your code would become like this:

require 'middleclass'

-- middleclass needs the class name as a parameter
SuperClass = class('SuperClass') -- I don't see the point of using a K here, BTW

function SuperClass:initialize(x,y) -- initialize instead of init
    self.x = x
    self.y = y
end

function SuperClass:debug()
    print(string.format("(%d, %d)", self.x, self.y))
end
--

Ship = class('Ship', SuperClass) -- notice that we put the superclass here

function Ship:initialize(x, y) -- initialize instead of init again
    -- you can accept and set parameters here
    print('ship:initialize() called')

    self = SuperClass.initialize(self,x,y) -- notice the extra self and initialize here
    print('attempting to call self:debug()')
    self:debug() -- works! prints  
    print('ok!')
end

function Ship:draw()
    print('ship:draw() called')

    print('attempting to call self:debug()')
    self:debug()
    print('ok')
end

Upvotes: 2

Related Questions