Does anybody know what's the easiest way to add a tail to a moving pixel?
A pixel is moving one px to the right but instead of just moving it there should stay a pixel in the pixels first location for a short time. Imagine a fade out.
Is there an easy way to accomplish this?
Make a table, fill it with the last X pixels, draw them all. Probably use a lookup table to do the color fade. That's the most robust way and works when you're clearing the screen every frame. (What you end up with is a small-scale particle system, basically.)
Another option could be to not redraw the whole screen, and randomly make any pixels you find a step dimmer. (not every pixel on the screen, that would take too long) Quite a few of these in the tweetjam thread.
edit: example functions incoming
function _init() dots={} colors={5,5,6,6,7,7} x,y=0,64 dx=2 dy=0 end function _update() x+=dx y+=dy add(dots,newdot(x,y)) for dot in all(dots) do dot.life-=1 if (dot.life<1) del(dots, dot) end end function _draw() cls() for dot in all(dots) do pset(dot.x,dot.y,colors[dot.life]) end end function newdot(newx, newy) return {x=newx,y=newy,life=6} end |
I know =A= way to do this. I don't know if it's very efficient. Not just you, I would very much like to see some intelligent (and hopefully small) code written for this situation.
Hmmm. I think this could get problematic as I'm in 3D.
Imagine this:
I got 400 points all have a x, y and z property.
To get the x, y and z I have 3 formulas.
x needs u and v as parameter
y needs u and v as parameter
z need v as parameter.
u and v get randomly generated. u = rand(1), v = rand(4)
each Pixel has a unique u and v and these are stored with the pixel to0.
When I'm updating my "animation" i simply get u and v for the pixel I want to update and do this:
u = u - 0.005
v = v - 0.005
then I recalculate x, y and z using the formulas I mentioned.
Adding stuff like the above could cause heavy CPU use maybe the simplest solution would be to call CLS() every second update. Is that possible?
Another solution I'm thinking about would be to have two tables holding 200 pixels each. One table for current, one for last state however this would allow me only a two pixel tail at all. Moreover I don't know if the pixels are moving by one pixel. It could depend on the formulas.
However I could also just generate 100 pixels and then add your method mentioned. This would probably the best idea.
If you've got a larger pile of things to track, definitely use static-length tables. (run through and ignore zeroes) Add() and del()ing can really thrash the memory when you have 100's of entries, and gc will kill you.
In fact, the second option might be superior. Just pget() a pile of random pixels, and "dim" them with a lookup table, no other tracking. (and of course run through drawing all the active pixels on top)
If it gets too noisy, another abstractification might work - grab a low res "copy" of the area in question, (1 px in every aXa block,) downgrade all the colors, draw that with a-wide blocks or circles as a background before the normal/active stuff. (kinda going for a glow or blurry effect that fades)
edit: another thought: what is the resolution of your output? (again, I'm thinking of the ouput you have, not the 3d data it represents) Looking at that 3d swirl cart you posted, the actual number of pixels that might get drawn any frame isn't ridiculous. if there's a function or shortcut to run through just the pixels that might be relevant, you could probably use the screen as a buffer and have practically unlimited trails.
edit - yeah, the screen totally worked as a buffer. (doubled processing time roughly) Check _init and _draw for my shenanigans.
Phew ! This took MUCH longer than I expected. Very tricky, it wasn't until an hour later I realized I had to spawn each new bit of the snake, not just monitor the tail end as I did for a BASIC array.
This should be about as efficient code as you can get for a snake with a vanishing tail.
For awhile there, I thought I was gonna have to ask your advice on coding it, Tyroney, as the logic in keeping track of the bits was starting to make my head hurt. :)
Adge, this is another example of a trailing pixel with a limited life.
Crash into an obstacle ? Press CTRL-R to reset or, if in editor, [ESC] to source code.
Oh god how come that you people are so smart! Thank you tyrony you definitely taught me something. I would have never understood that you just have to think about the output and not all the 3D stuff that is happening.
Which kind of solution are you using now? I haven't jet looked at the code but I couldn't read that out of your comments.
I'm currently also trying to add some z-sorting (Draw back ones first, front ones later). If you zoom out all the way you get what I mean. But for doing that I have to rotate everything back to a normal position then sort the list, rotate back and draw. Well I only have to rotate Z but still using CPU time.
Are there things I could optimize? Like imagine if i got 400 points in the table and they all get updated. If I zoom out I don't need all of them. And what happens when they are overlapping. Are there maybe parts I could develop more elegant solutions for?
As you saw, I'm not using ADD and DEL heavily. For keeping it infinite, I just go through the list, watch out for positive Y's and them recalculate them. Strange things happening here, I'm not really sure were the axes are + and - and why. There is still something wanky with my calculations. Easiest way would be to transform the formulas itself so the output would already be angled.
You seem to be an experienced programmer. So if you got some notes for me, feel free to tell me.
GOD ALL MIGHTY!!!!!
I now got z-sorting working.
As you all know I'm calculating x,y and z through formulas. To animate stuff I simply change the input values a little bit and recalculate the values. Also you can rotate the swirl to get a better view.
So how do you sort along the Z-Axes? First know that my 3D functions for rotating take a table and they will alter that table so you just have to draw it. However when the points are rotated sorting along the Z-Axis wouldn't be accurate anymore. I have two ways:
1 table for the actual unrotated values and 1 for the rotated ones. The latter one is drawn.
Or rotate the z value back and use it for sorting.
Which makes me think: I don't have to rotate the swirl back in neutral position right? It just doesn't matter...oh boy...
I choose the first one because it seemed easier at first. However it might go with a huge performance decrease, I will try the second now.
But you can't imagine how long it took me to realize that Lua copies tables by reference and not by value!!!! I was nearly crying ;D
...I have also implemented a wanky axes system but it seems to be disabled in this "build"
You can see that the sorting is working if you zoom all out. In the last versions it was never clear how the vortex was rotated. BUT NOW, you can assume :D
@tyroney: Thank you for your taily solution, I haven't used it yet but I will. It looks so much cooler!
Could you maybe explain again which solution you choose? I couldn't read that from your post.
@ultrabrite: thank you for your response. I haven't looked yet into "clean" programming the swirl yet. Everything I'm now doing is just to get stuff working. And if motivation keeps me going I will try to improve performance, though I'm not a good programmer so I will need some help. Thank you very much!
And could someone tell me what "pal" actually does? I'm not getting any smarter with the manual.
@adge: my previous post wasn't about your code performance at all. deleted it though (ramblings of a tired man).
I noticed that you're putting radians in the trigo functions, and you stated you had a hard time figuring out which axis is + or - ... the manual states:
"Quirks of PICO-8 - cos() and sin() take 0..1 instead of 0..PI*2, and sin() is inverted."
no idea why, I think zep just hates maths :(
I used pal() to change the colors your code assigned. I also got confused, as pget() didn't return what I expected, so the colors I wanted and the colors I used may be two completely different things.
here's visually what I wanted. (no pal(), just a lookup table and hardcoded colors using the original color thresholds)
about pal():
say you have a sprite of a guy in a red (8) shirt.
with pal(8,11) you'll draw the guy in a green (11) shirt.
same effect on pset(): pset(x,y,8) will draw a green pixel (and pget(x,y) will give you 11).
reset with pal(8,8) or the whole palette with pal()
@tyroney: you stat(1) is misplaced, should be at the end of draw()
you're actually running @ 1.664
remapping the screen is heavy duty!
Ok. I looked into that stuff a bit further and it seems to be far better to actually calculate 3 different states of a pixel, save them in 3 different tables and draw all 3 tables each draw loop.
However, with 2 states it's easy. I create points, I draw them and at the end of the draw event
vortex_2 = copy(vortex_1)
But how do I do this with 3 or 4 tables? I used a cycle counter but I got confused.
Every update vortex_1 position gets updated. After drawing, vortex_2 gets cloned from vortex_1 and vortex_1 gets updated. I have two different states now. These two are drawn. Vortex_3 is copied from Vortex_2, vortex_2 is copied from vortex_1 and 1 gets updated. Continuing that logic for n tables.
How do I approach this? I got something half working, tried to solve it, got confused.
Thats my code:
_init() vortex_1 = {} vortex_2 = {} vortex_3 = {} vortex_4 = {} vortex_5 = {} cycle = 1 end _update() update(vortex_1) if cycle == 5 then cycle = 1 end cycle = cycle + 1 end _draw() draw(vortex_1) draw(vortex_2) draw(vortex_3) draw(vortex_4) draw(vortex_5) if cycle == 2 then vortex_2 = copyt(vortex) vortex_5 = copyt(vortex_4) end if cycle == 3 then vortex_3 = copyt(vortex_2) vortex_5 = copyt(vortex_4) end if cycle == 4 then vortex_4 = copyt(vortex_3) vortex_5 = copyt(vortex_4) end end |
I would like to prevent calling update(vortex) 5 times a loop to save cpu time.
Using this code I get a tail but some parts of it are flickering due to syncing some tables at the wrong time or only every second third or fourth loop. I'm probably using some bad logic, maybe some of you can help me.
I don't think you need to clone tables all around:
update(vortex_5,vortex_1) --read from vortex_1, write to vortex_5
then
vortex_1,vortex_2,vortex_3,vortex_4,vortex_5= vortex_5,vortex_1,vortex_2,vortex_3,vortex_4
(references!)
then you draw vortices from 5 to 1, with adequates colors.
hope this makes sense...
Hmmm, it probably makes sense but I don't understand what you mean.
What I need is like n tables with vortex_1=actual state, 2=last state, 3=state before last state and so on.
Take a look:
LEFT to enable TimeMachine
Up and Down to rotate
x, C zoom
Ultra has it.
"a,b=b,a" swaps the values of a and b. That long multi-assign line he had was swapping the values of all the vortex tables. ("a,b,c,d,e=b,c,d,e,a")
So go do it! Or I will tomorrow after lunch if you don't.
Well, I can't even pull out the update code into a function. The more I use Lua, the more I hate it....wtf is meant by:
"<eof><name> or '...' expected near 'in'"
I'm trying to pass a table to a function.
The table looks like:
t= {
{1,1,1,1,1,1},
{1,1,1,1,1,1},
...
}
with 1 being random numbers.
here's a working exemple, tried to make it short and clear:
t1={} t2={} t3={} function _init() for i=1,40 do t1[i] = { x=rnd(128),y=rnd(128) } end end function _update60() -- current chronological order is -- t1,t2,t3 -- you're gonna drop t3 (oldest data) -- so use it to store your new state -- update t3 from t1 for i=1,#t1 do t3[i]={ x=(t1[i].x+1)%128, y=(t1[i].y+1)%128 } end -- now chronological order is -- t3,t1,t2 -- rename tables t1,t2,t3=t3,t1,t2 -- now chronological order is -- t1,t2,t3 end function _draw() cls() -- draw oldest to newest for i=1,#t3 do pset(t3[i].x,t3[i].y,1) end for i=1,#t2 do pset(t2[i].x,t2[i].y,5) end for i=1,#t1 do pset(t1[i].x,t1[i].y,7) end end |
note that t1,t2,t3=t3,t1,t2 uses a nice particularity of lua
in other languages you'd use a temporay variable: tmp=t3 t3=t2 t2=t1 t1=tmp
hope that helps
concerning the errors you get, you'll need to post the relevant line to get help, it's all about context.
This is how update works:
if vortex_animate == true then for i=1, #vortex do node = vortex[i] u = node[4] - decrease_u v = node[5] - decrease_v x = cos(u)*v*v y = -v*3 z = sin(u)*v*v yabs = y vortex[i][1] = x vortex[i][2] = y vortex[i][3] = z vortex[i][4] = u vortex[i][5] = v vortex[i][6] = yabs if keepinfinite == true then if (vortex[i][2]>=-1) then u = rnd(randu) v = rnd(randv) x = cos(u)*v*v y = -v*3 z = sin(u)*v*v yabs = y vortex[i][1] = x vortex[i][2] = y vortex[i][3] = z vortex[i][4] = u vortex[i][5] = v vortex[i][6] = yabs end end end |
I tried to pull it out into a function like that:
function update_vortex(tabletoupdate) for i=1, #vortex do node = vortex[i] u = node[4] - decrease_u v = node[5] - decrease_v x = cos(u)*v*v y = -v*3 z = sin(u)*v*v yabs = y vortex[i][1] = x vortex[i][2] = y vortex[i][3] = z vortex[i][4] = u vortex[i][5] = v vortex[i][6] = yabs if (vortex[i][2]>=-1) then u = rnd(randu) v = rnd(randv) x = cos(u)*v*v y = -v*3 z = sin(u)*v*v yabs = y vortex[i][1] = x vortex[i][2] = y vortex[i][3] = z vortex[i][4] = u vortex[i][5] = v vortex[i][6] = yabs end end |
Don't ask me why but it doesn't work.
but you're updating a global called vortex, not your parameter tabletoupdate,
maybe your function definition should be
function update_vortex(vortex)
so vortex will refer to your parameter instead.
What!??? Thats even weirder than how assigning a table to a table in lua only works by reference. I'm used to program in C and there something like this would be totally ok.
int increment(a) { a = a + 1; return a; } int main() { int counter = 1; counter = increment(counter); printf(%d, counter) } |
If you are forced to name the arguments of a function like the "object" you will actually pass, you could run into shortage of variable names pretty soon I think. But well nothing to do about.
Anyway, your solution works pretty damn fine. But I really don't know why because if you do something like the following every table should be the same because Lua's memory management copies everything by reference natively.
vortex_1, vortex_2, vortex_3, vortex_4 = vortex_4, vortex_1, vortex_2, vortex_3 |
So you set vortex_1 to be 4, but when you later assign 3 to 4, 4 will point to 3 and so will 1.
But it works which means I got some logic problems in my head or it isn't copied by reference.
This is the result:
nope nope nope.
functions work just like in c. in your code above you're not using 'tabletoupdate' at all.
this is what you did:
int globy; void increment(int param) { globy++; } |
regarding multi-assgnment, a,b=b,a is done in one shot. what it does is more akin to
tmp_a=a tmp_b=b ; a=tmp_b b=tmp_a
Allright, I see I got something wrong there.
I was wondering what the correct way is to animate stuff? Like if you want something to fade in slowly or have some kind of animation? Just as i did? Increasing a value in _update?
Yup. Animation is a change over time, so you have to keep track of time, and/or let _update() tick and then you can gradually do something, which yields some kind of animation.
As to correct, the way that works, and gets the performance you need, and in larger carts has a small enough number of tokens is the "right" way. Almost every way one might do something has some kind of compromise built it. My "let's process the entire screen" method took a lot of time, and was limited by the number of colors available, but only needed a a handful of lines and a lookup table.
edit: pretty much every extra thing you want to animate/ease/fade adds a new variable or three you need to track. (or hundreds if you're doing an entire 3d spiral of particles)
[Please log in to post a comment]