This site is currently in read-only mode during migration to a new platform.
You cannot post questions, answers or comments, as they would be lost during the migration otherwise.
0 votes
func _physics_process(delta):
handle_movement(delta)

func handle_movement(delta):
var direction = Vector3()

if Input.is_action_pressed("forward"):
    direction += transform.basis.z
    is_moving = true

elif Input.is_action_pressed("backward"):
    direction -= transform.basis.z
    is_moving = true

elif Input.is_action_pressed("left"):
    direction += transform.basis.x
    is_moving = true

elif Input.is_action_pressed("right"):
    direction -= transform.basis.x
    is_moving = true

else:
    is_moving = false

if is_moving == true:
    animation_mode.travel("WALK")
else:
    animation_mode.travel("IDLE")

I have this code here and all the animations are looped in the Animation Player. But for some reason, when I test it out in-game, it doesn't loop as intended.

Am I missing something? If so, what is it? If I am doing something wrong, can someone help me fix it? I'm pretty new to the whole state machine thing. I mainly used animations instead of the Blendspace 1Ds because I didn't understand how to work those.

Godot version 3.4.3
in Engine by (69 points)

1 Answer

0 votes
Best answer

I havent worked with AnimationPlayer for a while, so I dont remember what exactly travel() does , I think it esentially starts to plays the desired animation? If I am right, then your problem is that you are triggering travel() every frame, since it is inside a procces() function (I think the last 3/4 questions I answered, were of people not realizing of procces() triggering stuff, haha). I may be wrong though if travel() works differently.

Without knowing the rest of your code and how you setted up your state machine, I dont have a "best" way to solve your issue, but what you have to do is make sure to not trigger travel() every frame. I personally use setters in my state machines, to trigger stuff only when entering or exiting states.

by (450 points)
selected by

Not related to your question, but maybe it will help. I think it is a good aproach to manage inputs in _unhandled_input() and just let the input determine a normalized direction Vector3() and maybe a "strength" var (if you care to get the strength of the inputs). Then in procces() you just move using this direction

To prevent the animation from being played each frame, instead of going into idle animation if the user didn't press a direction, you should have is_moving = false when the player releases forward or back. That way, pressing forward/back moves, the character only enter the moving animation when you initially pressed the button (not when you hold it down), just like you have, and when they release the button the player goes idle.

Your current logic right now is every frame a button hasn't just been pressed, go idle. So your character will immediately stop after 1 tick

func _physics_process(delta):
handle_movement(delta)

func handle_movement(delta):
var direction = Vector3()

if Input.is_action_pressed("forward"):
    direction += transform.basis.z

elif Input.is_action_pressed("backward"):
    direction -= transform.basis.z

if Input.is_action_just_pressed("forward"):
    is_moving = true

elif Input.is_action_just_pressed("backward"):
    is_moving = true

if Input.is_action_pressed("left"):
    null #You will dash to the left/right and I haven't gotten to that so I put null for now

elif Input.is_action_pressed("right"):
    null

if Input.is_action_just_released("forward") or Input.is_action_just_released("backward"):
    is_moving = false

if is_moving == true:
    animation_mode.travel("WALK")
else:
    animation_mode.travel("IDLE")

direction = direction.normalized()

this is what I got. The animations now play when they are supposed to, but it still doesn't loop properly. @Pomelo when you said that don't trigger travel every frame, I assumed @godotdev provided a fix to that. If I weren't to do it in physics process, where would I put my animations?

I dont agree with what @godotdev wrote. as I said in my awnser, everything that you write in procces() is "read" by the engine in every frame. I recomend you getting used to read the Documentation! For example I recomended you to read unhandled_input() and also understand setters (both come in handy so you dont end up using procces() for everything).

now back to your problem, what you want to solve is not a matter of changing the state of your state machine, but when to trigger the animations. You could make a state machine that handles this, but since it seems you are quite new to coding I will recomend an easier but less powerfull aproach: (I wrote only what is relevant to the animation)

