GML [solved] Wavy / Teared Screen FX Using Surfaces (draw_surface_part)

N

Nikles

Guest
Hi folks!
I'm currently experiencing a brain fart and I need your help.

I'm creating a wavy/tearing effect (gif below). How do I make it seamlessly repeat forever? Because as it is right now it just keeps moving all those surfaces outside the display. And they never cycles back.

If I reset the sy (screen y) to 0 it will cycle back but I get a jittery effect (just like the bad loop below)



Here's the code in the create event of my controller:
Code:
sy = 0
Here's the post draw event
Code:
var as = application_surface
var dw = display_width
var dh = display_height

var band_num    = 16
var band_height    = dh / band_num

for (var current_band = 0; current_band < band_num * 2; current_band++)
{
    draw_surface_part(as, 0, 0 + band_height * current_band - sy, dw, band_height, 0 + sin(current_band) * 32 , 0 + band_height * current_band - sy)
}

sy += 4

if (band_height * band_num - sy + band_height) <= 0
    sy = 0
Can you help me implement a seamless loop?

EDIT:
I've edited the above code to highlight what's going on; I'm looping blindly at random here because I can't wrap my head around it. Right now I have zero ideas on how to approach the issue. Any input is appreciated.

 
Last edited by a moderator:

2Dcube

Member
I'm not sure I fully understand your code.
But I'm thinking you need to reset sy at the exact right moment. Since sy increases with 4 every step,
Code:
if sy > 4 * band_height * band_num
{
    sy = 0
}
? I could be wrong.
 

Bingdom

Googledom
I'd suggest looking at using mod or %. Here's why.

I haven't fully looked at your code, so I'm going to generalise it.

If you have a value that you know it won't approach to 0 for a long time (but in your case, you're setting it to 0 at a wrong time), don't directly set it as 0 when it surpasses the max value. Once the timer (or offset) becomes greater than the max value, you do timer - maxValue to reset it. This is where modulo can take place (It's a bit different from subtraction though). Modulo divides a number by a number then returns the remainder.

A code example:
Code:
yOffset = (yOffset + speed) % maxValue
In this example, yOffset will never be > maxValue. It would always get wrapped back to the remainder.

PS: This can be done much more efficiently through a shader.
 
Last edited:
N

Nikles

Guest
Thank you. It doesn't really work but I appreciate your suggestions.

There's probably something flawed in the way I'm approaching the problem here but I can't see what.
I might as well try the shader route but I feel like I might be facing the same "rewind jitterness" / "out of synch looping" effect.
 

TheouAegis

Member
Why not use a sine wave? A sine wave goes from -1 to 1,so you'd multiply the value by how far you want it to deviate. Maybe 16 in your case. I think (not sure) it'd be sin(current_band/band_number)*16
 
N

Nikles

Guest
I am using a sine wave but each band horizontal position must stay fixed (and not oscillate).
So each band has its own horizontal position defined as
Code:
0 + sin(current_band) * 32
(and yes don't consider the "0 + ")
 

TheouAegis

Member
Oh yeah. My phone cut it off lol.

...
So does your code draw the surface like

Code:
0
    1
        2
            3
                4
                    5
But then just using sy to essentially shify the surface up?

Try modifying the sin value instead of the surface offset.

sin(current_band + sy) * 32

Then just increase sy by 1 instead of 4. You can loop it back to 0 when it reaches band_num*2.

sy = (sy+1) mod (band_num*2);
 
N

Nikles

Guest
None of the solutions above actually worked but they you all gave me the correct ideas to work with, so thank you all!

This is the correct code
Code:
var as = application_surface
var dw = display_width
var dh = display_height

var band_num    = 16
var band_height    = dh / band_num

for (var current_band = 0; current_band < band_num * 2; current_band++)
{
    draw_surface_part(as, 0, 0 + band_height * current_band - sy, dw, band_height, 0 + sin( (degtorad(360) / band_num ) * current_band) * 32 , 0 + band_height * current_band - sy)
}

sy = (sy + 4) % (band_height * band_num) // Loops here.
As you can see I now use the following code to find the X offset of the bands:
Code:
sin( (degtorad(360) / band_num ) * current_band) * 32
Using degtorad(360) / band_num ensures that I complete a cycle without jitters. So I can then safely loop with (sy + 4) % (band_height * band_num).

I can vary the speed of the waves as well by increasing or decreasing the value I add (or subtract) to sy.

Thank you all again!

EDIT (31st Oct 2019): Quite some time passed since this thread but re-reading it, I noticed that, actually, @Bingdom's solution has been essential. I don't know why I haven't acknowledged it before but that's the main reason I could get this working without the looping jitters. Also, I switched to a shader as per his suggestion as well so... thanks :)
 
Last edited by a moderator:
Top