2

我在我的简单平台游戏中有一个脚本,它说如果我的玩家在地面上并且按下“Z”,他在 Y 轴上的移动将上升到 600,如果他不在地面上,他会去执行跳跃动画。

所以事情是这样的,我知道它只播放跳跃动画的第一帧,因为代码不断检测玩家在空中。我想要一种方法来告诉代码只触发一次动画。

我尝试使用一个名为的函数,input_(event):但它似乎没有is_action_just_pressed输入类型,只是is_action_pressed.

我对 Godot 还很陌生,不知道如何使用信号。信号可能通过 提供帮助animation_finished(),尽管该功能可能与我在代码中实际想要做的事情无关。

这是我的代码:

extends KinematicBody2D
#Variables van aquí 

var movimiento = Vector2();
var gravedad = 20; 
var arriba = Vector2(0, -1);
var velocidadMax = 600;
var fuerza_salto = -600;
var aceleracion = 5;
var saltando = false;
func _ready(): # Esto es void Start()
    pass;

func _physics_process(delta): #Esto es void Update() 

    movimiento.y += gravedad;


    if Input.is_action_pressed("ui_right"):
        $SonicSprite.flip_h = true;
        $SonicSprite.play("Walk");
        movimiento.x = min(movimiento.x +aceleracion, velocidadMax);


    elif Input.is_action_pressed("ui_left"):
        $SonicSprite.flip_h = false;
        $SonicSprite.play("Walk");
        movimiento.x = max(movimiento.x-aceleracion, -velocidadMax);

    else:
        movimiento.x = lerp(movimiento.x, 0, 0.09);
        $SonicSprite.play("Idle");


    if is_on_floor():
        if Input.is_action_just_pressed("z"):
            movimiento.y = fuerza_salto;
    else:
        $SonicSprite.play("Jump");

    movimiento = move_and_slide(movimiento, arriba)
4

3 回答 3

2

I had the smae problem, and it was solved for me adding the next after move_and_slide():

if velocity.y == 0:
    velocity.y = 10

Apparently, if velocity is 0 after move_and_slide() it does not detect is_on_floor() anymore (bug?) so i added small velocity in direction of gravity.

About usinginput_(event), you do not need just_pressed because it only process when there is an input.. you can do somethin like this:

func _input(event):
    if event.is_action_pressed("ui_up") and is_on_floor():
        velocity.y = jump_speed

What i called velocity, i think in your script is called movimiento. Also, i think you are mixing gravities and velocities in movimiento. I'm sharing you my character scrpit so you can compare and see if it works better:

extends KinematicBody2D

var Bullet = preload("res://Bullet.tscn")

var speed = 200
var jump_speed = -300
var shot_speed = 100
var velocity = Vector2()
var grav = 980
var shooting = false

func _ready():
    $AnimatedSprite.play()
    $AnimatedSprite.connect("animation_finished",self,"on_animation_finished")
func _input(event):
    if event.is_action_pressed("ui_up") and is_on_floor():
        velocity.y = jump_speed
    if event.is_action_pressed("shoot") and !shooting:
        shooting = true
        shot_speed = 20
        velocity.y = -200
        fire_weapon()

func _physics_process(delta):
    velocity.x = 0
    if Input.is_action_pressed("ui_right"):
        velocity.x += speed
    if Input.is_action_pressed("ui_left"):
        velocity.x -= speed
    if velocity.length() > 0:
        if velocity.x < 0:
            $AnimatedSprite.flip_v = true
            $AnimatedSprite.rotation_degrees = 180 
        elif velocity.x > 0:
            $AnimatedSprite.flip_v = false
            $AnimatedSprite.rotation_degrees = 0 
    if shooting:
        $AnimatedSprite.animation = "shot"
        velocity.x = -shot_speed * cos($AnimatedSprite.rotation_degrees)
        shot_speed *= 0.98
    else:
        if is_on_floor():
            if velocity.x == 0:
                $AnimatedSprite.animation = "idle"
            else:
                $AnimatedSprite.animation = "moving"
        else:
            $AnimatedSprite.animation = "jumping"

    velocity.y += grav * delta
    velocity = move_and_slide(velocity, Vector2(0,-1))
    if velocity.y == 0:
        velocity.y = 10

func on_animation_finished():
    if $AnimatedSprite.animation == "shot":
        shooting = false

func fire_weapon():
    var bullet = Bullet.instance()
    get_parent().add_child(bullet)
    if $AnimatedSprite.flip_v :
        bullet.position = $ShotLeft.global_position
    else:
        bullet.position = $ShotRight.global_position
    bullet.rotation_degrees = $AnimatedSprite.rotation_degrees
    bullet.linear_velocity = Vector2(1500 * cos(bullet.rotation),1500*sin(bullet.rotation))

Notice that i do not use gravity as a velocity or moviemiento; instead y multiply it by delta for getting velocity, as acceleration x time gives velocity.

I hope this helps.

于 2018-11-13T16:11:18.807 回答
1

让我们剖析这里发生的事情,您启动代码并要求您的播放器在一帧中播放动画,然后在另一帧和下一帧中再次播放动画,只要按下正确的键或您处于正确的状态。

问题是什么,问题在于每次调用函数 play 它都会一次又一次地触发动画,因此它只是重新启动或另一个调用单独运行,这会导致意外行为。

更好的方法是同时管理播放器和动画播放器的状态,并使用它来执行动画调用。

enum State { IDLE, WALK, RUN, JUMP, INAIR, GROUNDED } ## This is to manage the player state
var my_state = State.IDLE  ## Start the player with the idle state

至于动画播放器状态,请使用您正在使用 GUI 或类似下面的代码谈论的信号。

get_node("Animation Player").connect("animation_finished", this, "method_name")
## Here I assume that you have the Animation Player as the Child of the Node you the script on

并且还保存一个布尔变量来判断动画是否正在播放。

if ( animation_not_playing and (case)):
    animation_player.play("animation")

根据您的喜好将其变为真或假。基于动画完成信号。

将来,您可能甚至需要考虑使用简单的 FSM 来维护所有这些状态数据和变量。

于 2018-09-12T17:51:44.267 回答
1

此类问题可以使用有限状态机来管理您的角色控制和行为来解决。您将在从步行状态进入跳跃状态时播放跳跃动画。尽早使用正确的设计模式可以防止将来出现意大利面条式代码。

这不是 Godot 特定的解决方案,但绝对值得您关注。

于 2018-09-07T17:00:21.367 回答