The Player Controller with RigidBody2D is a demonstration of how you can implement your own player in a platformer-style game.

You can download the project files here.


You need to be using Godot Engine 2.2 (or newer) in order to use a few functions we are going to use. This written tutorial is a short explanation of how you implement a player controller using RigidBody2D.

For a more detailed look at the player controller with RigidBody2D, please watch the video tutorial at the top.

Resources

You will need to download these files in order to follow the tutorial. It contains the project from the moment we begin our tutorial. Load up Godot Engine and load the project by hitting the Scan button and navigate to the 0006 - Player Controller with RigidBody2D folder. Then double click the project, or select it once and press Edit

Creating Main Scene

Load the project and begin by creating a Control node. Name the node "main", and save the scene (CTRL+S). Name it main.tscn to indicate that this will be the scene we are going to run first every time we hit Play. Hit the Play button on the top, and select main.tscn as the main scene when it asks you to select one.

Create a TileMap and name it "world". Select it, and load the exported TileSet that is found inside at res://tilesets/grassland/export/grassland.tres.
Begin drawing your world. Feel free to be creative, just make sure you have ground to stand on (the only tile without a collision box, is the underground_deep tile).

Creating the Player Scene

Create a new Scene, by selecting Scene -> New Scene.

In your new scene, start by adding a RigidBody2D node. This will be the root for the player. Name the node "player", and save the scene as player.tscn inside the res://player/ folder. This will help keep your scenes organized.

It is good practice to keep all files related to each-other in one place. Don't try to organize it by having one script and scene folder. A player scene would be in a /player/ folder. The scripts would also go in the same folder, such as /player/scripts/. Audio clips would be put inside player/audio/ and so on. On bigger projects, this will make life easier, as you wont have to look for scripts and resources that the player is using.

Select the RigidBody2D node and set Mode to "Character". This will prevent the player from rolling around when moving or affected by outside forces.

(If you want the player to act like a potato when you move it, just leave it on Rigid.)

Lastly, change Gravity Scale to 10. Feel free to play around with this at the end of the tutorial.


Create a Sprite, CollisionShape2D and a Camera2D as children of the player node. Name them accordingly.

Select the Sprite node, and load the texture from /player/character.png.

Select the CollisionShape2D and add shape "CapsuleShape2D".

Adjust the position of the collision, and resize it so that it fits the character.

Select your Camera2D. Make sure to set it as Current camera, and zoom in by changing the zoom level on X and Y to 0.5.

Last we will create our ground check. This is done by adding a Area2D on our player. Name it "ground_check". Select it, and add a CollisionShape2D named "ground_collision".

Load a Circle shape on the CollisionShape2D. Resize it and move it so that it interacts with the ground.

Create the signals for the Area2D. Double click body_enter and body_exit in order to create the functions inside our player script.
These will be used to toggle grounded.

Make sure to keep the default method names on both of them.

Implementing player controller script

This is the part we add our player script. We are going to be creating two scripts. One will be put directly on the player. The other script is the character script the player will inherit from. Using this method, you can easily create other NPCs/Enemies that can move simply by inheriting it, and creating the algorithm for the movement.

Let us start by creating our player.gd script.

Right click the player node, and select Add Script.


Name it "player.gd" and save.

Insert the following code.

extends "res://character.gd"

# Grounded?
var grounded = false 

# Jumping
var can_jump = false
var jump_time = 0
const TOP_JUMP_TIME = 0.1 # in seconds

# Start
func _ready():
	# Set player properties
	acceleration = 1000
	top_move_speed = 200
	top_jump_speed = 400

# Apply force
func apply_force(state):
	# Move Left
	if(Input.is_action_pressed("ui_left")):
		directional_force += DIRECTION.LEFT
	
	# Move Right
	if(Input.is_action_pressed("ui_right")):
		directional_force += DIRECTION.RIGHT
	
	# Jump
	if(Input.is_action_pressed("ui_select") && jump_time < TOP_JUMP_TIME && can_jump):
		directional_force += DIRECTION.UP
		jump_time += state.get_step()
	elif(Input.is_action_just_released("ui_select")):
		can_jump = false # Prevents the player from jumping more than once while in air
	
	# While on the ground
	if(grounded):
		can_jump = true
		jump_time = 0


# Enter Ground
func _on_ground_check_body_enter( body ):
	# Get groups
	var groups = body.get_groups()
	
	# If we are on a solid ground
	if(groups.has("solid")):
		grounded = true


# Exit Ground
func _on_ground_check_body_exit( body ):
	# Get groups
	var groups = body.get_groups()
	
	# If we are on a solid ground
	if(groups.has("solid")):
		grounded = false

Save the player.gd script, and create a new script named "character.gd". Save it in res://character.gd.

In our character.gd, we are going to be using func _integrate_forces(state). This is similar to _process(delta), except that it is meant for doing heavier physics using the state argument that it gives.

Add the following code inside character.gd:

extends RigidBody2D

# Default Character Properties (Should be overwritten)
var acceleration = 10000
var top_move_speed = 200
var top_jump_speed = 400

# Movement Vars
var directional_force = Vector2()
const DIRECTION = {
	ZERO = Vector2(0, 0),
	LEFT = Vector2(-1, 0),
	RIGHT = Vector2(1, 0),
	UP = Vector2(0, -1),
	DOWN = Vector2(0, 1)
}

func _integrate_forces(state):
	# Final force
	var final_force = Vector2()
	
	# We are not moving when you are not changing the direction
	directional_force = DIRECTION.ZERO
	
	# Apply force on character
	apply_force(state)
	
	# Get movement velocity
	final_force = state.get_linear_velocity() + (directional_force * acceleration)
	
	# Prevent ourselves from exceeding top speeds
	if(final_force.x > top_move_speed):
		final_force.x = top_move_speed
	elif(final_force.x < -top_move_speed):
		final_force.x = -top_move_speed
	# Prevent jumping too fast / falling too fast
	if(final_force.y > top_jump_speed):
		final_force.y = top_jump_speed
	elif(final_force.y < -top_jump_speed):
		final_force.y = -top_jump_speed
	
	# Add force
	state.set_linear_velocity(final_force)

# This func is overwritten by the character
func apply_force(state):
	pass

The player.gd uses apply_force(state) that is run in character.gd. This is done so that you can set your own logic that determines if and when the character should move.
A player would check for input, while a NPC or Enemy would have an algorithm that determines whether or not he should move. This way, all you have to do is set directional_force from within your player/npc and the character.gd script will do the work for you.

Save the player scene again (CTRL+S) and instance it in our main.tscn scene by hitting the chain button, and select player.tscn.

Hit the play button and we are done! Feel free to experiment and change settings to your liking.