var is_walk_playing := false # write this outside of procees

if Input.is_action_pressed("forward") or Input.is_action_pressed("backward"): 
    if not is_walk_playing:
        animation_mode.travel("WALK")
        is_walk_playing = true

This way when you press forward, walk starts to play, but it wont trigger in the next frame even if you keep pressing, or even if the state is is_moving. then when you to stop walking, make sure to do is_walk_playing = false, so you can trigger the animation the next time.

I tried to implement the code you provided here (I put here the whole script. The part you provided is at the bottom):

extends KinematicBody

export var speed : float = 20
export var acceleration : float = 50
export var air_acceleration : float = 5
export var gravity : float = 0.98
export var max_terminal_velocity : float = 54
export var jump_power : float = 20

export(float, 0.1, 1) var mouse_sensitivity : float = 0.3
export(float, -90, 0) var min_pitch : float = -90
export(float, 0, 90) var max_pitch : float = 90

var velocity : Vector3
var y_velocity : float

onready var camera_pivot = $camerapivot
onready var camera = $camerapivot/cameraboom/Camera
onready var animation_tree = $AnimationTree
onready var animation_mode = animation_tree.get("parameters/playback")

func ready():
Input.set
mousemode(Input.MOUSEMODECAPTURED)
animation
tree.active = true

func process(delta):
if Input.is
actionjustpressed("uicancel"):
Input.set
mousemode(Input.MOUSEMODE_VISIBLE)

func input(event):
if event is InputEventMouseMotion:
rotation
degrees.y -= event.relative.x * mousesensitivity
camera
pivot.rotationdegrees.x -= event.relative.y * mousesensitivity
camerapivot.rotationdegrees.x = clamp(camerapivot.rotationdegrees.x, minpitch, maxpitch)

func physicsprocess(delta):
handle_movement(delta)

func handle_movement(delta):
var direction = Vector3()

if Input.is_action_pressed("forward"):
    direction += transform.basis.z

elif Input.is_action_pressed("backward"):
    direction -= transform.basis.z

if Input.is_action_pressed("left"):
    null

elif Input.is_action_pressed("right"):
    null

direction = direction.normalized()

var accel = acceleration if is_on_floor() else air_acceleration
velocity = velocity.linear_interpolate(direction * speed, accel * delta)

if is_on_floor():
    y_velocity = -0.01
else:
    y_velocity = clamp(y_velocity - gravity, -max_terminal_velocity, max_terminal_velocity)

if Input.is_action_just_pressed("jump") and is_on_floor():
    y_velocity = jump_power
    animation_mode.travel("AIR")

velocity.y = y_velocity
velocity = move_and_slide(velocity, Vector3.UP)

func _unhandled_input(event):
var is_walk_playing := false # write this outside of procees

if Input.is_action_pressed("forward") or Input.is_action_pressed("backward"): 
    if not is_walk_playing:
        animation_mode.travel("WALK")
        is_walk_playing = true

if Input.is_action_just_released("forward") or Input.is_action_just_released("backward"):
    if is_walk_playing:
        animation_mode.travel("IDLE")
        is_walk_playing = false

Honestly, I'm very clueless on this topic. I've only worked with the state machine once and I only really understand how to use it for 2D Top-Down games (e.g. Zelda-style games) through Heartbeast's RPG tutorial.

It also might be a problem with my State Machine. Say, do you know how to use Blendspace 1Ds and animations in the state machine node? I'm pretty sure the Blendspace 1Ds can help with the transition from walk to sprint but when I tried it, it didn't work.

This is how my state machine is currently put together:
https://drive.google.com/file/d/1yV-Kb2KWEMPcqOLcR_NAYt9bpIxe2KSb/view?usp=sharing

After watching https://www.youtube.com/watch?v=WY2cN9uG6W8 I'm currently making a transition from the State Machine to the Blend Tree. It seems very straightforward. Hopefully it works out.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read Frequently asked questions and How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to [email protected] with your username.