Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: How to use animation for one object using different spritesheets?  (Read 541 times)

0 Members and 1 Guest are viewing this topic.

Akum

  • Newbie
  • *
  • Posts: 3
    • View Profile
I have a lot of animations for one object, and one sprite sheet can be very large. Can I use animations for the same object on different animation sprite sheets? For example, separate animations for idle, walking, attack, skill 1, skill 2, etc.?
Or would it be a much better solution to put everything on one sprite sheet?

Hapax

  • Hero Member
  • *****
  • Posts: 3353
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: How to use animation for one object using different spritesheets?
« Reply #1 on: August 02, 2023, 10:02:11 pm »
Short answer: yes, you can.

How to implement it, however, is up to you ;D

When you draw a sprite, you apply its texture during the draw call.
If you have multiple sheets, you can apply the texture required from any sheet at any time.

That's the easy bit.

If you are batching sprites - that is, drawing multiple sprites at once - you can only draw all of them using the same sheet/texture: one texture per draw call.
If you are batching sprites and they have different sheets, you would need to split them into groups: one group/draw call per sheet.

It's simpler on a single sheet, for sure, for not beyond the realms of possibility to use multiple sheets.

If, however, you're only doing it for one object (e.g. the unique player character) then it should be pretty easy indeed :)
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

Akum

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: How to use animation for one object using different spritesheets?
« Reply #2 on: August 03, 2023, 08:38:12 am »
Now when the animation state changes, my sprite shows the entire sprite sheet for 1 second. And I can't figure out how to solve it. I will be very grateful for any help.


Player.h
#pragma once
#include "Entity.h"

class Player : public Entity
{
private:
    // Variables
   
     /// !!!!
    std::string currentAnimationStatus;
    std::string newAnimationStatus;
    ///

    // Iniatilizer functions
    void initVariables();
    void initComponents();

public:
    Player(float x, float y, std::map<std::string, sf::Texture*> texture_sheets);
    virtual ~Player();

    // Functions
    virtual void update(const float& dt);
};

 

Player.cpp
#include "Player.h"

// Initializer functions

void Player::initVariables()
{
        this->currentAnimationStatus = "";
}

void Player::initComponents()
{
       
}


// Constructors / Destructors
Player::Player(float x,float y, std::map<std::string, sf::Texture*> texture_sheets)
{
       
        this->initVariables();

        this->setPosition(x, y);

        this->createHitboxComponent(this->sprite, 60.f, 60.f, 128.f, 150.f);
        this->createMovementComponent(300.f, 10.f, 6.f);
        this->createAnimationComponent(texture_sheets);

        this->animationComponent->addAnimation("IDLE", "LEFT", 10.f, 0, 1, 7, 1, 256, 256);
        this->animationComponent->addAnimation("IDLE", "RIGHT", 10.f, 0, 3, 7, 3, 256, 256);
        this->animationComponent->addAnimation("IDLE", "UP", 10.f, 0, 2, 7, 2, 256, 256);
        this->animationComponent->addAnimation("IDLE", "DOWN", 10.f, 0, 0, 7, 0, 256, 256);

        this->animationComponent->addAnimation("MOVING", "LEFT", 10.f, 0, 1, 6, 1, 256, 256);
        this->animationComponent->addAnimation("MOVING", "RIGHT", 10.f, 0, 3, 6, 3, 256, 256);
        this->animationComponent->addAnimation("MOVING", "UP", 10.f, 0, 2, 6, 2, 256, 256);
        this->animationComponent->addAnimation("MOVING", "DOWN", 10.f, 0, 0, 6, 0, 256, 256);

        //this->animationComponent->addAnimation("ATTACK_LEFT", 10.f, 0, 9, 10, 9, 256, 256);
        //this->animationComponent->addAnimation("ATTACK_RIGHT", 10.f, 0, 11, 10, 11, 256, 256);
        //this->animationComponent->addAnimation("ATTACK_UP", 10.f, 0, 10, 10, 10, 256, 256);
        //this->animationComponent->addAnimation("ATTACK_DOWN", 10.f, 0, 8, 10, 8, 256, 256);
        //

}

