Hi, I find myself doing lots of OOP in pico-8 just as I find it a bit more manageable in my head even if it isn't token efficient. I used to create objects with the following method. As a slightly aside point what does cls in the setmetatable call stand for or do? I never worked it out.
setmetatable(actors, { __call = function (cls, ...) local self = setmetatable({}, cls) self:_init(...) return self end, }) function actors:_init(type,x,y,xoffset,yoffset,xoffset2,yoffset2,health) --actors initilization function sets all of it's parameters return self end add(actorTable,actors(--parameters)) |
Recently however I have been creating them with the following method.
function createenemy(x,y,path) local enem = {} --Enemy variables and methods are initialized enem.update = function(this) end enem.hit = function(this) end enem.draw = function(this) spr(this.sprite,this.x,this.y) end add(enemy,enem) add(objects,enem) end |
Is one of these better than the other? The latter feels simpler at least to me but are there any performance differences or style issues?
I only just started programming in Lua/Pico-8 but also use OOP a lot. Although OOP in Lua is a bit expensive token-wise, I exploit inheritance a lot in my current game, and I feel that with the re-use this enables, the ratio "functionality realized"/"tokens used" is good enough. Either way, I find it most natural way to program as well.
I use (variants of) both approaches. A drawback of the second approach is that it requires more memory. Class method implementations are not shared between instances of the same class. The code looks more natural though and is less verbose. Furthermore, private members can be defined as locals in the create function, which makes them really private and saves tokens (no need to prefix them with "this.")
As far as I know, you cannot implement inheritance using the second approach, so if you want to use that, you have to resort to the first approach.
So, I tend to use the second approach for classes that have only one/a few instances at a time, where I do not need inheritance (e.g. Game, Level) and the second for all other in-game objects (Enemy, Mover, Object, Tile, etc)
Btw, "cls" is a shorthand for class.
LUA OOP really shines for large projects...where token will hit you hard!
Suggest an hybrid approach, using the object:method call to support method inheritance:
— base constructor function make_object(x,y) local c={ x=x,y=y, update=update_object } return c end function make_plane(x,y) local c=make_object(x,y) c.update=update_plane return c end function update_object(self) self.x+=1 self.y+=2 end function update_plane(self) — call super function update_object(self) — do smthg plane specific... end — create a plane local p=make_plane(5,7) p:update() — same as p.update(p) but prettier! |
Okay cool. As a note you can also do inheritance in the second approach by returning the object you create and then having classes that inherit from that parent class call the parents initialization function and set their local obj to that before doing the changes necessary.
function createBullet(x,y,owner) local obj = {} --initialize variables obj.x = x obj.y = y obj.destroy = function(this) createexplosion(this.x,this.y) del(objects,this) end -- The body of the update function that all types of bullet will execute -- If other objects inherit from this class they can overwrite the update method -- but still call the common bullet behaviour from commonUpdate() obj.commonUpdate = function(this) --code end obj.update = function(this) this.commonUpdate(this) end add(objects,obj)--Optional, if you add here then ones inheriting shouldn't add it return obj end |
function createEnemyTrackBullet(x,y) local obj =createbullet(x,y,false) --Changes variables as necessary obj.update = function(this) --Calls the inherited common behaviour this.commonUpdate(this) --Then unique behavior for the child if (this.timer == 30*4) this.destroy(this) end obj.draw = function(this) end end |
[Please log in to post a comment]