Search code examples
inheritanceluacodea

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.


Solution

  • 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)