Effects: Blood Splatter

Hi, all.

First, I’d like to apologize for slowing down these tutorial series. Things have been a little rough since my largest contract project fell through. For the most part, I’ve been scrambling to jump on something else. Sending out prototypes, replying to job offer emails, etc. No big decisions have been made, but I’m still hopeful.

Anyway, I’ve been told time and time again to do a series on all of my little ‘juice’ effects. Dust when you land from high jumps, screen shake, smoke from bullet fire, etc, etc. I’m going to start that today!

However, I’m going to start with one that’s new to me. Mostly because it’s the most interesting to me, currently. That way you’ll get to see my thought process as I piece these things together.

Blood Splatter

Recently I played through Hotline Miami 2, as I am sure that a lot of you did, as well. Each kill is accompanied with a fairly brutal blood splatter. I wanted to see if I could piece together a top-down dynamic blood splatter that achieves a similar effect.

Final Effect

Surfaces

Firs thing’s first, if you don’t know much about Game Maker surfaces, now is your just to go study up on them. Most of my effects are temporary. They fade away after a few frames. This blood is here to stay. We don’t want 1000 blood objects stacking up as you plow through hordes of enemies, so we’ll draw the blood to a surface, destroy the blood object, and continue to redraw the surface without clearing it (the blood will remain drawn after the objects have been cleaned up).

I do this with an object specifically for drawing the blood. Let’s call it ‘oControllerBlood’.

/// CREATE EVENT ///

// Create the surface, preferably as large as the room
surf = surface_create(room_width, room_height);

/// DESTROY EVENT ///

// Remember to clean up the memory used for your surface
surface_free(surf);

/*
Note:

It may actually be necessary to copy this into a ROOM END EVENT, as well.
I don't believe this is called if a room ends prior to the object being destroyed.
*/

/// DRAW EVENT ///

// Draw blood surface
if (surface_exists(surf))
    draw_surface(surf, 0, 0);
// Create new surface if DX destroys old surface due to port resize
else
    surf = surface_create(room_width, room_height);

Again, DirectX requires that all surfaces be destroyed when the user messes with window resizing. This will create a new surface if that happens. More can be done to make this work better. I'll return to it later, but it's not too important for now.

But that's it! That will handle our blood surface. Make sure to put one of these bad boys in your room before you start spawning blood objects.

BLOOD

Ok, now for the actual blood. What I like about Hotline Miami's blood splatters are the outliers. The long streams that should out at seemingly random angles. It's not just a pool of blood. It's a somewhat natural splatter. I don't want a blob. It shouldn't look like it was poured. It should look impactful, like you stomped in it.

I started by making a blood sprite. It's simply a red circle. 16x16, I believe. I have to think ahead to how I'm going to spawn this group of objects. I want the point of impact to be fairly closed. It'll be a puddle if different segments stretching out in several directions, so the sprite needs to be big enough that the initial set of objects overlap and form that puddle.

Here's what I came up with for the CREATE EVENT:

/// CREATE EVENT ///

// This probably isn't necessary, but I'd like *some* randomness to the initial puddle
image_xscale = random_range(0.33, 1);
image_yscale = image_xscale;

// Simple, move each blob in a random direction
movDir = random(360);
// The is a drastic range because I'm hoping for those big streaks 
// The low numbers will stay around the puddle, while the high numbers create quick streams
movSpd = random_range(3, 12);

// I want the blood to start out very fast, but also slow down incredibly fast
// The overall splatter will last 2-8 frames
fric       = random_range(movSpd / 8, movSpd / 2);

// As the blood spreads, it thins out
// This creates the *points* at the end of each stream
// The blood will spread for 3-10 frames
sizeChange = random_range(image_xscale / 10, image_xscale / 3); 

This is what I ended with. It was actually pretty close to what my first attempt was, but that's not always the case. These effects are often about trial and error. Try to picture what the effects are ACTUALLY doing. In this case, I knew that I wanted a HUGE initial impact with an equally HUGE decay. Fast moving with loads of friction.

/// STEP EVENT ///

// Thin out the blood as it spreads
image_xscale -= sizeChange;
image_yscale  = image_xscale;

// I also decided to fade the blood out by a random percentage as it spreads
// These values seemed to feel right
if (movSpd > 0)
    image_alpha -= random_range(0.05, 0.1);

// Slow down the blood with friction
movSpd = Approach(movSpd, 0, fric);

// I decided to draw the blood to the surface here
// This isn't really necessary, you could put this somewhere else
if (instance_exists(oControllerBlood)) {
    surface_set_target(oControllerBlood.surf);
    draw_sprite_ext(sBlood, 0, x, y, image_xscale, image_yscale, image_angle, c_white, image_alpha);
    surface_reset_target();  
}

// Once the blood thins away to nothing, destroy it
// Destroy self
if (image_xscale <= 0)
    instance_destroy();

/// END STEP EVENT ///

// Actual movement
// Feel free to use h/vspeed if you don't want to do this yourself
// This is also a bunch of needless sin/cos calls
x += lengthdir_x(movSpd, movDir);
y += lengthdir_y(movSpd, movDir);

Be sure to put SOMETHING in your draw event to stop the object from drawing itself. The blood surface will draw it. If not, it will draw twice until the object is destroyed.

Test

Lastly, I added a small test to see how we can actually use these objects. Here I pack a group of blood objects together on every mouse click. I spawn between 4 and 32 blood objects so that you can see the different shapes that we create depending on the density of the initial impact.

/// STEP EVENT OF MY GAME CONTROLLER OBJECT ///

// Amount of blood
var count = random_range(4, 32);

// Left-mouse click
if (mouse_check_button_pressed(mb_left)) {
    for (var i = 0; i < count; i++) {
        // Spawn blood within an 8x8 square around the mouse cursor
        instance_create(mouse_x + random_range(-8, 8), mouse_y + random_range(-8, 8), oBlood);
    }
    
    // Screenshake
    alarm[0] = 4;
    shake    = true;
}

Hope this helps! Maybe it will be enough to get some you thinking about how to accomplish the other dynamic effects that you need.

-Z

Effects: Blood Splatter

3 thoughts on “Effects: Blood Splatter

  1. Epos Nix says:

    That was amazingly amazing. Amazingly so…! I put your code in a little physics sandbox I’m playing with and, well… my game much more colorful now! Check it out:

    Like

  2. cyred says:

    Zack, there is no such thing as an “approach command”
    “// Slow down the blood with friction
    movSpd = Approach(movSpd, 0, fric);”

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s