7 min read

Character Controller

Table of Contents

What is a Character

Characters in your game are dynamic entities capable of moving and performing various actions within a level. By using a StateMachine node, you can configure and manage different states to control their behavior seamlessly.

State scripts interact with functions defined in the CharacterEntity class (or any script that extends from it). The pre-built system allows your characters to:

  • Move
  • Run
  • Jump
  • Attack
  • Flash (for example, when receiving damage)
  • Interact with other game elements and entities

These capabilities can be further expanded by adding new functions to the character’s script and customizing the states defined in the character’s StateMachine.

Additionally, you can integrate a HealthController node to manage a character’s health points, making it easy to handle damage, healing, or showing a custom health bar.

Pre-built Characters

The template includes two pre-built characters: a player (player.tscn) and an enemy (enemy.tscn). Both characters share the same base script, character_entity.gd. The difference lies in their implementation:

  • The player extends the CharacterEntity class through the player_entity.gd script.
  • The enemy uses the character_entity.gd script directly.

You’re free to create a custom enemy_entity.gd script that extends from CharacterEntity to add specific functionality for enemies that are not present in the base class.

Player

What makes the player character unique is the ability to respond to user inputs. This functionality is managed by a specialized state called StateInputListener. By adding this state to any character, you can make it responsive to inputs, not just the player. The actions that StateInputListener listens for are defined in Project Settings > Input Map. You can customize the controls to fit your needs. Furthermore, the player character includes:

  • a StateMachine node with pre-built states to handle movement, attack, hurt, fall, and death.
  • a InteractionTrigger node to detect interactions with other game elements.
  • a SmokeParticles node for visual effects when the character runs.
  • an Inventory node for managing items.
  • a HealthController node to manage health points.

Enemy

The enemy character is a simplified version of the player character. It includes a StateMachine node to handle death. The enemy character also includes a HealthController node to manage health points.

Create a New Character

To create new characters, simply create a new scene inheriting from the entity.tscn scene.

Inheriting from the entity.tscn scene

This scene serves as the foundation for all characters, including the pre-built ones in the template. Features of the entity.tscn scene include:

  • Pre-Built animations: idle, walk, run, jump, and attack animations managed by an AnimationPlayer node and an AnimationTree.
  • Dynamic shadow: a Shadow node that adjusts its size dynamically based on the character’s height (e.g., when the character jumps).
  • Flash shader: a shader applied to the AnimatedSprite2D node that creates a flash effect, useful for visual feedback (e.g., when the character takes damage).
  • Collision detection:
    • the BlocksDetector node detects collisions with walls and obstacles.
    • the FallDetector node identifies when the character has jumped into a non-walkable area.
  • Combat features:
    • the HitBox node detects collisions with enemies.
    • the HurtBox node detects collisions with enemy hitboxes.
đź’ˇ

For more specialized characters, you can also inherit from the player.tscn or enemy.tscn scenes.

Character Actions

The CharacterEntity class includes several functions that you can call to perform actions on the character.

Move

The move() function moves the character in a given direction. This function is used by the StateInputListener state to move the player character based on user inputs and also by other states to move the character in a specific direction. When the character moves, the facing property is updated to reflect the direction of movement and the is_moving property is set to true.

Run

To make the character run, you just need to pass a value higher than 1 to the property speed_multiplier. This value is used to multiply the character’s speed when moving. In the StateInputListener state the speed_multiplier property is updated to make the character run when the run action is pressed. When the character runs, the is_running property is set to true and the running_particles, if present, are activated.

Jump

The jump() function makes the character jump. In the StateInputListener state the jump() function is called when the jump action is pressed. In the jump() function, the is_jumping property is set to true and other properties are updated to manage the jump. At the end of the jump, you have to call the end_jump() function to reset the character’s properties. Ideally this function should be called when the character lands on the ground. In a CharacterEntity this is handled by the jump animation of the AnimationPlayer.

Attack

The attack() function makes the character attack. In the StateInputListener state the attack() function is called when the attack action is pressed. In the PlayerEntity, the value of the is_attacking is handled by the attack state of the StateMachine with a StateParamsSetter state. At the end of the attack, you have to call the end_attack() function to reset the character’s properties. Ideally this function should be called when the attack animation ends. In a CharacterEntity this is handled by the char-attack/ animations of the AnimationPlayer.

Flash

The flash() function makes the character sprites flash. To make the sprites flash:

  • The sprites must have a shader material with the flash.gdshader shader.
  • The sprites must be in the flash group.

Interact

To enable interactions, a character requires the following:

  • An Area2D node with the monitorable property set to true.
  • The collision_layer property of the Area2D must match the collision_mask property of an InteractionArea defined within a StateInteract state.

Check the Interaction System for more details.

Health Controller

The HealthController is a versatile node that can be added to any character or entity (or node) to manage its health points (HP). It keeps track of the current HP value and provides a simple way to handle health-related events. If you want to display a visual representation of the entity’s health, you can assign a scene to the health_bar property, which will automatically update based on the current HP.

The behavior of the Health Controller can be customized using states from the StateMachine . You can define what happens when HP increases, decreases, or reaches zero. This makes it easy to trigger effects such as damage animations, healing effects, or death sequences.

By default, both the player.tscn and enemy.tscn scenes include a HealthController node, allowing you to easily manage their health status. If needed, you can extend this system by adding a Health Controller to other entities and customizing how they react to HP changes.

Health Bars

A Health Bar provides a visual representation of an entity’s health, making it easier for players to track damage and healing. To display a health bar for any character or entity, assign a scene to the health_bar property of the HealthController. This allows the health bar to automatically update whenever HP changes.

For an example of how to set up a health bar, check the pre-built player_hud.tscn scene. This scene demonstrates how to properly configure a health bar to follow an entity and visually represent its health status. You can use it as a reference to create custom health bars for enemies, NPCs, or other game elements.

Is something unclear, or do you have suggestions to improve a part of this document? Feel free to share your thoughts leaving a comment below!