Skip to content

Collision Detection and Handling

DDR edited this page Feb 7, 2023 · 7 revisions

Level Collisions

There are two collision systems used simultaneously in frogatto. The first is the "solidity" system, which is used to track movement of objects, and keeps objects from overlapping as they move around. This system is fairly fine-grained and has internal logic to keep objects from passing through terrain/each other if their velocity in a single cycle was big enough to put them far on the other side of a barrier.

This first system has an additional property called "solid_dimensions". An object may be in any number [ed. note: < 32 or 64, iirc?] of named "dimensions", and it may exist in these dimensions "weakly" or "strongly". Two objects will only collide with each other if they share at least one dimension, and at least one of them is in that dimension strongly. This is useful in Frogatto, for instance, if you want to make it so the player can 'walk through' NPC's who are meant to be in the background, but they still collide with each other and the level. You simply make the NPCs exist in some separate NPC dimension (this would be done by setting solid_dimensions="[npc]" in the object file for that NPC).

This property is accessible from FFL as "solid_dimensions_in".

"Object"/"Named Area" Collisions

The second is a less rigorous system using "named" areas inside an object's sprite area, which gets used to mark off certain areas for special behavior. We use this right now to mark areas that are offensive (like the tip of a sphere), to mark which parts of a sprite's body are vulnerable to damage, and even to do other things like marking which parts of a sprite are valid for triggering speech, opening of doors, flipping of levers and such.

These named types are not hard-coded in the engine, they're simply established by convention in the current game files. We use "attack" for hostile parts, "body" for vulnerable parts, "interact" for parts triggered by some action involving the interact key (talking, opening, etc), and "thrown" is also used for objects spat by frogatto.

To create these areas, you add a key named xxx_area=x1,y1,x2,y2 inside one of the object's [animation] tags, where xxx is the name of the type of area you want to create. Naturally, this means it's only valid inside that animation (useful for attacks). If you want something to be true for all animations, you would either copy/paste it to all of them, or put it in [base:animation].

Once you've defined such an area in an object, that same object needs to supply code for what it does when that area is collided with. When that area is collided with by any other area of a different object, the game will fire an on_collide_object_xxx="code goes here" event. For example, if something hits our body, it will trigger a on_collide_object_body="" event. From inside this event, there's a variable we can access called "collide_with" that describes the foreign object, and the part of the foreign object we bumped into. We need to query this to find out of the bumped-into part of the foreign object is something harmless, like it's body, or is something harmful, like its attack. We do this by querying: "if(collide_with_area = 'attack', get hurt)".

One note on object areas is that they function on a pixel-opacity basis; they only hit if the involved pixels are opaque. This is helpful to make e.g. enemy shots only hurt the player if they actually, directly are seen to touch the player. This is on by default, so just by defining a rectangle, only the things inside which are opaque actually trigger a collision.

For a variety of reasons, this isn't always desirable, to turn it off and make all pixels inside the rectangle, opaque or not, trigger a collision, use the syntax body_area: [21,10,41,31,"solid"], inside an object's animation data. One major benefit is that if you're using this "solid" flag, the rectangle can be any size (the coords are relative to the image frame), and thus you can make a collision rectangle much bigger than the source image, using e.g. body_area: [-20,-20,60,60,"solid"]

There's a shorthand for making the rectangle for the frame encompass all pixels of the image, which is body_area: "all", To combine this with solid, use body_area: ["solid","all"],

Chat conversation on the subject:

The following is a raw dump of an IRC conversation which restates most of the above in slightly different words, which might make it more comprehensible to some. (I figure this is better than just deleting it, since this might be just the thing to bust up confusion for some people).

[11:05pm] Sirp: rujasu: so there is what I call "solid collision detection" and "object collision detection"

[11:05pm] rujasu: ah, okay.

[11:06pm] Sirp: solid collision detection rigorously checks if two objects collide or an object collides with the level on a pixel-by-pixel basis.

[11:06pm] Sirp: objects have a 'solid area' and objects are moved pixel by pixel, if a movement would cause solid areas to overlap it is disallowed, and an event triggered.

[11:07pm] Sirp: this is useful for any kind of collision where you don't want an object moving through something -- i.e. to stop objects moving through walls or falling through floors.

[11:08pm] Sirp: object collision detection on the other hand takes place on a frame-by-frame basis. Objects may have different areas defined as rectangles within their frames

[11:08pm] Sirp: if two such rectangles of different objects overlap, a collision event is generated.

[11:08pm] Sirp: however, the game engine does nothing intrinsic when an object collision occurs -- it's completely up to the FFL to do something interesting (if something is necessary at all)

[11:09pm] rujasu: Ahhhhhhhhhh

[11:09pm] Sirp: object collisions are generally useful for e.g. projectiles fired at a player ... or for detecting if the player can enter a door ... things like that.

[11:09pm] rujasu: (that clears some things up a bit)

[11:09pm] rujasu: right, right

[11:09pm] Sirp: object collisions are also much more efficient, so they are preferred unless you need the rigor of solid collisions.

[11:10pm] Sirp: ...one other note about solid collisions, we have this concept known as "solid dimensions"

[11:11pm] Sirp: an object may be in any number of named "dimensions", and it may exist in these dimensions "weakly" or "strongly"

[11:11pm] Sirp: two objects will only collide with each other if they share at least one dimension, and at least one of them is in that dimension strongly.

[11:12pm] Sirp: this is useful in Frogatto, for instance, if you want to make it so the player can 'walk through' NPC's who are meant to be in the background, but they still collide with each other and the level.

[11:12pm] Sirp: just make the NPC's in some separate NPC dimension.

[11:14pm] rujasu: hmm, okay.

[11:18pm] rujasu: Sirp - so, how to detect an object collision?

[11:19pm] Sirp: rujasu: it will fire an event, on_collide_object_xxx

[11:20pm] Sirp: where xxx is the area within your object that was collided with

[11:20pm] Jetrel: rujasu: e.g. the engine does the detection work, you just do the handling of it after the fact.

[11:20pm] Sirp: and then it will pass in collide_with as the object you collided with and collide_with_area as the name of the area within that object you collided with

[11:20pm] Sirp: rujasu: e.g. a common idiom: on_collide_object_body="if(collide_with_area = 'attack', ...ouch we got hit, take damage in here...)"

[11:21pm] Jetrel: Which brings us to another subject - dave says "xxx" is the area; each of these areas can be given a different name (which could easily be called a "type", if you prefer).

[11:21pm] Jetrel: So, you can name a certain collision area something, and it lets you limit when that kind of collision occurs.

[11:22pm] Sirp: another more subtle use of collisions is for doors ...

[11:22pm] Sirp: doors define a collision area called 'interact'

[11:22pm] Jetrel: Sometimes when frogatto touches an enemy, you want him to hurt the enemy; sometimes you want the opposite. So we use "attack" to designate the parts that are harmful, and "body" to designate the parts that are harm-able.

[11:22pm] Sirp: so when frogatto's body collides with an 'interact' area we set the object collided with as the 'interactable' object

[11:23pm] Sirp: so then if the player presses up at that point we trigger an interaction between frogatto and the interactable object.

[11:23pm] Jetrel: For example, we don't want frogatto's tongue to be susceptible to damage, otherwise he'd get hurt all the time.

[11:24pm] Sirp: rujasu: yet another subtlety is that the collision detection actually uses pixel-by-pixel detection. It takes non-alpha pixels within the rectangle specified as the hit area

[11:25pm] Sirp: however there is a way to turn this off and make it do it just by the rectangle.

[11:28pm] rujasu: hmm, okay.

Clone this wiki locally