0 votes

i can apply animationplayer for 4 direction just fine but when come to 8 direction i have no idea, can someone help me, this my movement script for 8 direction.

var RIGHT = Input.is_action_pressed("ui_right")
var LEFT = Input.is_action_pressed("ui_left")
var DOWN = Input.is_action_pressed("ui_down")
var UP = Input.is_action_pressed("ui_up")
############Walking##################
move_direction.x = int(RIGHT) -int(LEFT)
move_direction.y = int(DOWN)  -int(UP)
in Engine by (393 points)

3 Answers

+1 vote
Best answer

You could store the animation paths to an array, then use the angle of move_direction to get the index to have your animation player play. And I added a facing value to store which direction you were last facing, otherwise it always turns right when idle.

var directions = ["right", "down_right", "down", "down_left", "left", "up_left", "up", "up_right"]
var facing = Vector2()

func _physics_process(delta):
    var move_direction = Vector2()

    var LEFT = Input.is_action_pressed("ui_left")
    var RIGHT = Input.is_action_pressed("ui_right")
    var UP = Input.is_action_pressed("ui_up")
    var DOWN = Input.is_action_pressed("ui_down")

    move_direction.x = int(RIGHT) - int(LEFT)
    move_direction.y = int(DOWN) - int(UP)

    if LEFT || RIGHT || UP || DOWN:
        facing = move_direction

    var animation = direction2str(facing)

    if $AnimationPlayer.assigned_animation != animation:
        $AnimationPlayer.play(animation)

func direction2str(direction):
    var angle = direction.angle()
    if angle < 0:
        angle += 2 * PI
    var index = round(angle / PI * 4)
    return directions[index]
by (64 points)
selected by

thanks you for your teaching, i try out your code and it work, and it look simply elegant but i have no idea how PI work and make wonder how to apply idle/facing. because my old code switch between str("idle") and str("move") as you can see. my animation mostly loop.

for some reason when i paste my code here not all in gray

extends KinematicBody2D

const move_speed = 100
var move_direction = dir.center
var sprite_direction = "_Down"



func _physics_process(delta):
    control_loop()
    move_loop()
    sprite_loop()
    if move_direction != dir.center:
        animation_play("Fmove")
    else:
        animation_play("Fidle")


    pass

func control_loop():#control key
    var RIGHT = Input.is_action_pressed("ui_right")
    var LEFT = Input.is_action_pressed("ui_left")
    var DOWN = Input.is_action_pressed("ui_down")
    var UP = Input.is_action_pressed("ui_up")
    ############Walking##################
    move_direction.x = int(RIGHT) -int(LEFT)
    move_direction.y = int(DOWN)  -int(UP)


func move_loop():
    var motion = move_direction.normalized() * move_speed
    move_and_slide(motion, dir.center)


func sprite_loop():
    match move_direction:
        dir.right:
            sprite_direction ="_Right"
        dir.left:
            sprite_direction = "_Left"
        dir.up:
            sprite_direction = "_Up"
        dir.down:
            sprite_direction = "_Down"


func animation_play(animation):# to atcive animation_switch
    var New_anim = str(animation,sprite_direction)
    if $anim.current_animation != New_anim:
        $anim.play(New_anim)

Howdy. Sorry just now seen you replied. I probably should've commented my code explaining it. Basically what the direction2str method does is get the angle you're moving. It's in radians, which is based on PI. So instead of -180 to 180 (degrees), it's -PI to PI (3.14 etc). 0 is right, negative is counter-clockwise to right and positive is clockwise to right.

To make things simple we want to make angle positive, so if it's less than 0, we add 2 * PI to it. So now angle is a value between 0 and 2 * PI. We can then divide it by PI * 4 and round it to get a value between 0 and 7, which correlates to the direction you're facing. 0 being right going clockwise. You then use that value to determine which string in the array you need to use.

As for "idle" and "move", I would have them named like "idle_right" and "move_right" etc. So that you can determine if you're idling or moving, then do something along the lines of:

var suffix = "idle_"
var direction = direction2str(facing)
var animation = suffix + direction # idle_right

I know I'm quite late to the party, but in my code I found some bugs where (angle / 4PI) was getting me 8, which is out of the bounds of the array. To solve this I returned

directions[int(index) % 8]

instead of just

directions[index]

in the direction2str function. This prevents any out-of-bounds by just making the index a ring of sorts.

+1 vote

A faster but maybe not the best way to do this would be:

# Animation control handlers
if Input.is_action_pressed("ui_left") and !Input.is_action_pressed("ui_down") and !Input.is_action_pressed("ui_up"):
    _play_animation('w_l')

elif Input.is_action_pressed("ui_right") and !Input.is_action_pressed("ui_down") and !Input.is_action_pressed("ui_up"):
    _play_animation('w_r')


if Input.is_action_pressed("ui_up"):
    if Input.is_action_pressed("ui_left"):
        _play_animation('w_ul')

    elif Input.is_action_pressed("ui_right"):
        _play_animation('w_ur')

    else:
        _play_animation('w_u')


if Input.is_action_pressed("ui_down"):
    if Input.is_action_pressed("ui_left"):
        _play_animation('w_dl')

    elif Input.is_action_pressed("ui_right"):
        _play_animation('w_dr')

    else:
        _play_animation('w_d')

in this case playanimation(name) is a function to change the current animation, but you don't need to bother with this and you can change the Input.isactionpressed(direction) for your respective direction variable.

by (71 points)
edited by

Thanks for the answer, Lucas. I've been looking for a way to move my character in four or more directions while also changing the animation. This may prove useful.

Notice that if you change the animation directly on _physics_process(delta) or even _process(delta) you need to prevent it from change if it's already being played to avoid your animations of being stuck at the first frame. In my case, I have this _play_animation(name) like that:

_play_animation(animation_name):
    if old_animation != animation_name:
       $AnimationPlayer.play(animation_name)
       old_animation = animation_name

Yeah, I've read some of GDQuest's code, and Nathan's put that if old_animation != animation_name: ... logic in his code.

thank you you code easy to understand for beginer like me.

0 votes

I have this difficulty, but in my project the movement is controlled by the mouse.
the code looks like this:

extends KinematicBody2D

export (int) var speed = 150

var target = Vector2()
var velocity = Vector2()
var ini_target = position

func ready():
target = ini
target
pass

func physicsprocess(_delta):

if Input.is_action_pressed("click"):
    target = get_global_mouse_position()

velocity = (target-position).normalized()*speed

if (target - position).length() > 5:
    velocity = move_and_slide(velocity)
    $sprite_move.play("mov_down")

else:
    $sprite_move.stop()

the code works well, but i have no idea how can i get the direction to the target to make a choose of wat animation the node plays.

by (18 points)

This is not an answer to the original (two years old!) question. If you want to ask a related question, click the three dots in the bottom right corner and select "Ask Related Question". That is in your interest, as it will appear as a new, unanswered question, thus people are a lot more likely to see it and help you.

i have no idea how can i get the direction to the target

You're already using it: (target-position).normalized()

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 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 webmaster@godotengine.org with your username.