0
votes

I'm currently learning to create my first game ever, and I chose SDL2 to be the game engine. I can make animation for the main character sprite separately. However I figured that it would be ridiculously tedious to control the sprite that way. So I loaded the sprite sheet into a 2 dimensional array hoping that I would be able to control the sprite with all kind of actions with just that one sprite sheet.

#define HOR_FRAMES 16
#define VER_FRAMES 16


    // These are for the images and sprites
    Sprite catGif;
    SDL_Rect catRect[VER_FRAMES][HOR_FRAMES];

    bool load()
    {
    bool success = true;   
    // Load cat sprite sheets
    if (!catGif.loadFromFile("./assets/pets/fighter_cat_sprite/cat_sprite_base_64.png",
                            sceneRenderer, 0X00, 0x00, 0x00))
    {
        printf("%s", SDL_GetError());
        success = false;
    }
    else
    {
        int initX = 0;
        int initY = 0;
        for (int ver = 0; ver < VER_FRAMES; ver++)
        {

            for (int hor = 0; hor < HOR_FRAMES; hor++)
            {
                catRect[ver][hor].x = initX;
                catRect[ver][hor].y = initY;
                catRect[ver][hor].w = 64;
                catRect[ver][hor].h = 64;
                initX += 64;
            }
            initX = 0;
            initY += 64;
        }
        initX = 0;
        initY = 0;
    }
    return success;
    }

Then I created a simple enum to define the actions, which is the rows in the sprite sheet

enum Actions{
    IDLE,
    WALK,
    TOTAL
};

After that, in main(), I add the control logic and set the variables in away that I think appropriately for rendering.

#include "init.h"
#include "Sprite.h"

LTexture background(SCREEN_WIDTH, SCREEN_HEIGHT);
SDL_Rect bgRect;

LTexture foreground(SCREEN_WIDTH, SCREEN_HEIGHT);
SDL_Rect fgRect;

int frame = 0;
int maxFrame = 0;
SDL_RendererFlip flipType;
int main(int argc, char* argv[])
{
    init();
    load();

    int action;

    bool quitFlag = false;
    SDL_Event event;

    while (!quitFlag)
    {
        action = IDLE;
        while (SDL_PollEvent(&event) != 0) // Handle events in queue
        {
            if (event.type == SDL_QUIT || 
                event.key.keysym.sym == SDLK_ESCAPE)
                quitFlag = true;
        }

        const Uint8* states = SDL_GetKeyboardState(NULL);

        if (states[SDL_SCANCODE_A])
        {
            maxFrame = 8;
            action = WALK;
            flipType = SDL_FLIP_HORIZONTAL;
            catGif.moveLeft();
        }
        else if (states[SDL_SCANCODE_D])
        {
            maxFrame = 8;
            action = WALK;
            flipType = SDL_FLIP_NONE;
            catGif.moveRight();
        }
        else
        {
            maxFrame = 4;
            action = IDLE;
        }

        // Drawing
        background.clipRender(0, 0, sceneRenderer, &bgRect);
        foreground.clipRender(0, SCREEN_HEIGHT / 2, sceneRenderer, &fgRect);

        SDL_Rect* currFrame = &catRect[action][frame / maxFrame];
        int x = catGif.xGetter();
        int y = catGif.yGetter();

        catGif.render(x, y, sceneRenderer, currFrame, NULL, 0, flipType);

        // Update
        SDL_RenderPresent(sceneRenderer);

        frame++;
        if (frame / maxFrame == maxFrame)
        {
            frame = 0;
        }
    }

    clean();

    return 0;
}

The sprite played smoothly as I pressed the A/D buttons. However, problems appeared after I released the buttons, the sprite sheet continued to play itself through to the end and then the sprite disappeared, even though the "action" variable has been set and there is no way that it can increment itself, I'm pretty sure about that. Please help me to understand how that happened, hopefully I can try to fix it, or take another approach. Thank you. I use code::blocks 16.01 on Fedora linux. And this is the sprite sheet: cat_sprite_sheet

1

1 Answers

0
votes

You must reset frame to 0 when changing the animation state.

{
    maxFrame = ...;
    frame = 0;
    ...
}

The bug is there because of == check in your frame increment. If current frame is 40 and you release A/D, maxFrame is set back to 4. frame / maxFrame = 40 / 4 = 10 > 4, so it will never be equal to maxFrame, and frame keeps increasing forever, thus make your sprite to disappear when [frame / maxFrame] goes out of bound. Reset frame to 0 will solve this.