The template for the character controller in Godot is very basic. ``` extends CharacterBody2D const SPEED = 300.0 const JUMP_VELOCITY = -300.0 # Get the gravity from the project settings to be synced with RigidBody nodes. var gravity = ProjectSettings.get_setting("physics/2d/default_gravity") func _physics_process(delta): # Add the gravity. if not is_on_floor(): velocity.y += gravity * delta # Handle jump. if Input.is_action_just_pressed("ui_accept") and is_on_floor(): velocity.y = JUMP_VELOCITY # Get the input direction and handle the movement/deceleration. # As good practice, you should replace UI actions with custom gameplay actions. var direction = Input.get_axis("ui_left", "ui_right") if direction: velocity.x = direction * SPEED else: velocity.x = move_toward(velocity.x, 0, SPEED) move_and_slide() ``` The basic template uses two helper functions `is_on_floor` and `move_and_slide` along with the `velocity` variable to achieve jumping, falling, and movement. Let us use some of our newfound knowledge of physics to improve our player character! ### Exported Variables Exported variables allow us to make changes to them in the Godot editor. This is nice for play testing as it gives us a visual way to tweak our code. Let's take the imortant variables and export them. ``` @export var SPEED = 10400 @export var JUMP_VELOCITY = -300 ``` ### Collisions with RigidBodies Each time a PlayerCharacter body moves via `move_and_slide` any colliders it made contact with are stored in an array that can be retrieved via the `get_slide_collision` method. We can use that to apply an impulse to any RigidBody the player collided with. ``` # This represents the player's inertia # after calling move_and_slide() for i in get_slide_collision_count(): var c = get_slide_collision(i) if c.get_collider() is RigidBody2D: c.get_collider().apply_central_impulse(-c.get_normal() * PUSH_FORCE) ``` ## Creating a Melee Force Push Attack We can use our new knowledge of impulses to do some pretty cool attacks agains RigidBodies! To pull off the attack we need to know: * Is there a target in range? * What direction is the player facing? * Is there a target in front of them in range? * Are they attacking? * How much force should be applied? To tell if there is a target in range, we can use a handy feature called a Raycast. ### Raycasts Raycasts can be used to detect collisions within a very narrow line. They are very helpful for detecting objects within a certain proximity. We will use them to detect if there are any items we can attack with a force-push within range of the player character. First we make a reference to our two raycasts: ``` @onready var rightRay = $RightRaycast @onready var leftRay = $LeftRaycast var pushRightEnabled = false var pushLeftEnabled = false #note no type assigned var pushTarget #can not force-push right if facing left var faceLeft = false ``` When we detect the action of a raycast we need to respond. First of all, we need to track which direction the player is facing, assuming that they can only force-push in the direction they are facing. ``` if direction: velocity.x = direction * SPEED faceLeft = true if direction<0 else false ``` ``` if rightRay.is_colliding(): var collider = rightRay.get_collider() if collider is Node: if collider.is_in_group("boxes"): pushRightEnabled = true pushTarget = collider else: pushRightEnabled = false ``` And we can do the same for the left ray: ``` if leftRay.is_colliding(): var collider = leftRay.get_collider() if collider is Node: if collider.is_in_group("boxes"): pushLeftEnabled = true pushTarget = collider else: pushLeftEnabled = false ``` Now we just have to detect if the player is holding the force push button. ``` if Input.is_action_just_pressed("push") && pushRightEnabled && faceLeft == false: label.text="shove" pushTarget.apply_central_impulse(Vector2(1,0) * PUSH_FORCE * 20) pushRightEnabled = false if Input.is_action_just_pressed("push") && pushLeftEnabled : label.text="shove" pushTarget.apply_central_impulse(Vector2(-1,0) * PUSH_FORCE * 20) pushLeftEnabled = false ```