A downloadable node

Download NowName your own price

This node is a great starting point for a complex character controller. The current setup is designed for a Kinematic2D node, but it can be quickly changed to work with RigidBody2D

This is an advanced implementation of a character controller,  It is especially useful for complex behavior patterns.

The advantage of this style of player controller is that it is highly modular, keeps code segmented to prevent spaghetti, and lets each state be totally self contained.

The state pattern can be replicated to control any physics body and the input section can be deleted to create a great controller for an npc or enemy.


NOTE :  You must implement your own states to make use of this package a demo is given.

README


Set Up

  • Extract files directly into your project folder.
  • Connect templates to path : Project -> Project Settings -> Application -> Editor -> Script Template Search Path 
    • Edit the "Script Template Search Path" to point to "AdvancedKinematic2DPlayerController/script_templates/"
      • OR
    • Copy contents of script_templates to your own templates folder
  • Add Globals.gd as Autoload

Before you begin

Please read through 

  1. KinematicPlayerController2D.gd
  2. PlayerState.gd
  3. PlayerStateTemplate_COMMENTED.gd

Inputs

Inputs are set up entirely via code and values are assigned to a dictionary of raw values, this is then parsed into something usable at the start of every frame.

Make sure to make appropriate changes to the raw_input_struct which holds the raw input values and the parsed_input_struct which holds the values you will use regularly in code.

raw_input_struct :

Updated with every input event. This could be any number of times in a frame. 

parsed_input_struct :

Updated at the start of every frame. This means inputs are polled only once in a frame. The intention is that parsed_input_struct is used by states for triggering state changes.

States

States are treated similarly to cartridges for old game consoles. Each cartridge (state) contains everything needed to run itself, or it can ask the console (player_controller) to tell it a few things. When the cartridge is over, it tells the console which cartridge to insert next, but the console must actually eject and replace the cartridge.

This way the decisions on which state should be next, and under what conditions a state change should occur happen entirely within the current state.

In practice the system simply assisgns a new script to the current_state node when the old state determines it is done.

i.e. A Jump state could check if the player is grounded, if so it can ask to move to a landing state/ walking state/idle state depending on the desired behavior.

 #JumpState
func init(_args):
    name = "JumpState"
    .init(_args)

On entering the jump state, play jump animation

func on_state_enter(): 
    .on_state_enter()
    anim_player.play("Jump")

When the animation is done, move to a fall state, the states are already setup to recieve signals from the attached Animation Player Node.

func on_animation_finished(_anim_name: String):
     .on_animation_finished(_anim_name)
     if _anim_name = "Jump":
     request_state_exit(Globals.player_states.fall, null)

Download

Download NowName your own price

Click download now to get access to the following files:

AdvancedPlayerController.zip 164 kB
Advanced-Player-Controller-Demo.zip 157 kB

Development log