I had a functioning fighting game written in Python with movement, frame data, attacks (that didn't work) but no gravity. I'm trying to rewrite it with Godot, but I'm really lost.
I had a system of implementing frame data into my attacks so I could actually transition from idle to attacks and vice versa easily. Right now, my issue is I want to attack and play that animation, but it keeps constantly playing the idle animation. I want to get this one thing working before I start doing hitboxes and collision stuff. Here is my code:
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)
This code is basically just a mishmash of a bunch of tutorials of things I was told to do (Like for instance, I was told not use AnimatedSprite
and instead use AnimationTree
, but it seems the code is a lot more complicated and is just giving me the same problems as last time). If you need me to, I can also post my Python code as well, but it's a giant mess with no comments on it at all.
You can make animations with AnimatedSprite
. You have my blessing. AnimatedSprite
is there to be used. And "I was told so" is not a good reason not to.
Also, AnimatedSprite
vs AnimationPlayer
and AnimationTree
is not an either or. Yes, there are somethings for which you would need AnimationPlayer
and AnimationTree
, however you can always have the AnimationPlayer
trigger an AnimatedSprite
animation. In fact, AnimationPlayer
can call any method (and AnimationTree
will control the AnimationPlayer
). Thus, any work done with AnimatedSprite
is not wasted.
Calling set_physics_process
will enable or disable _physics_process
. And similarly set_process
enables or disables _process
. However, if you have a the method then it is enabled by default. Thus, you only need to enable them if you disabled them.
We can compress a lot of your _physics_process
code:
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)
The line anim.flip_h = walking_speed < 0
will be setting flip_h
to true
when walking_speed
is negative, and to false
otherwise.
And we are setting walking_speed
based on get_action_strength
will return a number between 0
and 1
that represents how strongly the action is pressed. Which also means that if they are mapped to a joystick or similar, this gives you sensitivity support.
If you prefer to not have that sensitivity, sign
will do:
var walking_speed := sign(
Input.get_action_strength("ui_right") -
Input.get_action_strength("ui_left")
)
With KinematicBody2D
you would have to handle gravity. Basically give it some downwards velocity.
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)
However, it is better to implement gravity with 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
I have removed the multiplication with delta
at the end because move_and_slide
does that internally. That is, it takes a velocity not a displacement.
This way the falling_speed
will not keep growing out of control.
You can also add an up vector (so Godot knows what is a ceiling, what is a floor, and what is a wall), and then you can check is_on_ceiling
, is_on_floor
and is_on_wall
.
Also, you can tell move_and_slide
that the floor_max_angle
is 0
, so it does not slide (nothing counts as a slope, only perfectly horizontal floors are floors), if that is what you want.
And, of course, the animation.
If you are going to use an AnimationTree
, I suggest to set the root as an AnimationNodeStateMachine
. Then, from code, you can get the state machine playback object, like this:
var state_machine = $AnimationTree.get("parameters/playback")
You can tell it you want it to go to a different state, like this:
state_machine.travel("name_of_some_state")
And you can ask it what the current state is with:
var state:String = state_machine.get_current_node()
You can, for example have an "idle" state in your state machine, and an "attacking" state in your state machine, with a transition from "idle" to "attacking", and another transition from "attacking" to "idle", but that one set to transition "at the end". You can, of course, have other states. Then use the current state (from get_current_node
) to figure out what the player can do.
With an AnimationPlayer
you can check what is the current_animation
and is_playing
; you can also connect the "animation_finished"
signal. With an AnimatedSrite
you can check what is the current animation
and whether or not it is playing
; you can also connect the "animation_finished"
signal.