简体   繁体   中英

SDL_Texture move animation

i want to make interpolation for my mini game. There are 2 SDL_Textures, when i click on first and then on second, they positions are swapped, but without move animation. So how to make animated place swapping for SDL_Textures ?

void MainLoop() {

SDL_Event Event;

    float t = 0.0f;
    float dt = 0.1f;

    float currentTime = 0.0f;
    float accumulator = 0.0f;

    while (Running)
    {
        while(SDL_PollEvent(&Event)) {
            OnEvent(&Event);
        }
        const float newTime = SDL_GetTicks();
        float frameTime = newTime - currentTime;
        const Uint32 maxFrameTime = 1000; // 1 sec per frame is the slowest we allow
        if( frameTime > maxFrameTime) {
            frameTime = maxFrameTime;
        }
        currentTime = newTime;
        accumulator += frameTime;
        while( accumulator >= dt )
        {
            this->UpdatePositions(); // simulate a "frame" of logic at a different FPS than we simulate a frame of rendering
            accumulator -= dt;
            t += dt;    
        }

        Render();
}

Main Render method:

void Puzzle::Render() const {
    SDL_RenderClear(renderer);
    for (int i = 0; i < blocksNum; ++i)
    {
        Rectangle texRect = normalizeTexCoords(blockRect(blocks[i]));
        Rectangle scrRectOrigin = blockRect(i);

        DrawRect(scrRectOrigin, i, texRect);
    }

    SDL_RenderPresent(renderer);
}


void DrawRect(const Rectangle& screenCoords, int textureId, const Rectangle& textureCoord) {
    SDL_Texture *texture = blockTexture(blocks[textureId]);
    renderTexture(texture, renderer, (int)screenCoords.left, (int)screenCoords.top, (int)blockWidth, (int)blockHeight);
    //SDL_DestroyTexture(texture2);
}

void renderTexture(SDL_Texture *tex, SDL_Renderer *ren, int x, int y, int w, int h) {
    // Destination rectangle position
    SDL_Rect dst; 
    dst.x = x; dst.y = y; dst.w = w; dst.h = h;
    SDL_RenderCopy(ren, tex, NULL, &dst);
}

And in my UpdatePosition method i want to make my SDL_Textures moving, but it still swapping without animation.

void Puzzle::UpdatePositions()
{
    if (secondSwappedBlock != -1) {
        Rectangle firstRect = blockRect(blocks[firstSwappedBlock]);
        firstRect.left += speed * deltaTime;
        firstRect.right += speed * deltaTime;

        SDL_Texture *texture = blockTexture(blocks[firstSwappedBlock]);
        renderTexture(texture, renderer, (int)firstRect.left, (int)firstRect.top, (int)blockWidth, (int)blockHeight);
    }
}


void Puzzle::SwapBlocks(const int first, const int second) const
{
    if (first != second)
    {
        const int t = blocks[first];
        blocks[first] = blocks[second];
        blocks[second] = t;
    } 
}

Here is a minimal (yet fully working) example:

#include "SDL.h"
#include <assert.h>

/* linear interpolation between {start.x; start.y} and {end.x; end.y} */
static void rect_lerp(SDL_Rect *out, const SDL_Rect *start, const SDL_Rect *end, float f) {
    float t = 1.0f - f;
    out->x = (float)start->x * t + (float)end->x * f;
    out->y = (float)start->y * t + (float)end->y * f;
}

int main(int argc, char **argv) {
    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);

    SDL_Window *window = SDL_CreateWindow("example",
            SDL_WINDOWPOS_UNDEFINED,
            SDL_WINDOWPOS_UNDEFINED,
            640,
            480,
            SDL_WINDOW_SHOWN);
    assert(window);
    SDL_Renderer *renderer =  SDL_CreateRenderer(window, 0, SDL_RENDERER_ACCELERATED);
    assert(renderer);

    int quit = 0;
    int animate = 0;
    Uint32 animation_start_time;

    /* whole animation will play in e.g. one seconds */
    const Uint32 animation_time_total = 1000;

    SDL_Rect rect0 = {
        .x = 0, .y = 0, .w = 128, .h = 128
    };
    SDL_Rect rect1 = {
        .x = 256, .y = 256, .w = 128, .h = 128
    };

    while(!quit) {
        /* time of current frame */
        Uint32 tcurrent = SDL_GetTicks();

        SDL_Event event;
        while(SDL_PollEvent(&event)) {
            if(event.type == SDL_KEYUP) {
                if(event.key.keysym.sym == SDLK_ESCAPE) {
                    quit = 1;
                    break;
                } else if(event.key.keysym.sym == SDLK_SPACE) {
                    animate = 1;
                    animation_start_time = tcurrent;
                }
            }
        }

        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
        SDL_RenderClear(renderer);

        SDL_Rect r0 = rect0, r1 = rect1;
        /* if not within animation - leave coordinates as they are */
        if(animate) {
            if(tcurrent > animation_start_time + animation_time_total) {
                /* animation ends by now */
                /* swap rect0 and rect1 and stop animation */
                SDL_Rect t = rect0;
                rect0 = rect1;
                rect1 = t;
                animate = 0;

                /* need to update r0 and r1 too */
                r0 = rect0;
                r1 = rect1;
            } else {
                /* animation is incomplete - interpolate coordinates */
                /* calculate current animation percentage - in range [0; 1] */
                float factor = ((float)(tcurrent - animation_start_time)) / animation_time_total;
                rect_lerp(&r0, &rect0, &rect1, factor);
                rect_lerp(&r1, &rect1, &rect0, factor);
            }
        }

        /* r0 and r1 now have correct coordinates, draw */
        SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
        SDL_RenderDrawRect(renderer, &r0);

        SDL_SetRenderDrawColor(renderer, 255, 0, 255, 255);
        SDL_RenderDrawRect(renderer, &r1);

        SDL_RenderPresent(renderer);
    }

    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}

For sake of simplicity it draws rectangles instead of textures, but that could be easily changed.

As you can see, in that example I'm keeping old coordinates for as long as animation incomplete, and fixating it only when it is fully gone. This easily allows to replay it back and don't have problems with precision; but - if you'll try to press space when animation already playing, it will just restart.

Another way could be updating coordinates on each animation step (may have different update frequency then redraw); in that case, you'll need to keep current coordinates, destination coordinates (the ones that you'll reach once animation is complete) and, probably, start coordinates (to eliminate precision problems).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM