people often ask about object orientation (OOP) and inheritance, but can get confusing or incomplete guides. I want to help everyone understand how prototypes work!
lua does support object-oriented programming (but does not require it), based on prototype inheritance — not classes.
metatables are part of this but also often misunderstood. this is a complete explainer that I wrote on discord and keep not putting here in a clean way. so I am putting it here as it is!
so what is a prototype?
goblin={ sp=42, } function goblin:new(x,y) local obj={x=x, y=y} return setmetatable(obj, {__index=self}) end function goblin:draw() spr(self.sp,self.x,self.y) end gob1=goblin:new(40,40) gob2=goblin:new(60,70) |
alright so this is like 6 different things working together.
1) table with methods: goblin
is a table with property sp
and methods new
and draw
(a method is a function attached to an object)
calling goblin:new(40,40)
is the same as goblin.new(self,40,40)
; self is «the object we are working on»; defining function goblin:draw()
is the same as function goblin.draw(self)
and the same as goblin={sp=42, draw=function(self) spr(...) end, somethingelse=true}
there is a difference between the two methods in my example: when we call goblin:new
, the self
param inside it will be the goblin
table itself (the prototype), but when we have gob1:draw()
then self inside draw will refer to gob1.
2) metatables. there are special methods that lua uses to implement operators (+
, /
, ..
, etc). the method for addition for example is __add
(we call these metamethods). if I want to be able to do vector1 + vector2
for my custom vector tables for example, I need to define a method named __add
but I can’t do it in the vector
prototype table, lua will not look it there; it is required to define these metamethods in a table’s metatable. (the metatable is similar to a class in other object-oriented languages: it is the template, or the definition, or the model for many tables.) so that’s what happens in goblin:new
: I define a metatable with one metamethod and set it on my goblin object.
3) prototype. these are great! if I have 50 goblins on screen and they have different health and coordinates but the same properties for sprite, width, height, etc, and the same methods for update, attack, draw, etc, I would like to define all the shared things in one place, then have custom things in each goblin table. that’s what a prototype is for! here in goblin
example we have sp
and draw
shared, and in a specific goblin table (it starts as obj
on line 6, then it’s gob1
or gob2
in my example) we have specific data like x and y.
much more info on this really useful site: https://gameprogrammingpatterns.com/prototype.html
alright so how do I say «gob1 and gob2 should delegate to goblin prototype for some properties»? I define a metatable with an __index
property. it is called by lua when I do gob1.sp
and sp
is not found inside gob1
. it can be a function (can be useful to have a dynamic property that’s computed from other properties) or another table: the prototype!
more: https://www.lua.org/pil/13.4.1.html
now this is the cool thing with prototype-based object-oriented language that you don’t have in such a nice way with class-based OO languages
what if I want 40 goblins on screen and 10 archer goblins? these should have a different sprite and different attack method.
archer=goblin:new() archer.sp=52 function archer:attack(target) --use the bow end arcgob1=archer:new(20,50) arcgob2=archer:new(40,70) |
look at this from bottom to top:
arcgob1.x
will be 20,y
50- arcgob1’s and arcgob2’s prototype is
archer
, because when we callarcher:new
,self
on line 6 of my first code block points toarcher
arcgob1.sp
is 52 (the value comes from the prototype)arcgob1:attack(hero)
will use ourarcher:attack
method.arcgob1:draw()
will usegoblin:draw
! archer itself is a table that has goblin for prototype
draw
is not found in arcgob1, look at its prototype archer, not found, look at its prototype goblin, found! call it withself
pointing to arcgob1.
result:
Greetings,
I'm relatively new to the Picotron and even though it is a "fantasy workstation" I was wondering if there was a way to use it as an actual OS specifically for the Raspberry Pi. If anyone knows if there is a way to use it as an ISO for a Raspberry Pi I would love to get some info as to learning how to make it it's own OS.
--REBOOTER--
Ordered by Elevated Recovery
Made by Charlie Boudchicha (https://www.fiverr.com/charlie_boud)
You need to complete every achievements to win the game :
- make a bad decision
- make a productive decision
- do something to rest
- unlock the 4 blue rooms
- go through 15 rooms
- survive 3 trap rooms
Hello, considering the following:
Trying to remake Vampire Survivors and a fun project
I have a character class
Characters have effects that are applied to a target. In this example my target = player
I have a character that is an instance of that class. Let's call him "Antonio"
I have a "run", Antonio is copied into run.player
I set player to be run.player
My problem is that I have to initiate the character class and Antonio before I create "player". But player is not yet created so player referenced on the character class is nil.
I hope this makes sense, I have a workaround but it feels clunky and I'm wondering if there are other solutions.
Here is my code before the workaround:
character = {} character.__index = character function character:new(name) local o = setmetatable({}, character) o.name = name o.x = 0 o.y = 0 o.sp_scale = 1 o.w = 16 o.h = 16 o.effects = effects o.original_invincibility_frames = 10 [ [size=16][color=#ffaabb] [ Continue Reading.. ] [/color][/size] ](/bbs/?pid=147242#p) |
I've been using this tutorial as my principal information for programming, since there's no picotron specific resource.
I'm experimenting with table-based classes, according to the guide I'm supposed to be able to create a base blueprint of an object and then instantiate it, but when I do so following the example, the object is not copied but instead it becomes a reference, because every change gets applied to the first object.
I made a sample project, first I try the guide's way, then I try it in a way I know works
enemies = {} enemies2 = {} enemy = { type = 0, sp = 1, x = 0, y = 0, dx = 0, dy = 0, update=function(self) self.x += self.dx self.y += self.dy end, draw=function(self) spr(self.sp, self.x, self.y) end } goblin = enemy --copy enemy class goblin.sp = 2 goblin.type = 3 goblin.x = 6 goblin.y = 10 ogre = enemy --copy enemy class ogre.sp = 3 ogre.type = 4 ogre.x = 40 ogre.y = 50 function _init() add(enemies, enemy) add(enemies, goblin) add(enemies, ogre) add_enemy(16,16,4) add_enemy(32,32,5) add_enemy(64,64,6) end function add_enemy(new_x,new_y,sprIndex) add(enemies2, { type = 0, sp = sprIndex, x = new_x, y = new_y, dx = 0, dy = 0, update=function(self) self.x += self.dx self.y += self.dy end, draw=function(self) spr(self.sp, self.x, self.y) end }) end function _draw() for e in all(enemies) do e:draw() end for en in all(enemies2) do en:draw() end end |
If I define the object in the add function then each object acts as independent object, is this how tables are supposed to function?
hi, i'm loving picotron so far! i just wanted to report some issues using coroutines.
when using coroutine.resume, it can sometimes return early when the coroutine runs for a while due to how picotron does timeslicing. from reading head.lua, i learned that using coresume instead of coroutine.resume fixes this, which makes me think maybe coroutine.resume should be replaced with it. another more minor issue is that coresume only returns the first value of a multiple-value return/yield from the coroutine, and not any more.
here's a simple test case:
local function long_running_coroutine () for i = 1, 100 do cls() end return 1, 2 end function working () local c = coroutine.create (long_running_coroutine) coresume (c) assert (coroutine.status(c) == 'dead', 'this works') end function not_working () local c = coroutine.create (long_running_coroutine) coroutine.resume (c) assert (coroutine.status(c) == 'dead', 'this doesn\'t work') [ [size=16][color=#ffaabb] [ Continue Reading.. ] [/color][/size] ](/bbs/?pid=147216#p) |
A GUI library for Picotron!
It includes basic components: boxes, text boxes, text input, buttons, sliders, radio and multiple selection buttons, and color selecter. And layout components: vertical and horizontal stacks, dropdowns, a topbar and a scrollable container.
It can be useful to test game mechanics or to build app interfaces.
You can use this cart and see how it works or check the documentation on the github repository.
(I'll try to make a video on how to use it and post it here too :))
Here's one simple example:
[hidden]
include "pgui.lua" function _init() -- Define the initial value of the slider slidervalue = 10 end function _update() -- Refresh pgui each frame pgui:refresh() -- Create a slider and set its value back from its return value slidervalue = pgui:component("hslider",{pos=vec(190,20),value=slidervalue}) end function _draw() cls(5) -- Draw the circle, its size is based on the return value of the slider circfill(240,140,slidervalue,8) -- Draw all pgui components pgui:draw() end |
Lens 0.1.2
Capture and view debugging information. This app will run and allow you to send it values to log and inspect. Since this runs as a separate process, you can still inspect log values even in the event of your program crashing.
Supports all data types and includes the ability to inspect table values.
Usage
To use, include "libs/lens.lua"
from the cart, and call Lens.log()
.
Otherwise just send a "log"
event to the lens
process:
send_message(lens_pid, { event = "log", value = "log output", }) |
This is an early release to collect feedback, so please let me know if you find it useful or any issues you encounter.
n-back (Wikipedia, Gwern)
How to play:
1-Back: Remember whether the position or color (or both) is the same as in the previous round, press the according button/buttons if they match.
2-Back: Remember whether position or color are the same as 2 rounds ago.
N-back: Remember whether position or color are the same as n rounds ago.
Controls:
Z or <- for a position match
X or -> for a color match
P to pause (+ additional menu)
Tips:
Try playing in standard mode to understand how the game works, in endless the game ends when you make a mistake or miss a match.
Cascade Cascade
A game somewhere in the space between a breakout clone and a puzzle bobble clone, inspired by a half-remembered game I played on an airplane seatback.
Controls
- Aim with left and right arrows
- Press [x] to fire
Or, as of 2024.04.27, point and click with the mouse to aim and fire.
Rules
This is an endless arcade game. Your goal is to survive by keeping the blocks from reaching the bottom.
- Each hit reduces a block's strength by one. When a block's strength reaches zero, it's destroyed.
- Powerups (white concentric circles) increase the number of balls you can accumulate for your next shot.
- The number of balls you'll fire next shot is indicated by the combo meter on the right side of the screen. It increases by one-half for each block you hit.
ASTROYD
Any good computer needs one of these. This is my version, with a bunch of juiceful extra stuff that wasn't in the original classic release.
What, I really have to explain the game? In this day and age? Fine...
The Controls
- ⬅️ and ➡️ to steer.
- ⬆️ to thrust forward.
- [Z] to perform a hyperspace warp! Your momentum is conserved. You might warp face-first into an asteroid, though.
- [X] to fire.
The Game
As you destroy asteroids and flying saucers, you will gain points. See, this is one of those "arcade games" where the points are the point, and the reward is a high score.
That's neat and all, but the game will also become more difficult as you increase your score and clear screens. I wonder if there's a limit...
Welcome to Brain Games. A collection of puzzles and game to test our logic and puzzle-solving skills. Take your time and have fun in these 6 different games.
Jumps
Use peg solitaire rules to solve these puzzles. Use one chip to jump over another and remove the jumped chip from the board. The puzzles get more difficult as the levels get higher! Can you get the board down to one chip?
Dice
Can you get all the dice to the six face? These puzzles start out easy but increase in difficult as you level up. You'll need to use Rubik's Cube like logic to solve the more complex levels.