Log In  

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?

P#30667 2016-10-11 13:06 ( Edited 2016-10-19 17:01)

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
P#30668 2016-10-11 13:56 ( Edited 2016-10-11 18:12)
:: dw817

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.

P#30672 2016-10-11 14:40 ( Edited 2016-10-11 18:40)
:: adge

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.

P#30698 2016-10-11 19:04 ( Edited 2016-10-11 23:07)
1

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.

Cart #30710 | 2016-10-11 | Code ▽ | Embed ▽ | No License
1

P#30699 2016-10-11 19:17 ( Edited 2016-10-11 23:54)
:: dw817

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.

Cart #30729 | 2016-10-12 | Code ▽ | Embed ▽ | No License

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.

P#30730 2016-10-12 01:53 ( Edited 2016-10-12 05:58)
:: adge

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.

P#30733 2016-10-12 04:11 ( Edited 2016-10-12 08:23)
:: adge

Cart #30755 | 2016-10-12 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

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.

P#30757 2016-10-12 13:04 ( Edited 2016-10-12 18:41)

@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 :(

P#30771 2016-10-12 15:11 ( Edited 2016-10-12 20:54)

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)

Cart #30774 | 2016-10-12 | Code ▽ | Embed ▽ | No License

P#30773 2016-10-12 15:22 ( Edited 2016-10-12 19:26)

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()

P#30775 2016-10-12 15:33 ( Edited 2016-10-12 21:01)

@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!

P#30778 2016-10-12 16:59 ( Edited 2016-10-12 20:59)

Thanks! I feel much better about reality that way.

P#30780 2016-10-12 17:16 ( Edited 2016-10-12 21:16)
:: adge

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.

P#30989 2016-10-15 17:58 ( Edited 2016-10-15 21:58)

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...

P#30991 2016-10-15 18:44 ( Edited 2016-10-15 22:45)
:: adge

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:

Cart #30998 | 2016-10-16 | Code ▽ | Embed ▽ | No License

LEFT to enable TimeMachine
Up and Down to rotate
x, C zoom

P#30999 2016-10-15 20:09 ( Edited 2016-10-16 00:10)

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.

P#31004 2016-10-16 01:16 ( Edited 2016-10-16 05:17)
:: adge

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.

P#31011 2016-10-16 04:53 ( Edited 2016-10-16 08:53)

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.

P#31014 2016-10-16 05:59 ( Edited 2016-10-16 10:08)
:: adge

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.

P#31034 2016-10-16 09:02 ( Edited 2016-10-16 13:02)

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.

P#31035 2016-10-16 09:23 ( Edited 2016-10-16 13:32)
:: adge

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:

Cart #31050 | 2016-10-16 | Code ▽ | Embed ▽ | No License

P#31049 2016-10-16 12:38 ( Edited 2016-10-16 16:56)

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

P#31051 2016-10-16 13:26 ( Edited 2016-10-16 17:26)
:: adge

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?

P#31218 2016-10-19 08:13 ( Edited 2016-10-19 12:13)

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)

P#31241 2016-10-19 13:01 ( Edited 2016-10-19 17:03)

[Please log in to post a comment]

Follow Lexaloffle:        
Generated 2020-08-08 06:13 | 0.069s | 2097k | Q:113