Player::~Player()
{

}


// Functions
void Player::update(const float& dt)
{
        this->movementComponent->update(dt);

        this->newAnimationStatus = "";

        if (this->movementComponent->getState(IDLE)) {
                newAnimationStatus = "IDLE";
                if (this->movementComponent->getLastState() == MOVING_LEFT) {
                        this->animationComponent->play("IDLE", "LEFT", dt);
                }
                else if (this->movementComponent->getLastState() == MOVING_RIGHT) {
                        this->animationComponent->play("IDLE", "RIGHT", dt);
                }
                else if (this->movementComponent->getLastState() == MOVING_UP) {
                        this->animationComponent->play("IDLE", "UP", dt);
                }
                else if (this->movementComponent->getLastState() == MOVING_DOWN) {
                        this->animationComponent->play("IDLE", "DOWN", dt);
                }
                else {
                        this->animationComponent->play("IDLE", "DOWN", dt);
       
                }
        }
        else if (this->movementComponent->getState(MOVING)) {
                newAnimationStatus = "MOVING";
                if (this->movementComponent->getState(MOVING_LEFT)) {
                        //this->animationComponent->play("MOVING", "LEFT", dt, this->movementComponent->getVelocity().x, this->movementComponent->getMovementSpeed());
                       
                        this->animationComponent->play("MOVING", "LEFT", dt);
                }
                else if (this->movementComponent->getState(MOVING_RIGHT)) {
                       
                        this->animationComponent->play("MOVING", "RIGHT", dt);
                }
                else if (this->movementComponent->getState(MOVING_UP)) {
                       
                        this->animationComponent->play("MOVING", "UP", dt);
                }
                else if (this->movementComponent->getState(MOVING_DOWN)) {
                       
                        this->animationComponent->play("MOVING", "DOWN", dt);
                }
        }
       
        if (newAnimationStatus != this->currentAnimationStatus) {
                this->currentAnimationStatus = newAnimationStatus;
                this->animationComponent->changeSpritesheet(this->currentAnimationStatus);
        }

        this->hitboxComponent->update();
}

 

AnimationComponent.h

#pragma once

#include <iostream>
#include <string>
#include <map>

#include "SFML\System.hpp"
#include "SFML\Window.hpp"
#include "SFML\Graphics.hpp"
#include "SFML\Audio.hpp"
#include "SFML\Network.hpp"

class AnimationComponent
{
private:
        class Animation {
        public:
                // Variables
                sf::Sprite& sprite;
                std::map<std::string, sf::Texture*> textureSheets;
                std::string animationStatus;
                float animationTimer;
                float timer;
                unsigned int width;
                unsigned int height;
                sf::IntRect startRect;
                sf::IntRect currentRect;
                sf::IntRect endRect;

                // Constructors
                Animation(sf::Sprite& sprite, std::map<std::string, sf::Texture*> texture_sheets, std::string animation_status,
                        float animation_timer,
                        int start_frame_x, int start_frame_y, int frames_x, int frames_y, unsigned int width, unsigned int height
                )
                        : sprite(sprite), textureSheets(texture_sheets) , animationStatus(animation_status),
                        animationTimer(animation_timer), width(width), height(height)
                {
                       
                        this->timer = 0.f;
                        this->startRect = sf::IntRect(start_frame_x * width, start_frame_y * height, width, height);
                        this->currentRect = this->startRect;
                        this->endRect = sf::IntRect(frames_x * width, frames_y * height, width, height);
                       

                        this->sprite.setTexture(*this->textureSheets["IDLE"], true);
                        this->sprite.setTextureRect(this->startRect);
                }

                // Functions
                void play(const float& dt) {
                       
                        // Update timer
                        this->timer += 100.f * dt;
                        if (this->timer >= this->animationTimer) {
                                // reset timer
                                this->timer = 0.f;


                                // Animate
                                if (this->currentRect != this->endRect) {
                                        this->currentRect.left += this->width;
                                }
                                else  // Reset
                                {  
                                        this->currentRect.left = this->startRect.left;
                                }
                               
                                this->sprite.setTextureRect(this->currentRect);
                        }
                }

