First time 2D game, I need help

Godot Version

Godot Engine v4.3.stable.official.77dcf97d8

Question

This is my first time trying to make a game, and I have no prior experience with any script, I’m trying to make a 2D platformer (Mario/Metroid/Megaman (other “M” names) style).

I can only get my idle and walk animations to work, and I don’t know how to fix it. I’m trying to add a “roll” and a faster “run” animation, plus I want to add a timer to the idle animation if no inputs have been made. Sorry about the spaghetti. Can someone help?

extends CharacterBody2D

@export var walk_speed = 75.0
@export var run_speed = 150.0
@export_range(0, 1) var acceleration = 0.1
@export_range(0, 1) var deceleration = 0.1

@export var jump_force = -300.0
@export_range(0, 1) var decelerate_on_jump_release = 0.5

@export var roll_speed = 1000.0
@export var roll_max_distance = 60.0
@export var roll_curve : Curve
@export var roll_cooldown = 1.0

@onready var animated_sprite_2d: AnimatedSprite2D = $AnimatedSprite2D

var is_rolling = false
var roll_start_position = 0
var roll_direction = 0
var roll_timer = 0

func _physics_process(delta: float) → void:
# Add the gravity.
if not is_on_floor():
velocity += get_gravity() * delta
animated_sprite_2d.play(“p_jump”)

# Handle jump.
if Input.is_action_just_pressed("jump") and is_on_floor():
	velocity.y = jump_force

if Input.is_action_just_released("jump") and velocity.y < 0:
	velocity.y *= decelerate_on_jump_release

# Run
var speed
if Input.is_action_pressed("run"):
	speed = run_speed
	animated_sprite_2d.play("p_run_fast")
else:
	speed = walk_speed

# Get the input direction: -1, 0, 1
var direction := Input.get_axis("move_left", "move_right")
if direction:
	velocity.x = direction * speed
	animated_sprite_2d.play("p_run_walk")
	animated_sprite_2d.flip_h = direction < 0
else:
	velocity.x = move_toward(velocity.x, 0, speed)
	animated_sprite_2d.play("p_idle")

# Roll activation
if Input.is_action_just_pressed("roll") and direction and not is_rolling and roll_timer <= 0:
	is_rolling = true
	roll_start_position = position.x
	roll_direction = direction
	roll_timer = roll_cooldown
	animated_sprite_2d.play("p_roll")
	
# Preform Roll
if is_rolling:
	var current_distance = abs(position.x - roll_start_position)
	if current_distance >= roll_max_distance or is_on_wall():
		is_rolling = false
	else:
		velocity.x = roll_direction * roll_speed * roll_curve.sample(current_distance / roll_max_distance)
		velocity.y = 0

# Roll Timer
if roll_timer > 0:
	roll_timer -= delta

# Apply movement
if direction:
	velocity.x = move_toward(velocity.x, direction * speed, speed * acceleration) 
else:
	velocity.x = move_toward(velocity.x, 0, walk_speed * deceleration)

_physics_process is called several times per second by the engine. What’s happening is that your script sets the animation to p_roll just fine. Then 0.0167 seconds later, the function is called a second time and encounters this bit of code:

var direction := Input.get_axis("move_left", "move_right")
if direction:
	velocity.x = direction * speed
	animated_sprite_2d.play("p_run_walk")
	animated_sprite_2d.flip_h = direction < 0
else:
	velocity.x = move_toward(velocity.x, 0, speed)
	animated_sprite_2d.play("p_idle")

if direction: is a null check. direction is not null, so the animation is set to p_run_walk. The naked eye can’t even see the roll animation-- it lasted for all of one frame.

As you’ve said yourself, the code is starting to become a bit unwieldy. It’s a good thing you spotted this, as now would be a good time to refactor this code to follow the state machine pattern. If you search for something like ‘character animation state machine in Godot’, you’re almost certain to find something that puts you on the right track.

2 Likes

Thanks!