为什么我的代码没有使用正确的动画?

Why doesn't my code use the correct animation?

我有一个用 Python 编写的功能正常的格斗游戏,有移动、帧数据、攻击(没有用)但没有重力。我正在尝试用 Godot 重写它,但我真的迷路了。

我有一个在我的攻击中实施帧数据的系统,所以我实际上可以轻松地从空闲状态转换到攻击状态,反之亦然。现在,我的问题是我想攻击并播放那个动画,但它一直在播放空闲动画。在我开始做碰撞盒和碰撞之前,我想让这件事起作用。这是我的代码:

extends KinematicBody2D


export var walking_speed = 100
export var falling_speed = 0
var time = 0
onready var anim = $Sprite
var state = "Idle"

func _ready():
    set_physics_process(true)
    set_process(true)
    $AnimationTree.active = true

func gravity():
    pass

func _physics_process(delta):
    time += delta
    print(time)
    # If you press both A and D, you will stop entirely
    if Input.is_action_pressed("ui_left") and Input.is_action_pressed("ui_right"):
        walking_speed == 0
        $AnimationTree.set("parameters/Neutral/current", 0)
    #if you press A or D, you will move left or right accordingly
    elif Input.is_action_pressed("ui_left"):
        move_and_collide(Vector2(-walking_speed * delta, 0))
        anim.flip_h = true
        $AnimationTree.set("parameters/Neutral/current", 1)
    elif Input.is_action_pressed("ui_right"):
        move_and_collide(Vector2(walking_speed * delta, 0))
        anim.flip_h = false
        $AnimationTree.set("parameters/Neutral/current", 1)
    else:
        walking_speed = walking_speed

    if Input.is_action_just_pressed("Horizontal"):
        $AnimationTree.set("parameters/Neutral/current", 2)

这段代码基本上只是一堆我被告知要做的事情的教程的混杂(例如,我被告知不要使用 AnimatedSprite 而是使用 AnimationTree,但是它似乎代码要复杂得多,并且给我带来了与上次相同的问题)。如果你需要我,我也可以 post 我的 Python 代码,但它是一个巨大的混乱,根本没有评论。

您可以使用 AnimatedSprite 制作动画。你有我的祝福。 AnimatedSprite有没有用。而且“有人告诉我”并不是不这样做的好理由。

此外,AnimatedSpriteAnimationPlayerAnimationTree 不是非此即彼。是的,有些东西您需要 AnimationPlayerAnimationTree,但是您始终可以让 AnimationPlayer 触发 AnimatedSprite 动画。事实上,AnimationPlayer 可以调用任何方法(而 AnimationTree 将控制 AnimationPlayer)。因此,用 AnimatedSprite 完成的任何工作都不会浪费。


调用 set_physics_process 将启用或禁用 [​​=31=]。同样,set_process 启用或禁用 [​​=33=]。但是,如果您有该方法,那么默认情况下它是启用的。因此,只有在禁用它们时才需要启用它们。


我们可以压缩很多你的_physics_process代码:

func _physics_process(_delta:float) -> void:
    var walking_speed := (
        Input.get_action_strength("ui_right") -
        Input.get_action_strength("ui_left")
    )
    anim.flip_h = walking_speed < 0
    var velocity := Vector2(walking_speed, 0)
    move_and_collide(velocity * delta)

walking_speed 为负数时,行 anim.flip_h = walking_speed < 0 会将 flip_h 设置为 true,否则设置为 false

我们正在根据 get_action_strength 设置 walking_speed 将 return 一个介于 01 之间的数字,表示按下动作的力度。这也意味着如果它们被映射到操纵杆或类似物,这将为您提供灵敏度支持。

如果您不想那么敏感,sign 可以:

    var walking_speed := sign(
        Input.get_action_strength("ui_right") -
        Input.get_action_strength("ui_left")
    )

使用 KinematicBody2D 你将不得不处理重力。基本上给它一些向下的速度。

var gravitational_acceleration := 100 # pixels per second squared
var falling_speed := 0

func _physics_process(_delta:float) -> void:
    falling_speed += gravitational_acceleration * delta
    var walking_speed := (
        Input.get_action_strength("ui_right") -
        Input.get_action_strength("ui_left")
    )
    anim.flip_h = walking_speed < 0
    var velocity := Vector2(walking_speed, falling_speed)
    move_and_collide(velocity * delta)

不过,最好用move_and_slide实现重力:

var gravitational_acceleration := 100 # pixels per second squared
var falling_speed := 0

func _physics_process(_delta:float) -> void:
    falling_speed += gravitational_acceleration * delta
    var walking_speed := (
        Input.get_action_strength("ui_right") -
        Input.get_action_strength("ui_left")
    )
    anim.flip_h = walking_speed < 0
    var velocity := Vector2(walking_speed, falling_speed)
    velocity = move_and_slide(velocity)
    falling_speed = velocity.y

我在末尾删除了与 delta 的乘法,因为 move_and_slide 在内部执行此操作。也就是说,它需要速度而不是位移。

这样 falling_speed 就不会失去控制。

你也可以加上一个向上向量(这样Godot就知道什么是天花板,什么是地板,什么是墙),然后你可以检查is_on_ceilingis_on_flooris_on_wall.

另外,你可以告诉move_and_slidefloor_max_angle0,所以它不会滑动(什么都不算斜坡,只有完全水平的地板是地板),如果这是你想要的。


当然还有动画。

如果您要使用 AnimationTree,我建议将根设置为 AnimationNodeStateMachine。然后,从代码中,你可以得到状态机播放对象,像这样:

var state_machine = $AnimationTree.get("parameters/playback")

您可以告诉它您希望它进入不同的状态,如下所示:

state_machine.travel("name_of_some_state")

你可以问它当前状态是什么:

var state:String = state_machine.get_current_node()

例如,你可以在你的状态机中有一个“空闲”状态,在你的状态机中有一个“攻击”状态,从“空闲”到“攻击”的转换,以及从“攻击”的另一个转换”到“空闲”,但那个设置为“最后”过渡。 你当然可以有其他状态。然后使用当前状态(来自get_current_node)来计算玩家可以做什么。

AnimationPlayer你可以检查什么是current_animationis_playing;您还可以连接 "animation_finished" 信号。使用 AnimatedSrite 你可以检查当前的 animation 是什么,是否是 playing;您还可以连接 "animation_finished" 信号。