I'm loving PICO-8 with one exception: I'm not crazy about Lua. I've read the Lua documentation for OOP, but the table thing is so different and I can't find examples for what I'm trying to do.
I'm almost finished something I'm working on, but the larger the program gets the more I miss being able to separate the logic into objects (the way I'm used to anyway).
Long story short, I wanted to do the following:
Entity Class:
- x, y, width, height, speed, etc
- function update()
- function draw()
Player Class(that inherits from the entity class):
- score, weapon, power, etc
- function update(overrides but calls super function from entity)
- function draw(same)
Enemy Class(that inherits from the entity class):
- extra variables
- overridden functions calling super functions Entity class
SpecialEnemy Class(that inherits from Enemy/and then from Entity obviously)
- extra variables
- overridden functions calling super functions from Enemy class
I hope that makes sense. It looks like Lua doesn't really have a natural way to do multiple inheritance. Anyway, does anyone have any tips for how to implement something like this? Right now, I everything is just procedural.
Thanks.
I have a little library that implements classes that I use in my games (you can find them on my profile). You can cut-and-paste it from the start of the code if you view the code with the drop-down below the web player.
However, my library does not implement inheritance. I found that implementing inheritance through table/metatable delegation required too much Pico-8 CPU time to be practical. In practice I've not found the lack of inheritance to be much of a limitation, because in Lua (unlike Java or C++) you don't need to use inheritance for objects to have the same type. As long as they have properties or methods with the same name they are interchangeable. I implement common behaviour as functions rather than in base classes.
second @dredds comment, lua is more about composing functions working on a known properties.
That fact that A inherits from B is not mandatory.
I'm no expert on Lua and also pretty new to PICO-8, so take anything I post with a grain of salt. (1st post here)
Firstly, from your example, I'm assuming you want "multilevel" inheritance with override behavior, not multiple inheritance.
(If you need actual multiple inheritance, then I think you may be missing the point of PICO-8. The constraints force us to find creative solutions to all aspects, including both technical and design-wise. It's part of the charm.)
I haven't stress-tested for performance (as @dredds mentioned), but here goes.
Multilevel Inheritance w/ Override:
----------------- -- inheritance -- ----------------- -- clone() -- deep-clone objects function clone(t) --cred: harraps (pico-8 forums) local cln if type(t) == 'table' then cln = {} for k,v in pairs(t) do cln[k] = clone(v) end else cln = t end -- return cln end -- inherit() function inherit(p,nname) local c = clone(p) c.parent = p c.name = nname --just nil if unspecified -- return c end -- override() -- creates a c._func() from c.func() -- ...overwrites if pre-exists -- ...assumes: valid c.parent function override(c,func) if type(c[func]) == 'function' and type(c.parent[func]) == 'function' then c['_' .. func] = c.parent[func] end end -- |
Usage Example:
------------- -- example -- ------------- --parent p = {name='parent',parent=nil} -- function p:echo() print(self.name) end -- p:echo() print('') --child c = inherit(p,'child') -- override(c,'echo') function c:echo() self:_echo() print(self.name .. ' echo') end -- c:echo() --p:echo() ---- --c.parent:echo() print('') --grandchild g = inherit(c,'grandchild') -- g:echo() --c:echo() --p:echo() ---- --g.parent:echo() --g.parent.parent:echo() print('') -- |
Note that the 'parent' attribute is required for this to work. The 'name' isn't, but it was useful for the examples. Originally used it for some prototype-table stuff that's irrelevant here.
Also, credit to @harraps for the clone() function from this thread:
https://www.lexaloffle.com/bbs/?tid=2951
If anyone tests this out in an actual game or tech demo, I'd very much like to hear how it fares in terms of performance hit.
I think the attributes are simply copied by value instead of tracing an actual inheritance chain up. And the behaviors seem to use function pointers, which point directly to the function itself instead of being a chain of delegations.
Overridden functions will still involve a chain of calls if you want to stack behavior, though, as it involves calling on the original behavior from inside of the the new definition. A side benefit is that you can have the original behavior execute at any point within the new definition instead of being fixed at the start. Just move the [ self._func() ] call accordingly.
Thanks for the feedback; it was a great help!
As pointed out by @apirux, I was wanting "multilevel" not "multiple" inheritance. But, as both @dredds and @freds72 pointed out, Lua is flexible enough that I don't need the hierarchy that I thought I did.
Also, it took a few cracks at how I wanted to make new "objects" (I've seen a few different approaches), but I decided the following:
function new_object() object={ --vars } object.update=function(self) --update logic if object.dead then del(objects,self) end end object.draw=function(self) --draw logic end add(objects,object) end |
So I've started doing this for all my "things". The main reason I wanted this approach was because I was constantly having to jump long distances in my code to go from "update" and "draw" functions with if statements determining the type of objects being acted upon. And, as you guys suggested, I don't need all enemies (for example) to be an "enemy" object... as long as each object has the same necessary functions, I'm all set.
Thanks again.
[Please log in to post a comment]