GML Separate hit boxes for single instance? (Headshots, etc)

Dr_Nomz

Member
In a lot of games hitting different body parts can run different scripts against the affected entity, such as double damage or stun/stagger. How do I do that in GMS 1.4? Is there a tutorial or do I just use separate objects connected to the entity?
 

kburkhart84

Firehammer Games
I've heard of both ways. A single instance can check for stuff within various boxes manually, or you can have a single instance spawn "collider" instances that help control those, and those can tell the controlling instance when there were hits.
 

Director_X

Member
As posted above, seperate invisible collider objects would be simplest to do. Unless you have a 100 enemies on screen - all with 5 colliders on them! That might not be efficient.
 

Dr_Nomz

Member
What would be the simplest for animated enemies? The way I'm doing it is making the single object handle all animation, so would it be easy enough to apply those changes in position to any "hitbox" objects, or would the other method work better?

BTW what functions are best for the other method? I don't even know where to start for that.
 

NightFrost

Member
BTW what functions are best for the other method? I don't even know where to start for that.
In a single-object method, instead of having actual hidden collider objects you simulate them instead. Let's say you have a simple 20*20 pixel hitbox which for targeting purposes needs to be divided into upper and lower halves. You'd store into variables the x/y positions of every hit area, here being 0,0 to 20,10 for the upper half and 0,10 to 20,20 for the lower half. When a collision is registered, you loop through these boxes and check whether the colliding hitbox overlaps with these. First you compare the target's and collider's positions and calculate the relative position of the collider's hitbox. Overlap check would then be a rectangle-in-rectangle check, you could even use point check if it is a single-pixel bullet hitbox. Note that sprite origins affect these since it determines where the 0,0 origin point is inside the hitbox.

This all also assumes the boxes are not rotated. Checking overlap of two rotated boxes is somewhat more involved math. In other words, unless your multi-hitbox system is a simple affair, it may be best to leave the complexities for the game engine to handle and use invisible hitbox objects that report collisions to main object. They should check and report collisions in a Step event before the one where main object would look at these reports (ie Begin Step and main Step). Otherwise the reporting may lag behind one frame due to execution order.

Finally, it also should be considered that both methods can report hits to multiple target boxes at the same time, either because they overlap due to animation or design, and/or because the collider is larger than one pixel and overlaps multiple target boxes. Which hit to prioritize is then a judgment call the code must handle.
 

Dr_Nomz

Member
Okay but what if I want to use precise collisions? Say for example I check for the area just above the shoulders, will it be able to check the precise area of that mask, or no?
 

NightFrost

Member
I assume you're asking about single object method, as with invisible colliders you'd also use precise collisions. But, yes. You first check if there's a precise collision, which guarantees there is some overlap between collision masks. Then you can go through checking overlapping rectangles to figure which area(s) you've defined are touching.
 

Dr_Nomz

Member
So just to be clear, that would CHECK the rectangular area and define that "segment" of the hitbox, but all collisions would only hit it precisely, not rectangularly?
 

NightFrost

Member
Yeah, since you run a precise collision check first and it returns true, you know there is pixel overlap somewhere. So it logically follows when you go through the segment boxes, at least one of them is touching the collider's box. For debug purposes you may want some code in your Draw event that draws the segments with low alpha value, then set up overlaps in room editor and have them not move.
 

Dr_Nomz

Member
I tried point in rectangle, which worked for a rectangular check, but didn't seem to check base on the sprites own mask:
GML:
if collision_rectangle(x+0,y+0,x+50,y+25,obj_Bullet,true,true){
  draw_sprite(spr_Hit_BOX,0,x,y+200);
}
But it only works if I manually add the check in the same event, meaning I can't just set it as a variable in the create event and use that. Why is that? Alternative I CAN make that work using the var method, but again only in the same event.

I tried this, but it only checks the center origin of the bullet, and not the mask. It also checks a rectangular area without checking the object's mask:
GML:
if point_in_rectangle(obj_Bullet.x,obj_Bullet.y,x+0,y+0,x+50,y+25){
  draw_sprite(spr_Hit_BOX,0,x,y+200);
}
All of my sprites have precise collision checking enabled, and I'm trying to check the upper and lower halves of the sprite's precise collision mask. How do I do that?
 

saniblues

Member
Differential collision masks? I do a lot of different silly stuff for that.
GML:
function place_meeting_ext(_x, _y, _obj, _mask, _xsc, _ysc, _ang){
    // Remember these for later
    var xx = x, yy = y, msk = mask_index, xsc = image_xscale, ysc = image_yscale, ang = image_angle;
    // Move to position and apply fields
    x = _x; y = _y; image_xscale = _xsc; image_yscale = _ysc; image_angle = _ang;
    // This is the bread and butter. Try not to stand up too fast!
    var _r = place_meeting(x,y,_obj);
    // Move everything back
    x = xx; y = yy; mask_index = msk; image_xscale = xsc; image_yscale = ysc; image_angle = ang;
  
    return _r;
}
I don't know how abhorrent this is, but it werks™ for sprite-based collisions, so I'll do it when I'm feeling lazy. A limitation of that is that you need to handle your collisions in a step event, but it means significantly less setup.

There are several other options in this thread, I'd consider looking at those as well.
 

Dr_Nomz

Member
Yeah I've given it some thought, and I think I might just use object based collision rather than complex scripting. It should make it easier to have enemies that can have specific limbs targeted (like individual arms/legs) but thanks for the suggestions.

BTW I don't understand anything you just posted. Why is "function" written at the start? And does it say return at the bottom because it's supposed to be a script? I'm using GMS 1.4 btw.
 

kburkhart84

Firehammer Games
Yeah I've given it some thought, and I think I might just use object based collision rather than complex scripting. It should make it easier to have enemies that can have specific limbs targeted (like individual arms/legs) but thanks for the suggestions.

BTW I don't understand anything you just posted. Why is "function" written at the start? And does it say return at the bottom because it's supposed to be a script? I'm using GMS 1.4 btw.
There is nothing wrong with doing object based collisions....in many cases its easier to handle, and unless you have way too many, the performance is not an issue either.

And the reason it has function written is because as of version 2.3 there is new syntax for scripts/functions. You can just think of it as a script, the part that is inside the brackets. With the new syntax, the arguments come in with the names specified as well so you don't have to use argument0 etc.. anymore either. The below is the 1.4 version. I didn't check if it works or anything though, just a translation.

Code:
// Remember these for later
 var _x = argument0;
var _y = argument1;
var _obj = argument2;
var _mask = argument3;
var _xsc = argument4;
var _ysc = argumen5;
var _ang = argument6;

    var xx = x, yy = y, msk = mask_index, xsc = image_xscale, ysc = image_yscale, ang = image_angle;
    // Move to position and apply fields
    x = _x; y = _y; image_xscale = _xsc; image_yscale = _ysc; image_angle = _ang;
    // This is the bread and butter. Try not to stand up too fast!
    var _r = place_meeting(x,y,_obj);
    // Move everything back
    x = xx; y = yy; mask_index = msk; image_xscale = xsc; image_yscale = ysc; image_angle = ang;
  
    return _r;
 
Top