#1 Attack Collisions
From animations to collisions
In the last entry on this series I stated that players could perform movements and attacks using the character controller I wrote, but that was just a partial truth. Although players could trigger attacks, only their animations would play.
A collision system for a project like this can grow quite complex, so I left it out for the time being to keep things short and simple in the character controller. However, that is what I worked on next and what I will be covering in this post.
In the case of hammer-time, its collition system had the following requirements:
- The
PlayerCharacter
should be able to attack both enemies and elements in the environment (for convenience, I will refer to the group of them asInteractables
from now on). Interactables
shouldn’t need a reference to thePlayerCharacter
in order to work, or vice versa.- The
PlayerCharacter
shouldn’t care whether anInteractable
is an enemy or an element in the scene. - Since the
PlayerCharacter
has multiple attacks to choose from, I wanted theInteractables
to be able to react differently to them if necessary.

The interactable hurtboxes
I began by creating an InteractableHurtbox
node that would receive (and forward using signals) information about player attacks. This node is meant to be placed as a direct child of any detectable Area3D
by the player. This hurtbox would be checked for using a constant, relative NodePath
:
var hurt_box: InteractableHurtbox = detected_area_3d.get_node_or_null(INTERACTABLE_HURTBOX_RELATIVE_PATH)
This way the logic of the hurtbox is decoupled from the physics nodes using it. It might seem confusing, but the result is that creating a new Interactable
is as easy as:
- Adding an
InteractableHurtbox
inside anArea3D
. - Connecting the signals of the hurtbox that the new
Interactable
reacts to. - Writting the desired behaviours in the callback methods.

The player attack hitboxes
With the interactables ready, the next step was for the PlayerCharacter
to actually look for them when attacking. I needed to make sure that my approach fit nicely with the character controller and the finite state machine I already had.
I first created a new direct child in the player scene, attaching a PlayerCharacterHitboxes
script to it. This node parents all Area3D
hitboxes that the attack animations might use and sends a signal whenever an InteractableHurtbox
is detected. After that I updated said animations to include and fit them to the reach of each attack.
Lastly, I added a reference to PlayerCharacterHitboxes
in the base attack state of the FSM. Every time an attack state is entered/exited it connects/disconnects the exposed signal. The callback method sends the appropiate data to the hurtbox.
func on_interactable_hurtbox_hit(hurt_box: InteractableHurtbox) -> void:
hurt_box.receive_player_attack(_attack, _attack_type, player_character.global_transform)

Wrapping up and some examples
The two components I talked about so far, hurtboxes and hitboxes, are all that is needed to set up a basic collision system. On top of that, the use of forward signals really helps keeping things decoupled while at the same time providing modularity and scalability.
In the video above, the PlayerCharacter
attacks three different implementations of Interactable
that I wrote to showcase some of the cool stuff that can be done with this workflow:
SimpleInteractable: The small green ball. It does not move, simply shakes and changes color whenever an attack is received. (It also triggers camera shake and time freeze effects, but that is out of the scope of this article!)
PhysicsInteractable: The big orange ball. It is affected by gravity and collisions. It can be moved and thrown differently depending on the attack received. This interactable is fed a resource file that contains the data of the forces and torques to apply per attack.
LaunchablePhysicsInteractable: The pink cube. It extends
PhysicsInteractable
to include a launch functionality. Attacking this cube in the direction of another interactable launches it flying towards its target causing a chained attack.