Thursday, March 28, 2024

Animating the sprite part4

 More entries on animated sprites, now it's turn for hitbox and hurtbox where it depends on the animated frame. Again, we'll be getting help from: https://www.youtube.com/watch?v=vDbEfmPcv-Q

We are going to start renaming a few layers and masks so we can deal with them more easily in the long run.
Key part here is understanding and differentiating what is the use of each ( https://docs.godotengine.org/en/stable/tutorials/physics/physics_introduction.html ):

Layer: describes the layers that the object appears in.
Mask: describes what layers the body will scan for collisions


Go to Project > "Project Settings" and, in the general category, scroll down until you see "Layer names". Select the "2d physics" option for this.

We'll name it in this order: World, Player, PlayerHurtbox, EnemyHurtbox


That will make our life easier when we want to know what collides with.

- Now we are going to create one scene for hurtbox and another for hitbox.


- Add a collisionShape2d child note to each of them


Note: there's a warning telling us that the node needs a shape, but we're not going to create one here. The explanations for it is inheritance, we want them as a base to attach to other nodes like the player or the enemies. That's where we are going to work with that.

- Attach the hitbox scene to the player


- Right-click on the hitbox scene you've attached to the player and select "Editable Children"


- Now we are going to create the shapes, in the child of the hitbox node from the player.


- Before working with the shape, we want a Position2D node to use a visual indicator for the position of the item that will contain the hitbox, we are going to call it "HitboxPivot"


- Time to go back to our animation to add the hitbox. Select your animatedTree


- And then select your AnimationPlayer


You want to use the frame that better displays the range of the collision shape you want to create. Let's do an example with the attackDown here.

- Select a frame to display and go to your Hitbox to edit its shape


After editing your shape, you should have something like this


- You have to key the rotation of that hitboxPivot at the first frame of the animation


- Save and repeat the process for all the attack positions (left, up, down and up)


- But the hitbox is now set to active all the time, and we want it to only be active during the attack input. To do that, select your collisionShape in the Hitbox and disable it.


- Now go to your animation frames and key the disable property, so it sets off the collision during the attack frames and disables the collision after the animation ends finishes.


This works properly now, but before finishing, remember the layers from the beginning of the entry? It's time to set them (the layers were: 1 = World, 2 = Player, 3 = PlayerHurtbox, 4 = EnemyHurtbox).

- Put your player in his layer, 2. And select where he will scan for collisions, 1 and 4.



That's all for this animated sprites section.

Wednesday, March 27, 2024

Animating the sprite part3

 Yet again another entry for animated sprites, this time to make an attack action.

We'll be working upon the previous part 1 and part 2. Assuming you have your attack action key framed already, we can go and start creating the state machine and set up the states.

- To do this, we need to create a few more variables in our script:

```
extends KinematicBody2D

const ACCELERATION = 500
const MAX_SPEED = 80
const FRICTION = 500

enum {
    MOVE,
    ROLL,
    ATTACK
}
var state = MOVE
var velocity = Vector2.ZERO

onready var animationPlayer = $AnimationPlayer
onready var animationTree = $AnimationTree
onready var animationState = animationTree.get("parameters/playback")
```

- With them, we can update our functions to select the state it's in each time. Now we update the rest of the code
 
```
func _ready():
    animationTree.active = true

func _physics_process(delta):
    match state:
        MOVE:
            move_state(delta)
        ROLL:
            pass
        ATTACK:
            attack_state(delta)
    
func move_state(delta):
    var input_vector = Vector2.ZERO
    input_vector.x = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left")
    input_vector.y = Input.get_action_strength("ui_down") - Input.get_action_strength("ui_up")
    input_vector = input_vector.normalized()

    if input_vector != Vector2.ZERO:
        animationTree.set("parameters/Idle/blend_position", input_vector)
        animationTree.set("parameters/Run/blend_position", input_vector)
        animationState.travel("Run")
        velocity = velocity.move_toward(input_vector * MAX_SPEED, ACCELERATION * delta)
    else:
        animationState.travel("Idle")
        velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta)
    
    velocity = move_and_slide(velocity)
    
    if Input.is_action_just_pressed("attack"):
        state = ATTACK

func attack_state(delta):
    animationPlayer.play("AttackDown")
```


- However we don't have our BlendingSpace state and the attack goes in loop. So, it's time to add a new blendSpace2D:

- Set transitions from/to Idle, and add your vertex for the attack node

- Now, it gets to do one attack and gets blocked because it doesn't know what to do next. To fix that, we are going to add

add a track (a function) in our animationPlayer for each attack, we'll do one attack here as example

- Select "call method track", and select your player node in the pop-up

- Create a function for the attack finishing in your player script:
```
func attack_animation_finished():
    state = MOVE
```


- Go back to your animation animationPlayer and add an animation key in the newly appeared track.

- You want to select your created function `attack_animation_finished`

- Do the same for all your attack animations. Before running the game, your code for the player script should currently look like this:
```
extends KinematicBody2D

const ACCELERATION = 500
const MAX_SPEED = 80
const FRICTION = 500

enum {
    MOVE,
    ROLL,
    ATTACK
}
var state = MOVE
var velocity = Vector2.ZERO

onready var animationPlayer = $AnimationPlayer
onready var animationTree = $AnimationTree
onready var animationState = animationTree.get("parameters/playback")

func _ready():
    animationTree.active = true

func _physics_process(delta):
    match state:
        MOVE:
            move_state(delta)
        ROLL:
            pass
        ATTACK:
            attack_state(delta)
    
func move_state(delta):
    var input_vector = Vector2.ZERO
    input_vector.x = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left")
    input_vector.y = Input.get_action_strength("ui_down") - Input.get_action_strength("ui_up")
    input_vector = input_vector.normalized()

    if input_vector != Vector2.ZERO:
        animationTree.set("parameters/Idle/blend_position", input_vector)
        animationTree.set("parameters/Run/blend_position", input_vector)
        animationTree.set("parameters/Attack/blend_position", input_vector)
        animationState.travel("Run")
        velocity = velocity.move_toward(input_vector * MAX_SPEED, ACCELERATION * delta)
    else:
        animationState.travel("Idle")
        velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta)
    
    velocity = move_and_slide(velocity)
    
    if Input.is_action_just_pressed("attack"):
        state = ATTACK

func attack_state(delta):
    animationState.travel("Attack")

func attack_animation_finished():
    state = MOVE
```


If you followed this entry carefully, your character should have animations for moving around and attacking.
The next step should be adding hitboxes and hurtboxes, however, since this has become a bit of a big entry, will do those in the next entry. (Meanwhile, you can add a roll action following the attack example)

Animating the sprite part2

 Back again to our sprite animations with help from https://youtu.be/Z9aR9IiiHT8?si=U23rQSYZ-W73UEuE .

For this entry, will be creating and setting an animation tree to properly animate in all directions without the need of creating huge chunks of code for each direction.

Start adding the AnimationTree child node to your Player.tscn scene.

After that, we'll see an empty new tab. We are going to set the properties next.

The animationTree node needs an AnimationPlayer, our next step is telling the node what animation tree should use.

- Go to its properties, at the right side of the screen, and click on Anim. Player.
   
- A prompt with your scene tree will pop-up. Select your AnimationPlayer:

- Now we need to tell it the tree root. Go to its properties, again, and click on Tree Root. This time will be using New AnimationNodeStateMachine. Don't forget to click on "Active" (the debug panel will show you a warning to remind you though).

- Right click on the new displayed panel, and select BlendSpace2D

- Click over it to rename it, "Idle". This will contain all of our animations for idle states.

- Click on the icon at the right side of that to edit the BlendSpace2D. It says no blending triangles, you have to set points for the triangles.

- Now, click on each vertex and select the proper animation for it. (Remember that the y poles are reversed in the screen. 1 = down)

- When you finish each of the vertex for the idle positions, change your blend type to the three dots (this is because we are using a pixel art, and we don't want the images to be interpolated), you can see this

- Now you want to make another BlendSpace2D for "run", and add your run animation vertexes.

- Time to make the transition between both, select connect points and use it to join both blendingSpaces

- Toggle the autoplay for idle, so it's active when it starts
   
- Be sure you have all your animatedPlayer for NOT iddle actions frames looping, for them to work when you keep the same direction without stopping.

- With this, we can use those in our script:
```
extends KinematicBody2D

const ACCELERATION = 500
const MAX_SPEED = 80
const FRICTION = 500

var velocity = Vector2.ZERO

onready var animationPlayer = $AnimationPlayer
onready var animationTree = $AnimationTree
onready var animationState = animationTree.get("parameters/playback")

func _ready():
    animationTree.active = true

func _physics_process(delta):
    var input_vector = Vector2.ZERO
    input_vector.x = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left")
    input_vector.y = Input.get_action_strength("ui_down") - Input.get_action_strength("ui_up")
    input_vector = input_vector.normalized()

    if input_vector != Vector2.ZERO:
        animationTree.set("parameters/Idle/blend_position", input_vector)
        animationTree.set("parameters/Run/blend_position", input_vector)
        animationState.travel("Run")
        velocity = velocity.move_toward(input_vector * MAX_SPEED, ACCELERATION * delta)
    else:
        animationState.travel("Idle")
        velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta)
    
    velocity = move_and_slide(velocity)

```


This entry may look like not much after part 1, but it will help a lot to have a clean code and it leaves
a good basis for the next one, which will be for creating the attack action and writing code to use it.

Tuesday, March 26, 2024

Animating the sprite part1

 This entry uses the pixel art setup from the previous one, and again uses help from https://youtu.be/wX145eoLFSM?si=VXPIxsxfap8fprIX
We'll be using the resources from the video, so be sure to have them imported in your project.

Create a new scene (Player.tscn), add a KinematicBody2D root node with: a Sprite and a CollisionShape2D subnode. Like this:


Once you have done that, we can select our sprite subnode and add a new texture (created with animation in mind).

- Drag and drop your texture on the Texture cell:


As a result, you'll have something like this:


This is a full sprite sheet that contains all the animations for our player character, However we want it to only show a single image. In order to do that, we need to:

- (Still on your sprite) Go to Animation


- Select the number of frames for your texture, in our case is 60, and voilĂ !


Note: If we update the "Frame" parameter below, we can see it cycle through the frames (and see it animated).

Since we want the game's camera to focus and move with our character, add a camera2d node and be sure the "current" property is checked. [This camera part is not from the video]

After that, we can attach a script to our character so we can move it around the screen. We'll be updating it later, but for now (to test the basis) this will do:
[I'm just writing down the code from the video, because it will be edited to control the animations]
```
extends KinematicBody2D

const ACCELERATION = 500
const MAX_SPEED = 80
const FRICTION = 500

var velocity = Vector2.ZERO

func _physics_process(delta):
    var input_vector = Vector2.ZERO
    input_vector.x = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left")
    input_vector.y = Input.get_action_strength("ui_down") - Input.get_action_strength("ui_up")
    input_vector = input_vector.normalized()

    if input_vector != Vector2.ZERO:
        velocity = velocity.move_toward(input_vector * MAX_SPEED, ACCELERATION * delta)
    else:
        velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta)
    
    velocity = move_and_slide(velocity)
```


Now we can finally start animating our character, https://youtu.be/wX145eoLFSM?si=wVja37tuGZ-nE8VD

Start by adding a new AnimationPlayer subnode to your Player.tscn

This node basically lets you key any property in frames and have it animate. To do that, go to the animation section, below in your window and click on Animation:

A new menu will drop, click on new, and give your animation a name:

Since we are following the video, we named it "RunRight", and we have 6 frames (length = 0.6)

Now we have to select the property we want to animate, which is in the sprite, the frame property.

- Start with frame 1, and click on the key

- A pop-up will appear, deselect the checks and click on create

- A new frame will be added to your animation panel

- Go to the animation panel and move to the next frame, then add the new key

- Continue until you have added all the 6 frames in that animation, and save your scene.

- Repeat with any animation you want to add.

After creating your animations, go to the player script and update it to make them run in your game.
```
extends KinematicBody2D

const ACCELERATION = 500
const MAX_SPEED = 80
const FRICTION = 500

var velocity = Vector2.ZERO

onready var animationPlayer = $AnimationPlayer

func _physics_process(delta):
    var input_vector = Vector2.ZERO
    input_vector.x = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left")
    input_vector.y = Input.get_action_strength("ui_down") - Input.get_action_strength("ui_up")
    input_vector = input_vector.normalized()

    if input_vector != Vector2.ZERO:
        if input_vector.x > 0:
            animationPlayer.play("RunRight")
        else:
            animationPlayer.play("RunLeft")
        velocity = velocity.move_toward(input_vector * MAX_SPEED, ACCELERATION * delta)
    else:
        animationPlayer.play("IddleRight")
        velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta)
    
    velocity = move_and_slide(velocity)
```


You can now execute your game and try your animated sprite. There is still another thing to add, the state machines to clean the code and add different actions like an attack. That will be done in the next entry.