Godot Version
Godot 4
Question
I’m currently using an AnimationTree and I’m having some problems. I want to make it so that when you are holding down shift and a directional button and the player is at full speed, an animation plays and switches the state from bare hands to running in my statemachine. However, whenever these conditions are met (except for full speed) it will continuously play that animation and not switch to the running state. During the animations loop, when I fulfill the conditions again, then it will turn to the running state and not go back. I have no idea what is going on.
Here my nodes for the CharacterBody3D:
Here is what my AnimationTree looks like:
Here is what my CharacterBody3D code looks like:
extends CharacterBody3D
var speed
var Running
const WALK_SPEED = 10.0
const SPRINT_SPEED = 20.0
const JUMP_VELOCITY = 7.8
var accelerationx = 5.0
var dead = false
var full_speed = false
var animation_lock : bool = false
# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = 18.8
@onready var camera = $Camera3D
@onready var Hand = $"CanvasLayer/Bare Hands/Hand"
@onready var Reaction = $CanvasLayer/LiveReaction/Reaction
@onready var PlayerTree : AnimationTree = $"CanvasLayer/Bare Hands/AnimationTree"
@onready var StateMachine : PlayerStateMachine = $PlayerStateMachine
func _ready():
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
camera.current = true
PlayerTree.active = true
func _unhandled_input(event: InputEvent) -> void:
if dead:
return
if event is InputEventMouseButton:
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
elif event.is_action_pressed("ui_cancel"):
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
if Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
if event is InputEventMouseMotion:
rotate_y(-event.relative.x * 0.005)
camera.rotate_x(-event.relative.y * 0.005)
camera.rotation.x = clamp(camera.rotation.x, -PI/4, PI/3)
func _physics_process(delta):
var input_dir = Input.get_vector("left", "right", "forward", "backward")
var direction = (camera.transform.basis * transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
if not is_on_floor():
velocity.y -= gravity * delta
# Handle Jump.
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = JUMP_VELOCITY
# Handle Sprint.
if Input.is_action_pressed("sprint") and Input.get_vector("left", "right", "forward", "backward"):
speed = move_toward(speed, SPRINT_SPEED, accelerationx * delta)
if speed == SPRINT_SPEED:
full_speed = true
else:
speed = WALK_SPEED
if is_on_floor():
if direction:
velocity.x = direction.x * speed
velocity.z = direction.z * speed
else:
velocity.x = lerp(velocity.x, direction.x * speed, delta * 7.0)
velocity.z = lerp(velocity.z, direction.z * speed, delta * 7.0)
else:
velocity.x = lerp(velocity.x, direction.x * speed, delta * 3.0)
velocity.z = lerp(velocity.z, direction.z * speed, delta * 3.0)
move_and_slide()
punch()
run()
func punch():
if Input.is_action_pressed("Attack") and StateMachine.check_if_can_punch():
pass
func run():
if full_speed == true and StateMachine.check_if_can_run():
pass
Here is what my StateMachine looks like:
extends Node
class_name PlayerStateMachine
@export var Player : CharacterBody3D
@export var animation_tree : AnimationTree
@export var CurrentState : State
var states : Array[State]
func _ready():
for child in get_children():
if(child is State):
states.append(child)
#Set the states up with what they need to function
child.Player = Player
child.playback = animation_tree["parameters/playback"]
else:
push_warning("Child " + child.name + " is not a State for PlayerStateMachine")
func _physics_process(delta):
if(CurrentState.next_state != null):
switch_states(CurrentState.next_state)
CurrentState.state_process(delta)
func check_if_can_run():
return CurrentState.can_run
func check_if_can_punch():
return CurrentState.can_punch
func switch_states(new_state : State):
if(CurrentState != null):
CurrentState.on_exit()
CurrentState.next_state = null
CurrentState = new_state
CurrentState.on_enter()
func _input(event : InputEvent):
CurrentState.state_input(event)
Here is what the BareHands state looks like:
extends State
class_name Bare_HandsState
@export var punch_state : State
@export var punch_animation : String = "Punch"
@export var running_state : State
@export var running_animation : String = "Running"
func state_input(event : InputEvent):
if(event.is_action_pressed("Attack")):
punch()
if(event.is_action_pressed("sprint") and Input.get_vector("left", "right", "forward", "backward")):
run()
func punch():
next_state = punch_state
playback.travel(punch_animation)
func run():
next_state = running_state
playback.travel(running_animation)
Here is what my Running state looks like:
extends State
class_name RunningState
@export var bare_hands_state : State
func state_process(_delta):
Player.run()
func _on_animation_tree_animation_finished(String):
next_state = bare_hands_state
Here is what the State code looks like:
extends Node
class_name State
@export var can_run : bool = true
@export var can_punch : bool = true
var Player : CharacterBody3D
var next_state : State
var playback : AnimationNodeStateMachinePlayback
func state_process(_delta):
pass
func state_input(_event : InputEvent):
pass
func on_enter():
pass
func on_exit():
pass