So you want animation trails in Game Maker

screenshot101.png

I believe I know what you yearn for. You wish to forgo the bounds of the typical behaviour of a game engine, and make your game's moving elements leave conspicuous trails as they animate - that is, to prevent the screen from refreshing after each frame, and repeatedly draw the sprites onto the same canvas. Well, Game Maker, at least from version 6 onward, is not set up to easily permit this kind of nonsense.

Let's consider the drawing sequence of Game Maker 6+ :
* First, the screen's buffer still possesses the screen image from the previous frame.
* If viewports are enabled, the buffer is blanked.
* If the background color is visible, it is drawn over the entire buffer.
* If there is a background image, it is drawn over the buffer.
* All instances and tiles are drawn, in descending order of depth.
* The buffer is refreshed to the screen, or the viewports if views are enabled.

This sequence is unalterable. So, here are some ways of allowing the previous frame to persist:

* Don't have any views, and have a static background

What causes the buffer to be obliterated in each frame is the presence of viewports, and the drawing of backgrounds. So if you eliminate these, the buffer's contents will persist.

On the first frame of the room, the background can be drawn. But from then on, it must be invisible. So, include an object with this code:

Create event:
alarm[0] = 1;

Alarm 0 event:
for(i=0; i<8; i+=1) {
background_visible[0]=0;
}
background_showcolor = 0;

The main advantage of this code is that it works in Lite and Pro versions, and, miraculously, even works in the HTML5 engine of GM Studio. But, of course, losing the use of views constrains you to a single-screen room, unless you feel apt to re-implementing scrolling and such on your own terms. So you might want a more technically strenuous solution.

* Use a surface as an alternative screen buffer

If the screen buffer cannot be preserved, devise a replacement.

You might do this by creating an object with a large negative Depth value with this code:

Create event:
s = surface_create(view_wview, view_hview);
surface_set_target(s);
screen_redraw(); // Draw the room's initial state onto the surface
surface_reset_target();

End Step event:
surface_set_target(s);
with all
if (visible && sprite_exists(sprite_index)) {
// This implies that only view 0 is being used
draw_sprite_ext(sprite_index,image_index,x-view_xview,y-view_yview, image_xscale,image_yscale,image_angle,image_blend,image_alpha);
// Replace that with event_perform(ev_draw,0) if it is necessary
}
surface_reset_target();

Draw event:
draw_surface(s,view_xview,view_yview);

What this does is create a surface, draw the first frame of the room on it, and from then on, forcibly draw all of the room's sprites onto the surface before Game Maker runs the drawing sequence, and then, at the end of the drawing sequence, draw the surface over the entire screen, obscuring the screen with your own surface.

This has a number of slight pitfalls. Firstly, there's really no way of accurately replicating GM's instance drawing without prior knowledge of how the objects are implemented. If an object has a Draw event, then that event is performed instead of drawing the sprite. But if it does not (and there is no way for the code to find this out) then it must be drawn using draw_sprite_ext(). In order to ensure that the instances are drawn ideally, you must either give each object a Draw event (one in which it simply draws its own sprite) or forgo Draw events in all of your objects.

Secondly, since this doesn't prevent every object from being drawn normally during the drawing sequence, the Draw events for the objects are still performed. If you call the Draw event for all instances in the End Step event, then they are thus made to happen twice per frame. If you were foolish enough to put game logic inside a Draw event (and you would be very foolish to do so) then you must sort that out yourself. (You could certainly prevent the objects from being drawn normally by forcing visible=0 during the loop in the End Step event, but only if you're comfortable obliterating any values for visible that were already there.)

This has an interesting effect when combined with views. The surface moves with the view, so it often provides the illusion that no motion is occurring. If the view is tracking the player's sprite as it moves to the right, the surface moves to the right at the same speed, so the player's sprite seems to leave no trail at all. This is a counterintuitive but nonetheless logical conclusion, and may well be desirable if you are interested in making your game as "authentically glitchy" as possible.

Now, you might instead want an alternative: a surface which is stationary relative to the view, and covers the span of the entire room, so that the sprites always paint a trail regardless of the view's movements. The biggest problem with this is that, if your room is, say, 12000x480, then you quite probably won't be able to make a surface large enough without running out of video RAM. Nevertheless, if you feel your game is capable of doing this within reason, then feel free to modify that example code to suit this solution.

Attached is an example program that demonstrates these methods, in the objects o_trails_basic and o_trails_surface. The included room only demonstrates the latter object, and contains a view which follows the red sprite's movements.

AttachmentSize
Trails_code_example.gmk24.02 KB

Comments

Zecks's picture

im dumb

edit: nothing

dessgeega's picture

i'll try and do something

i'll try and do something with this next klik of the month.