• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

GameMaker How to make an image on a surface fade away?

Arashi

Member
Hello everyone.

I want to create trails for moving object using a surface.

For that I draw an object's sprite on a surface every step.

But I need it to fade away. Some way to make all the colors of the surface to become more and more transparent each step.

There must be a way, right? Can you please show me direction, at least?
 

obscene

Member
You could use two surfaces....

Draw onto surface 1.
Draw surface 1 onto surface 2 with slightly less alpha.
Draw surface 2 onto screen.

Next draw cycle...
Draw onto surface 2.
Draw surface 2 onto surface 1 with slightly less alpha.
Draw surface 1 onto screen.

Next draw cycle... repeat at beginning.

If you are drawing the surface with an additive blend mode you could also just draw a low-alpha black rectangle onto your surface each frame.
 
C

CedSharp

Guest
For that I draw an object's sprite on a surface every step.
NO! Please don't!
In the documentation, it clearly says that you should avoid drawing on a surface outside of the draw event because GameMaker will not be able to optimize rendering if you draw in the step event.


Or use bm_subtract?
That would be very hard to handle, specially if trails can overlap each other.

You could use two surfaces....

Draw onto surface 1.
Draw surface 1 onto surface 2 with slightly less alpha.
Draw surface 2 onto screen.

Next draw cycle...
Draw onto surface 2.
Draw surface 2 onto surface 1 with slightly less alpha.
Draw surface 1 onto screen.

Next draw cycle... repeat at beginning.

If you are drawing the surface with an additive blend mode you could also just draw a low-alpha black rectangle onto your surface each frame.
That's the way I would approach this, the 'double buffer' method.

Basically, you use 2 surfaces and one local variable ('var'):

1. Contains the surface on which you will draw. something like 'trail_surface'.
2. Contains the surface on which you will draw the first surface, for the fading effect. something like 'trail_fade_surface'.
3. a 'var' variable for swapping

And then you would apply this logic when you draw:

1. clear surface 2 (ex: trail_fade_surface)
2. draw surface 1 (ex: trail_surface) on surface 2 (ex: trail_fade_surface) with opacity ( use draw_set_alpha() before drawing the surface )
3. swap the two surfaces (they are IDs, so we cant just set surface1 = surface2)
4. draw trail on surface 1 (ex: trail_surface) ( don't forget to set draw_set_alpha(1) before this! )
5. draw surface 1 (ex: trail_surface) in your room/gui

Here is an example:
Code:
// I'm skipping validation that surface exists, but you should always do it!

// Step 1
surface_set_target( trail_fade_surface );
    draw_clear_alpha(c_black, 0);

    // Step 2
    draw_set_alpha(0.01); // Make smaller for slower fade effect
    draw_surface( trail_surface, 0, 0 );
surface_reset_target();

// Step 3
var temp = trail_surface;
trail_surface = trail_fade_surface;
trail_fade_surface = trail_surface;

// Step 4
draw_set_alpha(1);
draw_sprite(spr_trail, 0, x, y); // Just an example

// Step 5
draw_surface( trail_surface, 0, 0 );
Hopefully this helps you!
 

andev

Member
NO! Please don't!
In the documentation, it clearly says that you should avoid drawing on a surface outside of the draw event because GameMaker will not be able to optimize rendering if you draw in the step event.
I think he meant step to mean an iteration of all events, and not literally the step event?
Or use bm_subtract?
That would be very hard to handle, specially if trails can overlap each other.
How so? You draw a rectangle over the whole surface with bm_subtract at say 0.05 opacity, then draw all the new sprites that cast trails on top of that at 100% opacity?
 
C

CedSharp

Guest
I rather not take a chance with the step event.

You are correct for bm_subtract, I didn't understand it that way, my bad.
I was thinking you wanted him to draw each trail over their respective older ones using bm_subtract.
I suppose my brain isn't yet fully awaken, even at 3:40pm xD
 

Arashi

Member
What I do:

draw objects on surface "wisp_trail".
clear surface "fade_surf" with draw_clear_alpha(c_white,0);

enable shader, that substracts 0.01 from alpha
draw_set_blend_mode_ext(bm_one, bm_zero);
draw surface "wisp_trail" on "surf_fade"
reset shader
setblend mode "bm_normal"

copy surface "surf fade" on "wisp trail"

draw surface "wisp trail"

What I got:
transparent trails of wisps, that do not fade away.
Here is video: https://2ch.hk/gd/src/393168/15157449296370.mp4


Code:
#region check, if surface exists
if !surface_exists(wisp_trail)
    {
    wisp_trail = surface_create(surf_w_half*2,surf_h_half*2);
    surface_set_target(wisp_trail);
    draw_clear_alpha(c_white,0);
    surface_reset_target();
    }

if !surface_exists(fade_surf)
    {
    fade_surf = surface_create(surf_w_half*2,surf_h_half*2);
    surface_set_target(fade_surf);
    draw_clear_alpha(c_white,0)
    surface_reset_target();
    }
#endregion

#region draw sprites on wisp_trail surface
surface_set_target(wisp_trail)
if instance_exists(obj_wisp)
    {
    var my_id = self;
    var s_w = surf_w_half;
    var s_h = surf_h_half;
    with (obj_wisp)
        {
        if owner == my_id
            {
            draw_sprite(sprite_index,image_index,s_w -(owner.x-x), s_h-(owner.y-y)        )
            }
        }
    }
surface_reset_target();
#endregion


surface_set_target(fade_surf);
draw_clear_alpha(c_white,0);

shader_set(sh_fade);
draw_set_blend_mode_ext(bm_one, bm_zero);
draw_surface(wisp_trail,0,0);
draw_set_blend_mode(bm_normal);
shader_reset();
surface_reset_target();




surface_copy(wisp_trail,0,0,fade_surf);

draw_surface(wisp_trail,x-surf_w_half,y-surf_h_half);
Shader sh_fade is simply substracts alpha every step.

Code:
varying vec2 v_vTexcoord;
varying vec4 v_vColour;

void main()
{
    vec4 col = v_vColour * texture2D( gm_BaseTexture, v_vTexcoord );
    col.a -= 0.01;
    gl_FragColor = col;
}
 
Last edited:
Hi, I saw this post and am having some success with trying to do the same thing, but am having issues with some image permanence. I went the route of drawing a black rectangle of low opacity over the surface every draw frame, but I feel like eventually it reaches a point where there is a very miniscule hanging alpha value of the older trail images that doesn't 0 out.

wewe-export.gif

controller draw:
Code:
surface_set_target(mblur2);

    draw_surface(mblur,0,0);
    draw_set_alpha(.05); draw_set_color(c_black); 
    draw_rectangle(0,0,room_width,room_height,false)
    draw_set_alpha(1); draw_set_color(c_white);
    
surface_reset_target();

surface_copy(mblur,0,0,mblur2);

gpu_set_blendmode(bm_add) 
draw_surface(mblur,0,0);
gpu_set_blendmode(bm_normal)
object draw for trail:
Code:
    surface_set_target(mblur)
    gpu_set_fog(true,c_white,0,0)
    draw_self()
    gpu_set_fog(false,c_white,0,0)
    surface_reset_target()
 
Top