Search code examples
c++texturessdl

Two texture scrolling for simple water effect using SDL2 won't work as expected


I am trying to make a simple water effect in C++ using the SDL2 library. The idea is to have two textures made from the same surface, which uses a water texture bitmap image as its source. But when running the program, instead of the animation continuing indefinitely, the vertical texture sort of scrolls out of the window, followed by the horizontal one, leaving the canvas empty.

The code for the program is below

//on linux, compile with
//g++ -std=c++17  main.cpp -o prog -lSDL2 -ldl

//C++ standard libraries
#include <iostream>

//third party library
#include <SDL2/SDL.h>

int main(int argc, char* argv[]){
    if(SDL_Init(SDL_INIT_VIDEO) < 0){
        std::cout << "SDL could not be initialized: " << SDL_GetError();
    }
    else {
        std::cout << "SDL video system is ready to go\n";
    }

    SDL_Window* window = nullptr;
    window = SDL_CreateWindow("C++ SDL2 Window",
    300,300,640,480,SDL_WINDOW_SHOWN);

    SDL_Renderer* renderer = nullptr;
    renderer = SDL_CreateRenderer(window,-1,SDL_RENDERER_ACCELERATED);
    // https://github.com/MikeShah/SDL2_Tutorials/blob/main/13_scrolling/images/pool2.bmp
    SDL_Surface* surface = SDL_LoadBMP("./images/pool2.bmp");

    SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer,surface); //BG texture
    SDL_Texture* textureBlend = SDL_CreateTextureFromSurface(renderer,surface); //FG texture
    SDL_SetTextureBlendMode(textureBlend,SDL_BLENDMODE_MOD);
    SDL_FreeSurface(surface); 

    SDL_Rect horizontalFormer;
    horizontalFormer.x = 0;
    horizontalFormer.y = 0;
    horizontalFormer.w = 640;
    horizontalFormer.h = 480;

    SDL_Rect horizontalLatter;
    horizontalLatter.x = -639;
    horizontalLatter.y = 0;
    horizontalLatter.w = 640;
    horizontalLatter.h = 480;

    SDL_Rect verticalFormer;
    verticalFormer.x = 0;
    verticalFormer.y = 0;
    verticalFormer.w = 640;
    verticalFormer.h = 480;

    SDL_Rect verticalLatter;
    verticalLatter.x = 0;
    verticalLatter.y = -480;
    verticalLatter.w = 640;
    verticalLatter.h = 480;

    bool gameIsRunning = true;
    while(gameIsRunning){
        SDL_Event event;
        while(SDL_PollEvent(&event)){
            if(event.type == SDL_QUIT){
                gameIsRunning = false; 
                std::cout << "Goodbye!\n";
            }

            if(event.type == SDL_MOUSEBUTTONDOWN){
                if(event.button.button == SDL_BUTTON_RIGHT){
                    std::cout << "right mouse was clicked\n";
                    SDL_SetTextureBlendMode(textureBlend,SDL_BLENDMODE_MOD);
                }
                else if(event.button.button == SDL_BUTTON_LEFT){
                    SDL_SetTextureBlendMode(textureBlend, SDL_BLENDMODE_ADD);
                    std::cout << "left mouse was clicked\n";
                }
            }
        }

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

        int w,h;
        SDL_QueryTexture(texture,NULL,NULL,&w,&h);
        SDL_Delay(20);

        horizontalFormer.x++;
        if(horizontalFormer.x > 639){
           horizontalFormer.x = -639; 
        }

        horizontalLatter.x++;
        if(horizontalLatter.x > 639){
            horizontalFormer.x = -639;
        }

        verticalFormer.y++;
        if (horizontalFormer.y > 479){
            horizontalFormer.y = -480;
        }

        verticalLatter.y++;
        if (horizontalLatter.y > 479){
            horizontalLatter.y = -480;
        }
        
        SDL_RenderCopy(renderer, texture, NULL, &horizontalFormer);
        SDL_RenderCopy(renderer, texture, NULL, &horizontalLatter);
        SDL_RenderCopy(renderer, textureBlend, NULL, &verticalFormer);
        SDL_RenderCopy(renderer, textureBlend, NULL, &verticalLatter);

        SDL_RenderPresent(renderer);
    }
    SDL_DestroyTexture(texture);
    SDL_DestroyWindow(window);
    SDL_Quit();
    return 0;
}

I couldn't find any errors in the the position-update loop for the SDL_Rect objects, which is where I expected to find errors.


Solution

  • The sections that move and wrap the positions haven't been modified correctly between copy-pastes.

    • The second section has horizontalFormer.x = -639; instead of horizontalLatter.x = -639;
    • The third block has horizontalFormer instead of verticalFormer in two places.
    • The fourth block similarly has horizontalLatter instead of verticalLatter in two places.

    I haven't tested the code and changes as I am on mobile, but this seems like the likely culprit for the animation not repeating correctly.


    To avoid such errors, I'd suggest extracting the increment and wrap logic to a function, possibly taking a reference to make sure the same value is both read from and written to. Something like this:

    void incrementAndWrap(int& value, int minimum, int maximum) {
        value++;
        if (value >= maximum) {
            value = minimum;
        }
    }
    

    and using it something like this:

    incrementAndWrap(horizontalFormer.x, -640, 640);
    incrementAndWrap(horizontalLatter.x, -640, 640);
    incrementAndWrap(verticalFormer.y, -480, 480);
    incrementAndWrap(verticalLatter.y, -480, 480);
    

    Again, it might not compile as-is since I'm on mobile and haven't tested the code, but it should at least give you an idea of a possible way to avoid such copy-paste errors by extracting repeated code into functions.