I never used LUA before (I come from JS and Ruby), today a saw a bit about OOP here and since pico doesn't support metatables i'd like to know if this is the right way to OOP:
local player = {} function Player.new() local self = {} self.x = 64 self.y = 100 self.width = 20 self.height = 5 self.color = 7 self.get_x = function() --one way for method return self.x end function self.set_x(new_value) --another way for method self.x = new_value end function self.draw() rectfill(self.x, self.y, self.x + self.width, self.y + self.height, self.color) end return self end function redraw() rectfill(0,0,128,128,0) end function _init() player1 = Player.new() end function _update() local x = player1.get_x() player1.set_x(x + 1) end function _draw() redraw() player1.draw() end |
So... If there is anything wrong* there please tell me :), i know i could make a player.move method but i wanted to see getters and setters visually.
*I do consider anti-patterns as wrong.
Generally I'd say you want to avoid OOP on a platform like this if you can since it incurs a lot of overhead, but, this seems like the right way to do it for sure.
You should call member functions with a : though, such as
player1:set_x(x+1) |
Which isn't totally necessary the way you've written it, because the local self is being captured in your function definitions. The ":" captured the caller table and registers it as the "self" token for within the function. If you don't do that there won't be anything to reference the caller, since it'll just treat it like a typical agnostic function call.
It would be more efficient to write the member functions outside the new function, and then just assign them to the newly created table within it.
yeah, doing OOP type stuff in pico8 will really kill your token space. definitely wouldn't recommend using getters or setters.
Without metatables it's really quite a pain.
What's the difference between adding them directly or outside the constructor? I did this way because I think it's visually nice, but if it really makes a difference I'll chnge it (if you could edit this example to show me your way it would be awesome)
Could you elaborate more about this "token space"? I tried to google it but i can't figure out what you mean by that.
*edit
~Added: Token-based code limiting (8192 tokens, 32k ascii text)~
Do you mean this? Am i limited to 8k total chars in my games? Can use load() to expand this?
*another edit
from what i tested, a token is either a command or a char out of a command, and its limited on the cartridge itself so i guess i can use load() to bypass the limit :D
Regarding tokens, pico-8 has two limits on how much code you can include in a cartridge. The first is a simple limit on how much text you can have in your code section (32k characters is the limit). The second is a slightly less obvious limit on the number of tokens in your code (8192 tokens is the limit).
If you load up a cartridge and then in the console type 'info', you'll see how many characters and tokens it is using. And yeah, going full OOP will eat up a lot of extra tokens, which for many people is the limiting factor for how much stuff they can fit (it's harder to hit the text limit, though I have done it).
pico-8 cartridges have a limited capacity(and as such, a limited capacity for code), which is measured as tokens. You can see how much space you've used at the bottom-right of the code editor. I could try and explain how much a token is but it'd be easy to get a feel for it if you take note of when the counter goes up as you write code.
Function separation:
player = {} function player:draw() rectfill(self.x, self.y, self.x + self.width, self.y + self.height, self.color) end function player.new() local p = {} -- functions(and tables, jsyk) are not copied, but referenced, so it is more efficient in an OOP context where the idea is to create many copies of the same thing p.draw = player.draw --etc end |
Each time you define a function, Lua is compiling that function into bytecode and storing it in memory(at least temporarily due to garbage collection, which in a sense can be even more of a waste). So if you're defining that function each time you create an object, you're storing it in memory that many times. The pico is a pretty performance-critical platform, and redefining a function n times comes at a large and unnecessary CPU and memory cost, both are precious. By referencing the function defined at program start, it can exist in one place only--the Player table--and everything else just hooks onto that.
I hope that helps you out :) good luck
Yep, I hope this limit get raised, but load() workaround can probably help for now :)
Thanks for the explanation, low-level and memory management is something i'm really bad at (I blame the high-level langs for that)
[Please log in to post a comment]