Legacy GM Blood splatters: question about an old tutorial

M

Maisenberg

Guest
So I found this great looking little tutorial on how to do efficient and potentially infinite blood splatters: https://forum.yoyogames.com/index.php?threads/unlimited-blood-splats.1624/

However when I looked at the code the poster provided I knew something was missing. I don't understand how he achieves the effect of drawing a blood splatter only inside the wall, since the code doesn't check for anything related to that. Several users in that threa pointed out the same issue but it's not resolved there. It looks like this for me:

<Image Link Removed - Malicious Domain Detected>/k6OaxQ

As you can see some dots are a bit outside the wall. I acually like how this looks and I might even keep it but I'd really like to know how the effect in that tutorial was achieved. My code is the same as in the tutorial, which is obviously missing something.

Thanks in advance!
 

TheBroman90

Member
I don't know how he created this effect, but I made something similar.
In my game the blood fades out after a while, so I update the surface every step. You can maybe draw it only once if you don't fade.
Oh, and the blood object has to be not visible. Else it draws outside the walls.

Here's my blood surface object.

Code:
/// Create  Event

surface = surface_create(view_wview, view_hview);

Code:
/// Game End Event

if surface_exists(surface) surface_free(surface);

Code:
/// Draw Event

// Create Surface if it not exists.
if !surface_exists(surface)
    surface = surface_create(view_wview, view_hview);
    
surface_set_target(surface);

draw_clear_alpha(c_black, 0);

// Draw walls.
with(obj_wall)
    draw_sprite_ext(sprite_index, image_index, x-view_xview, y-view_yview, image_xscale, image_yscale, image_angle, image_blend, image_alpha);

// Draw Blood.
if instance_exists(obj_blood) {
    draw_set_colour_write_enable(true, true, true, false);
    
    with(obj_blood)
        draw_sprite_ext(sprite_index, image_index, x-view_xview, y-view_yview, image_xscale, image_yscale, image_angle, image_blend, image_alpha);
    
    draw_set_colour_write_enable(true, true, true, true);
}

surface_reset_target();

draw_surface(surface, view_xview, view_yview);
 
M

Maisenberg

Guest
Oops, I just solved this and then saw your response. I fixed it by creating a script that's basically draw_sprite_part but inside the bounding box of a specific instance:

Code:
///scr_draw_sprite_contained(sprite, subimage, inst, x, y)
//Given an instance inst, draws the part of the sprite spr contained in inst's bounding box

var spr  = argument[0];
var simg = argument[1];
var inst = argument[2];
var xx   = argument[3];
var yy   = argument[4];

var _w, _h, _xoff, _yoff, _x1,_y1, _x2, _y2;
_w    = sprite_get_width(spr);
_h    = sprite_get_height(spr);
_xoff = sprite_get_xoffset(spr);
_yoff = sprite_get_yoffset(spr);
_x1   = max(_xoff + inst.bbox_left - xx, 0);
_y1   = max(_yoff + inst.bbox_top - yy, 0);
_x2   = max(xx + _w - _xoff - inst.bbox_right - 1, 0);
_y2   = max(yy + _h - _yoff - inst.bbox_bottom - 1, 0);

draw_sprite_part(spr, simg, _x1, _y1, _w - _x1 - _x2, _h - _y1 - _y2, xx - _xoff + _x1, yy - _yoff + _y1);

Thanks for your answer, though, I would still like to uderstand it. I'm guessing the magic lies in
draw_set_colour_write_enable(true, true, true, false); but what does switching off the alpha channel do for this?
 
Last edited by a moderator:

TheBroman90

Member
what does switching off the alpha channel do for this?
draw_sprite_part
works fine, but then you are limited to draw inside box shapes.
If you wan't to draw to random shapes, you could try drawing to a surface with draw_set_colour_write_enable(true, true, true, false).

First you clear the surface with draw_clear_alpha(c_black, 0), so now we only have alpha.
Then you draw your walls, which have RGB channels.
Then by using draw_set_colour_write_enable(true, true, true, false) you're telling game maker that you want to draw the blood,
but only if there's something with RGB beneath. If we are outside the wall on the alpha pixels on the surface, then skip drawing.
 
Last edited:
M

Maisenberg

Guest

draw_sprite_part
works fine, but then you are limited to draw inside box shapes.
If you wan't to draw to random shapes, you could try drawing to a surface with draw_set_colour_write_enable(true, true, true, false).

First you clear the surface with draw_clear_alpha(c_black, 0), so now we only have alpha.
Then you draw your walls, which have RGB channels.
Then by using draw_set_colour_write_enable(true, true, true, false) you're telling game maker that you want to draw the blood,
but only if there's something with RGB beneath. If we are outside the wall on the alpha pixels on the surface, then skip drawing.
Aha, I see, that's very powerful! I really want to use that instead of my approach, but I have one issue: my walls need to be invisible, as they're just collision blocks (I place tiles over them). I don't really know how to combine this since your method is based on the walls being drawn on the screen, which I don't want (I apologize for not telling that in the original post, it just didn't cross my mind that it could be relevant). I have no idea if there's an easy fix for this or if I'll have to do something completely different. I thought of maybe drawing the tiles to the surface instead of the walls but I'm not quite sure how to do that, and maybe there's a simpler fix.

Right now I have:

Code:
//Create event

damagemask = surface_create(room_width, room_height);
surface_set_target(damagemask);
draw_clear_alpha(c_black, 0);
with (par_wall) {
    draw_sprite_ext(sprite_index, 0, x, y, image_xscale, image_yscale,
        image_angle, image_blend, 1);
}
surface_reset_target();
Code:
//Draw event

if surface_exists(damagemask) {

    if instance_exists(obj_wall_damage_effect) {

    surface_set_target(damagemask);

    draw_set_colour_write_enable(true, true, true, false);
    with (obj_wall_damage_effect) {
        draw_circle_colour(x, y, irandom_range(4, 30), c_red, c_blue, false);
        instance_destroy();
    }
    draw_set_colour_write_enable(true, true, true, true);

    surface_reset_target();

}
    draw_surface(damagemask, 0, 0);

} else {


    damagemask = surface_create(room_width, room_height);
    surface_set_target(damagemask);
    draw_clear_alpha(c_black, 0);
    with (par_wall) {
        draw_sprite_ext(sprite_index, 0, x, y, image_xscale, image_yscale,
            image_angle, image_blend, 1);
    }
    surface_reset_target();
}
Just drawing a circle for testing. The walls are only drawn at creation because since I destroy my "blood" objects I don't want to draw again on them

EDIT: So I finally got this working with invisible walls! I created two surfaces. First, I draw the wall effects on one surface. Then, on the other surface, I draw the walls normally, afterwards I set the blend mode with
Code:
draw_set_blend_mode_ext(bm_dest_alpha, bm_zero);
draw the surface with the effects, and finally draw this second surface on the screen.
 
Last edited by a moderator:
Top