Here’s a complete code example for scrolling an image file back and forth on the screen.
The scrolling direction changes whenever each edge of the texture is reached.
Read through all the code and make sure that you understand what everything does, and ask for anything that you’re unsure about.
Click here to view the code
#include <iostream>
#include <SDL.h>
#include <SDL_image.h>
#define WINDOW_WIDTH (800)
#define WINDOW_HEIGHT (600)
// The camera's different states
enum class ECameraState
{
CAMERA_STILL = 0,
CAMERA_SCROLLING_LEFT,
CAMERA_SCROLLING_RIGHT,
NUM_CAMERA_STATES
};
// Simple struct to hold camera data
struct SCamera
{
SCamera(void)
: XPosition(0.0f)
, YPosition(0.0f)
, Velocity(0.0f)
, Quad({0, 0, 0, 0})
, State(ECameraState::CAMERA_STILL)
{
}
SCamera(const float NewXPosition, const float NewYPosition, const float NewVelocity, const SDL_Rect& rNewQuad, const ECameraState NewState)
: XPosition(NewXPosition)
, YPosition(NewYPosition)
, Velocity(NewVelocity)
, Quad(rNewQuad)
, State(NewState)
{
}
float XPosition;
float YPosition;
float Velocity;
SDL_Rect Quad;
ECameraState State;
};
// Simple struct to hold texture data
struct STexture
{
STexture(void)
: pTexture(nullptr)
, Quad({0, 0, 0, 0})
{
}
STexture(SDL_Texture* pNewTexture, const SDL_Rect& rNewQuad)
: pTexture(pNewTexture)
, Quad(rNewQuad)
{
}
SDL_Texture* pTexture;
SDL_Rect Quad;
};
//////////////////////////////////////////////////////////////////////////
// Function declarations
bool InitializeSDL(void);
void DeinitializeSDL(void);
bool Create(void);
void Destroy(void);
void HandleEvents(void);
void Update(void);
void Render(void);
SDL_Texture* CreateTexture(const std::string& rPath);
//////////////////////////////////////////////////////////////////////////
SDL_Window* pWindow = nullptr;
SDL_Renderer* pRenderer = nullptr;
SDL_Event Event;
SCamera Camera;
STexture* pBackgroundTexture;
double DeltaTime = 0.0;
double OldTime = 0.0;
double NewTime = 0.0;
bool Running = true;
//////////////////////////////////////////////////////////////////////////
bool InitializeSDL()
{
if(SDL_Init(SDL_INIT_EVERYTHING) < 0)
{
std::cout << "Failed to initialize SDL. " << SDL_GetError() << std::endl;
return false;
}
std::cout << "SDL successfully initialized" << std::endl;
if(IMG_Init(IMG_INIT_PNG | IMG_INIT_TIF) == 0)
{
std::cout << "Failed to initialize SDL_Image. " << IMG_GetError() << std::endl;
return false;
}
std::cout << "SDL_Image successfully initialized" << std::endl;
if(SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1") == 0)
std::cout << "Failed to set the render scale quality. " << SDL_GetError() << std::endl;
std::cout << "SDL render scale quality successfully set to \"linear\"" << std::endl;
pWindow = SDL_CreateWindow("SDL image scroller", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_SHOWN);
if(!pWindow)
{
std::cout << "Failed to create the SDL window. " << SDL_GetError() << std::endl;
return false;
}
std::cout << "SDL window successfully created" << std::endl;
pRenderer = SDL_CreateRenderer(pWindow, -1, SDL_RENDERER_ACCELERATED);
if(!pRenderer)
{
std::cout << "Failed to create the SDL renderer. " << SDL_GetError() << std::endl;
return false;
}
std::cout << "SDL renderer successfully created" << std::endl;
if(SDL_SetRenderDrawColor(pRenderer, 192, 192, 192, 0xFF) == -1)
std::cout << "Failed to set the SDL render draw color. " << SDL_GetError() << std::endl;
else
std::cout << "SDL render draw color successfully set" << std::endl;
return true;
}
void DeinitializeSDL()
{
if(pRenderer)
{
SDL_DestroyRenderer(pRenderer);
pRenderer = nullptr;
}
if(pWindow)
{
SDL_DestroyWindow(pWindow);
pWindow = nullptr;
}
IMG_Quit();
SDL_Quit();
}
bool Create(void)
{
// Create the texture
SDL_Texture* pSDLTexture = CreateTexture("Data/Textures/Image.png");
if(!pSDLTexture)
return false;
int TextureWidth;
int TextureHeight;
SDL_QueryTexture(pSDLTexture, nullptr, nullptr, &TextureWidth, &TextureHeight);
SDL_Rect TextureQuad = {0, 0, TextureWidth, TextureHeight};
pBackgroundTexture = new STexture(pSDLTexture, TextureQuad);
//////////////////////////////////////////////////////////////////////////
// Create the camera
SDL_Rect CameraQuad = {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT};
Camera = SCamera(0.0f, 0.0f, 128.0f, CameraQuad, ECameraState::CAMERA_SCROLLING_RIGHT);
return true;
}
void Destroy(void)
{
SDL_DestroyTexture(pBackgroundTexture->pTexture);
pBackgroundTexture->pTexture = nullptr;
delete pBackgroundTexture;
pBackgroundTexture = nullptr;
}
void HandleEvents(void)
{
while(SDL_PollEvent(&Event))
{
switch(Event.type)
{
case SDL_QUIT:
{
Running = false;
break;
}
case SDL_KEYDOWN:
{
switch(Event.key.keysym.sym)
{
case SDLK_ESCAPE:
{
Running = false;
break;
}
}
}
}
}
}
void Update()
{
HandleEvents();
NewTime = SDL_GetTicks();
DeltaTime = (NewTime - OldTime) / 1000.0;
OldTime = NewTime;
// If the camera's state is currently scroll left, decrease the camera's position each frame
// If the camera's state is currently scroll right, increase the camera's position each frame
Camera.XPosition += ((Camera.State == ECameraState::CAMERA_SCROLLING_LEFT) ? -Camera.Velocity : Camera.Velocity) * (float)DeltaTime;
// When the camera reaches the far left side of the texture, change the camera state to scroll right
if(Camera.XPosition < 0.0f)
{
Camera.XPosition = 0.0f;
Camera.State = ECameraState::CAMERA_SCROLLING_RIGHT;
}
// When the camera reaches the far right side of the texture, change the camera state to scroll left
else if(Camera.XPosition > (float)((pBackgroundTexture->Quad.x + pBackgroundTexture->Quad.w) - Camera.Quad.w))
{
Camera.XPosition = (float)((pBackgroundTexture->Quad.x + pBackgroundTexture->Quad.w) - Camera.Quad.w);
Camera.State = ECameraState::CAMERA_SCROLLING_LEFT;
}
}
void Render()
{
SDL_RenderClear(pRenderer);
// Offset the texture's position relative to the camera
// When the camera moves, for example, to the right, move the texture to the left and vice versa
const SDL_Rect TextureQuad = { pBackgroundTexture->Quad.x - (int)Camera.XPosition,
pBackgroundTexture->Quad.y - (int)Camera.YPosition,
pBackgroundTexture->Quad.w,
pBackgroundTexture->Quad.h};
SDL_RenderCopy(pRenderer, pBackgroundTexture->pTexture, nullptr, &TextureQuad);
SDL_RenderPresent(pRenderer);
}
SDL_Texture* CreateTexture(const std::string& rPath)
{
SDL_Surface* pSurface = IMG_Load(rPath.c_str());
if(!pSurface)
{
std::cout << "Failed to create SDL surface. " << IMG_GetError() << std::endl;
return nullptr;
}
SDL_Texture* pTexture = SDL_CreateTextureFromSurface(pRenderer, pSurface);
SDL_FreeSurface(pSurface);
pSurface = nullptr;
if(!pTexture)
{
std::cout << "Failed to create SDL texture. " << SDL_GetError() << std::endl;
return nullptr;
}
return pTexture;
}
int main(int argc, char *argv[])
{
if(InitializeSDL())
{
if(Create())
{
while(Running)
{
Update();
Render();
SDL_Delay(1);
}
Destroy();
}
DeinitializeSDL();
}
return 0;
}