                void play(const float& dt, float modifier_percentage) {
                        // Update timer
                        if (modifier_percentage < 0.7f) {
                                modifier_percentage = 0.7f;
                        }
                                 
                        this->timer += modifier_percentage * 100.f * dt;
                        if (this->timer >= this->animationTimer) {
                                // reset timer
                                this->timer = 0.f;


                                // Animate
                                if (this->currentRect != this->endRect) {
                                        this->currentRect.left += this->width;
                                }
                                else  // Reset
                                {
                                        this->currentRect.left = this->startRect.left;
                                }

                                this->sprite.setTextureRect(this->currentRect);
                        }
                }
       

                void reset() {
                        this->timer = this->animationTimer;
               
                        this->currentRect = this->startRect;

                }
        };


        sf::Sprite& sprite;
        //sf::Texture& textureSheet;
        std::map<std::string, sf::Texture*> textureSheets;
        std::string animationStatus;
        std::map<std::string, std::map<std::string, Animation*>> animations;
        Animation* lastAnimation;
        Animation* priorityAnimation;
       

       

public:
       
        // Constructors / Destructors
        AnimationComponent(sf::Sprite& sprite, std::map<std::string, sf::Texture*> texture_sheets);
        virtual ~AnimationComponent();

        // Getter
        std::string getAnimationStatus();
       
        // Functions
        void addAnimation(
                const std::string animation_status,
                const std::string animation_side,
                float animation_timer,
                int start_frame_x, int start_frame_y, int frames_x, int frames_y, unsigned int width, unsigned int height
        );
        void changeSpritesheet(std::string animation_status);

        void play(const std::string animation_status, const std::string animation_side, const float& dt);
       
};


 

AnimationComponent.cpp
#include "AnimationComponent.h"


// Constructors / Destructors
AnimationComponent::AnimationComponent(sf::Sprite& sprite, std::map<std::string, sf::Texture*> texture_sheets)
        : sprite(sprite), textureSheets(texture_sheets),lastAnimation(NULL), priorityAnimation(NULL)
{

}
       
AnimationComponent::~AnimationComponent()
{
        for (auto& i : animations) {
                for (auto& j : i.second) {
                        delete j.second;
                }
        }
}

// Getter

std::string AnimationComponent::getAnimationStatus()
{
        return this->animationStatus;
}

// Functions

void AnimationComponent::addAnimation(
        const std::string animation_status,
        const std::string animation_side,
        float animation_timer,
        int start_frame_x, int start_frame_y, int frames_x, int frames_y, unsigned int width, unsigned int height
)
{
        this->animations[animation_status][animation_side] = new Animation(
                this->sprite, this->textureSheets, animation_status,
                animation_timer, start_frame_x,
                start_frame_y, frames_x, frames_y, width, height
        );
               
}

void AnimationComponent::changeSpritesheet(std::string animation_status)
{
        if (this->animationStatus != animation_status) {
                this->animationStatus = animation_status;      
                this->sprite.setTexture(*this->textureSheets[this->animationStatus], true);
        }

}

void AnimationComponent::play(
        const std::string animation_status,
        const std::string animation_side,
        const float& dt
)
{
        // Play
        this->animations[animation_status][animation_side]->play(dt);
}
 

Hapax

  • Hero Member
  • *****
  • Posts: 3353
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: How to use animation for one object using different spritesheets?
« Reply #3 on: August 03, 2023, 10:09:59 pm »
this->sprite.setTexture(*this->textureSheets[this->animationStatus], true);
If resetRect is true, the TextureRect property of the sprite is automatically adjusted to the size of the new texture.

The sprite's texture rect, as you probably know, is the part of the texture that will be used for the sprite. If you reset it, it uses the entire texture. If you don't, it uses the previously set texture rect (which might not be the right frame but it won't be the entire texture).
As soon as you change it/set the texture (or at least before you draw/use it), the texture rect must be set to the frame you wish to use in the new sheet.
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

Akum

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: How to use animation for one object using different spritesheets?
« Reply #4 on: August 04, 2023, 04:40:16 am »
It worked, thank you very much for your help.