You want to create a character & move it with code. For 2D platformers.
This recipe aims to solve this problem.
Making a character
In Godot, create a scene with CharacterBody2D as its root. Add a Sprite2D (or AnimatedSprite2D) nodes as its child. Also add collision shape. Overall setup should look like this:
Motion mechanics
Add a script to the character and add code to move left, right, jump, or stay idle like this (I used AnimatedSprite2D for these animations) if you use Sprite2D, just remove lines dealing with AnimatedSprite2D:
extends CharacterBody2D
@export var speed = 232
@export var jump_speed = -800
@export var gravity = 300 * 9.8
@export_range(0.0, 1.0) var friction = 0.32
@export_range(0.0 , 1.0) var acceleration = 0.32
var is_jumping :bool= false
func _physics_process(delta):
velocity.y += gravity * delta
var dir = Input.get_axis("ui_left", "ui_right")
if dir != 0:
if is_on_floor():
velocity.x = lerp(velocity.x, dir * speed, acceleration)
else:
velocity.x = lerp(velocity.x, dir * speed * 1.4, acceleration)
if dir > 0:
# flip the sprite
$AnimatedSprite2D.flip_h = false
else:
$AnimatedSprite2D.flip_h = true
$AnimatedSprite2D.play("run")
else:
velocity.x = lerp(velocity.x, 0.0, friction)
$AnimatedSprite2D.play("idle")
if Input.is_action_just_pressed("ui_up") and is_on_floor():
velocity.y = jump_speed
is_jumping = true
if is_jumping:
if is_on_floor():
is_jumping = false
$AnimatedSprite2D.play("fall")
move_and_slide()
Adding attack mechanics
Attack adds a bit of complexity. While attacking, we complete the animation even if another animation such as jump or run is triggered. Otherwise, attack animation will be cut abruptly. For this we add some more checks.
Code:
extends CharacterBody2D
@export var speed = 232
@export var jump_speed = -800
@export var gravity = 300 * 9.8
@export_range(0.0, 1.0) var friction = 0.32
@export_range(0.0 , 1.0) var acceleration = 0.32
var is_jumping: bool= false
var is_attacking: bool = false
func _physics_process(delta):
velocity.y += gravity * delta
var dir = Input.get_axis("ui_left", "ui_right")
if dir != 0:
if is_on_floor():
velocity.x = lerp(velocity.x, dir * speed, acceleration)
else:
velocity.x = lerp(velocity.x, dir * speed * 1.4, acceleration)
if dir > 0:
# flip the sprite
$AnimatedSprite2D.flip_h = false
else:
$AnimatedSprite2D.flip_h = true
if not is_attacking: $AnimatedSprite2D.play("run")
else:
velocity.x = lerp(velocity.x, 0.0, friction)
if not is_attacking: $AnimatedSprite2D.play("idle")
if Input.is_action_just_pressed("ui_up") and is_on_floor():
velocity.y = jump_speed
is_jumping = true
if is_jumping:
if is_on_floor():
is_jumping = false
if not is_attacking: $AnimatedSprite2D.play("fall")
if Input.is_action_just_pressed("ui_home"):
is_attacking = true
if is_attacking:
$AnimatedSprite2D.play("attack")
move_and_slide()
func _on_animated_sprite_2d_animation_finished():
if is_attacking:
is_attacking = false
print("ff")
func _on_animated_sprite_2d_animation_looped():
if is_attacking:
is_attacking = false
print("ff")
Thank you for reading <3