Hello it's me again with the stupid questions! :P
I was following this past week bridgs' great workshop on pico8, and learned a whole lot about how to make tables and other tidy things on pico8, but I do confess I got a bit lost with it and now I am facing trouble. I'm not getting used (yet) to the more lax way of asigning vaiablesa nd creating vectors/tables.
Anyway, I have made this element:
object = { width=2, y=60, color=3, time=0, speed=1, velocity=2, move=function(self) self.time+=0.01 self.velocity=sin(self.time) self.y+=(self.velocity*self.speed) end, } |
This works, but only if I want to make one object, I want to make many, however. THis is what I was doing and is wrong (takes object propeties and draws a rectangle with thm):
function draw_objects() local i objects={object,object,object} for i=0,2 do rectfill(0,objects[i].y,50,objects[i].y+objects[i].width,objects[i].color) objects:move() end end |
I read the documenttion and I don't really understand how you traverse a table. I thought this would work but I think the indexes ar not numeric like I am using them (it's giving an rerror about " atttempt to index field ? a nil value). Also not even sure how I am assigning objects is working. One more, I saw the method using keys and values, but really don't understand what "key" is supposed to be. Documentation makes it seem like I can make it up, but how?
Thanks in advance for the help!
I'm going to tackle your questions out of order.
Tables are made up of key/value pairs. Using your OBJECT table as an example, 'WIDTH' is a key and '2' is the value paired with it. It might help to think of key/value pairs like this:
object['width'] = 2 |
In fact, the dot operator you use to access the value for WIDTH is just shorthand for OBJECT['WIDTH'].
As a supplement, use this tutorial.
If you don't provide keys when creating a table, values will be indexed by integers. It might be unintuitive if you're coming from another programming language, but Lua begins indexing at 1 rather than 0.
OBJECT is a single table. You'll need to create separate tables if you want to have separate values. Dig into one of mhughson's carts and take a look at how he creates vectors or goons (M_VEC and M_GOON functions, respectively).
First, just clarifying: Where you have "objects={object,object,object}", is that just pseudocode to let us know you have a list somewhere that is called "objects" that has several objects like your first code block's "object" in it?
I'm going to assume "yes".
So, that said, first up is that you need to know Lua counts from 1, not 0 like most languages. This is stupid and annoying, but it's how things are. So you want a loop over three items to look like this:
> t={7,8,9} > for i=1,3 do print(t[i]) end 7 8 9 |
Next up, there's a useful operator "#" in Lua that lets you know how many unnamed, or array-style, elements are in a table. By this, I mean how many things you've added without keys in the declaration, appended by writing to the next empty entry, or called add() with:
> t={} > print(#t) 0 > t={4,5,6} > print(#t) 3 > t[4]="abc" > print(#t) 4 > add(t,-3) > print(#t) 5 |
So you can loop over an any-sized table like this:
> t={7,8,9} > for i=1,#t do print(t[i]) end 7 8 9 |
(To get all the named keys too, you have to use an iterator, but you don't need that here, so let's leave that for another day.)
Yet another useful note is that the loop variable in a 'for' loop is automatically declared local to that loop. You need not declare it ahead of time. In fact, if you do, the one you declare is actually hidden by the new one if it has the same name, and its value will be unchanged after the loop is done:
> i=5 > for i=7,8 do print(i) end 7 8 > print(i) 5 |
Finally, you have what looks like a typo, where inside your loop you refer to "objects:move()" when I think you meant to say "objects[i]:move()".
Given all that, your function should look like this:
-- assumes 'objects' is a global with all objects to be drawn in it function draw_objects() for i=1,#objects do rectfill(0,objects[i].y,50,objects[i].y+objects[i].width,objects[i].color) objects[i]:move() end end |
I would also suggest splitting out your logic/movement/physics into the update phase. This is how games usually do things, because updates usually cost less than draws and, when your draw takes too long, you can run multiple updates to keep your logic running at the right speed even if you're dropping draw frames.
Thus, you'd want this:
-- assumes 'objects' is a global with all objects to be drawn in it function update_objects() for i=1,#objects do objects[i]:move() end end function draw_objects() for i=1,#objects do rectfill(0,objects[i].y,50,objects[i].y+objects[i].width,objects[i].color) end end |
With each function being called from the respective _update() or _draw() function.
HI guys thanks for your help! You're the freaking best.
My error came from addressing the table cells starting from zero (like any decent language would do :D)
Also thanks for marking the difference between update and draw, I did not think of that. Will split it.
Now bear with me for a second, because this is where I get confused.
What I wrote as "objects", to me, was supposed to be an objects element in which each element was to be like the "object" element defined above, but I think that is exactly what isn't working.
Allow me to draw a little crappy diagram of what I want to achieve:
I hope that's clear enough and that's what Felice just explained.
Tarek said:
OBJECT is a single table. You'll need to create separate tables if you want to have separate values. |
And yeah that's what I want to achieve, but how?
If I understood you guys correctly, the OBJECTS element I am trying to create should have inside each cell objects of the structure I defined as OBJECT (same KEYs) but with different VALues.
So if I do, for example,
objects[1].width=2 |
will that NOT work in just changing the width value for the OBJECT width key in cell 1 only? Will it change it for all cells?
Thanks for everything so far, very helpful
You need to clone tables to have independant instances.
That's what I do in my games:
function clone(src,dst) -- safety checks if(src==dst) assert() if(type(src)!="table") assert() dst=dst or {} for k,v in pairs(src) do if(not dst[k]) dst[k]=v end end -- example use -- base "class" with methods and default values local object_class={ object = { color=3, time=0, speed=1, velocity=2, move=function(self) self.time+=0.01 self.velocity=sin(self.time) self.y+=(self.velocity*self.speed) end } -- create a new object -- note: I picked width and y as parameters, to be tuned to your use case function make_object(width,y) return clone(object_class,{ width=width, y=y }) end -- create 3 instances local objects={ make_object(50,12), make_object(40,8), make_object(30,24) } |
Would the example I posted above not do the same, although not as neatly/coder-y ?
What is assert()?
I did not see any cloning code in your example....
assert raises an exception if inner condition is false. Useful during development for something that must not happen.
In the code above I fill a table with 3 elements that are the exact same, but then I proposed changing the values individually afterwards.
object = { color=3, time=0, speed=1, velocity=2, move=function(self) self.time+=0.01 self.velocity=sin(self.time) self.y+=(self.velocity*self.speed) end } -- make the table (?) objects = {object,object,object} --then do many operations like this: objects[2].width=2 |
I don't know if that works or understand why it wouldn't, how it would differ from your code (besides your code looking much neater and pro).The way I see it, when I do "objects={object,object,object}", it's copying the "object"element 3 times each to a cell on that table, not referencing it 3 times. But maybe I am understanding wrong.
it's copying the "object"element 3 times each to a cell on that table, not referencing it 3 times.
Hum... no!
You are actually storing 3 times the same object reference.
Modifying one instance will actually change the value on all '3'.
Your problem kifrendo is that you made yourself a hamburger.
You named that hamburger "object."
You pulled out a lunchbox named "objects" with 3 slots and proceeded to put that SAME hamburger in all three slots.
If you were to reach in and grab one of the hamburgers in the slot and take a bit out of it. The other two would also get a bite taken out. You're referencing the same "object" 3 times.
What frendo72 is saying, is that you need to create more hamburgers.
So consider this:
-- this creates a hamburger with name value as paul object = { food="hamburger", name="paul" } --this creates a lunchbox with paul inside of it lunchbox = {object} --this creates another lunchbox with 2 pauls inside of it, both the same paul lunchbox2 = {object, object} --So right now if you make any changes to the paul in lunchbox, --like name him tom, then all the hamburgers in lunchbox2 are also named tom now function new_object(food,name) local obj ={ food=food, name=name } return obj end object2 = new_object("hotdog","steve") print(object2.name) --prints "steve" --this creates a lunchbox that has paul and steve inside lunchbox3 = {object,object2} --now consider this function copy_table(original) local original = original local new = {} -- this next line goes through each key/value pair in the first table, --and adds it to the new table so in our case it's just going to be the food and the name for k,v in pairs(original) do new.k=v end -- then just return the new table return new end paul2 = copy_table(object) print(paul2.name) --prints "paul" print(paul2.food) --prints "hamburger" |
Funny hamburger reference:P But yeah all I needed to be told was that I was creating references to the same object. I thought by doing that I was making new copies of it. Thanks for the clarification.
So the structure I made, on each cell, has a pointer to that one object.
I didn't put the same hamburger in the 3 slots (that's impossible! Stephen Hawking cries in his grave!), rather, I put in each slot a post-it that tells me which hamburger should go there. In the 3 slots, the post it points to the same hamburger! (which is in my backpack. Silly me).
I hate LUA.
Again thanks for your help, I shall be creating some code in a different manner, what I want to do with this, in this way, becomes too complicated (I only want to have three objects)
If you what you ever want is 3 different objects, doing so will work:
function update_object(self) self.time+=0.01 self.velocity=sin(self.time) self.y+=(self.velocity*self.speed) end object1 = { color=3, time=0, speed=1, velocity=2, move=update_object } object2 = { color=1, time=0, speed=5, velocity=-2, move=update_object } object3 = { color=5, time=0, speed=2, velocity=2, move=update_object } -- make the table objects = {object1,object2,object3} |
Ahaha, yes, you are right to a degree. It's a pointer, if I would have known that would have clarified it, I would have stuck with that, but I didn't know your programming history.
What languages are you more familiar with?
If you know C++, then assume a Lua variable will be one of these:
- fixed-point number
- bool
- std::shared_ptr<std::string>
- std::shared_ptr<std::unordered_map> (table)
- nullptr (nil)
If you assume anything other than a native type like number or bool is a shared_ptr (a refcounted pointer), it makes it easier to remember where you need to copy things.
I am more familiar with C type languages indeed!
It does do my head in that when you define a variable there's no clear indication of what type it is :/
Thanks for all your help people, it's been priceless!
So thanks to you guys I figured out how to make a little thing. But how can I insert a cartridge into my post? I don't seem to be able.
It's basically 3 "rasterbars" playing around.
Now it would be really nice to make these feel a bit more organic, it might be because each one has the same time variable? I tried modifying parameters but the results were always "meh"
The easiest way to share a cartridge these days is this:
> save @clip |
This'll save an ascii-encoded version of the .p8.png format to your clipboard, which you can paste into a message on the BBS. The BBS will recognize it when you preview your message and offer you a form to give it a name and stuff.
OK so here goes!
as you can see, the movement is very stiff, and i would like to attain a more cracktro-like effect!
One thing you should do is switch to _update60() so it'll run more smoothly. PICO-8 runs 30fps when you use _update().
When I change to update60, it runs faster instead of smoother. I must be messing up some math here :P
OK yeah I assumed as much, but why if I change this to use time() instead, if I use update60 it still goes faster? shouldn't time() be independent of framerate?
[Please log in to post a comment]