GM:S 1.4 Blood splatters: question about an old tutorial

Discussion in 'Programming' started by Maisenberg, Aug 23, 2017.

  1. Maisenberg

    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!
     
  2. TheBroman90

    TheBroman90 Member

    Joined:
    Jun 20, 2016
    Posts:
    367
    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);
     
    Maisenberg likes this.
  3. Maisenberg

    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: Aug 24, 2017
  4. TheBroman90

    TheBroman90 Member

    Joined:
    Jun 20, 2016
    Posts:
    367
    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: Aug 24, 2017
  5. Maisenberg

    Maisenberg Guest

    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: Aug 25, 2017

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice