Hi Naith, having bit of problems with the code, especially with CreateTexture had linker problems in xCode for symbols not recognised by my architecture, at the end I had to modify it and few member functions, events managements and input and it compiled successfully and I can see my loaded background but nothing move. I think I messed up somewhere being a beginner with SDL.
If you can please upload yours I will try to run it and see how it goes, at least I know it works and I can see why mine does not work. Many thanks again for your understanding and help!
[details=Summary]#include <SDL2/SDL.h>
#include <SDL2_image/SDL_image.h>
#include <iostream>
#include <string>
using namespace std;
#define WINDOW_WIDTH (800)
#define WINDOW_HEIGHT (600)
#define SECTION_WIDTH (WINDOW_WIDTH)
#define NUM_SECTIONS (3)
#define LEVEL_WIDTH (SECTION_WIDTH * NUM_SECTIONS)
SDL_Event e;
//The window we'll be rendering to
SDL_Window* pWindow = NULL;
//The window renderer
SDL_Renderer* pRenderer = NULL;
bool InitializeSDL();
bool InitializeSDL()
{
//Initialize SDL
if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
//Set texture filtering to linear
SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "1" );
//Create window
pWindow = SDL_CreateWindow( "SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_SHOWN );
//Create vsynced renderer for window
pRenderer = SDL_CreateRenderer( pWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC );
//Initialize renderer color
SDL_SetRenderDrawColor( pRenderer, 0xFF, 0xFF, 0xFF, 0xFF );
//Initialize PNG loading
int imgFlags = IMG_INIT_PNG;
IMG_Init( imgFlags );
return true ;
}
SDL_Texture* SDL_CreateTexture(std:: string path);
void Update(void);
void Destroy();
void Destroy()
{
}
void DeinitializeSDL();
void DeinitializeSDL()
{
//Destroy window
SDL_DestroyRenderer( pRenderer );
SDL_DestroyWindow( pWindow );
pWindow = NULL;
pRenderer = NULL;
//Quit SDL subsystems
IMG_Quit();
SDL_Quit();
}
double DeltaTime = 0.0;
double OldTime = 0.0;
double NewTime = 0.0;
enum EPlayerState
{
PLAYER_STILL = 0,
PLAYER_MOVING_LEFT,
PLAYER_MOVING_RIGHT,
NUM_PLAYER_STATES
};
enum ECameraState
{
CAMERA_STILL = 0,
CAMERA_SCROLLING_LEFT,
CAMERA_SCROLLING_RIGHT,
NUM_CAMERA_STATES
};
struct SPlayer
{
float Width = 32.0f;
float Height = 64.0f;
float XPosition = (WINDOW_WIDTH * 0.5f) - (Width * 0.5f);
float Velocity = 256.0f;
EPlayerState State = PLAYER_STILL;
void HandleEvents(SDL_Event& e);
};
struct SCamera
{
float XPosition = 0.0f;
float PrevXPosition = 0.0f;
float Velocity = 512.0f;
ECameraState State = CAMERA_STILL;
SDL_Rect Quad;
};
struct STexture
{
SDL_Texture* pTexture = nullptr;
SDL_Rect Quad;
bool loadFromFile( std::string path );
void free();
};
void STexture::free()
{
//Free texture if it exists
if( pTexture != NULL )
{
SDL_DestroyTexture( pTexture );
pTexture = NULL;
}
}
SPlayer Player;
SCamera Camera;
STexture BackgroundTexture[NUM_SECTIONS];
bool QuadVsQuad(const SDL_Rect& rQuad1, const SDL_Rect& rQuad2)
{
// Quad vs quad intersection test
if(rQuad1.x < rQuad2.x + rQuad2.w &&
rQuad1.x + rQuad1.w > rQuad2.x &&
rQuad1.y < rQuad2.y + rQuad2.h &&
rQuad1.y + rQuad1.h > rQuad2.y)
return true;
else
return false;
}
bool STexture::loadFromFile( std::string path )
{
//Get rid of preexisting texture
free();
//The final texture
SDL_Texture* newTexture = NULL;
//Load image at specified path
SDL_Surface* loadedSurface = IMG_Load( path.c_str() );
if( loadedSurface == NULL )
{
cout << "Unable to load image! SDL_image Error: \n" << path.c_str() << IMG_GetError();
}
else
{
//Color key image
SDL_SetColorKey( loadedSurface, SDL_TRUE, SDL_MapRGB( loadedSurface->format, 0, 0xFF, 0xFF ) );
//Create texture from surface pixels
newTexture = SDL_CreateTextureFromSurface( pRenderer, loadedSurface );
//Get rid of old loaded surface
SDL_FreeSurface( loadedSurface );
}
//Return success
pTexture = newTexture;
return pTexture != NULL;
}
bool Create(void)
{
// All this can be done in a for loop, but I don't wanna cause confusion so I'll just do it the manual way
BackgroundTexture[0].loadFromFile("armoriktest/bg.png");
BackgroundTexture[1].loadFromFile("armoriktest/bg2.png");
BackgroundTexture[2].loadFromFile("armoriktest/bg3.png");
if(!BackgroundTexture[0].pTexture) return false;
if(!BackgroundTexture[1].pTexture) return false;
if(!BackgroundTexture[2].pTexture) return false;
int TextureWidth = 0;
int TextureHeight = 0;
// Position each texture quad next to each other, will be used for culling later
SDL_QueryTexture(BackgroundTexture[0].pTexture, nullptr, nullptr, &TextureWidth, &TextureHeight);
BackgroundTexture[0].Quad = {TextureWidth * 0, 0, TextureWidth, TextureHeight};
BackgroundTexture[1].Quad = {TextureWidth * 1, 0, TextureWidth, TextureHeight};
BackgroundTexture[2].Quad = {TextureWidth * 2, 0, TextureWidth, TextureHeight};
// Also used for culling later
Camera.Quad = {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT};
return true;
}
void SPlayer::HandleEvents( SDL_Event& e )
{
//If a key was pressed
if( e.type == SDL_KEYDOWN && e.key.repeat == 0 )
{
//Adjust the velocity
switch( e.key.keysym.sym )
{
case SDLK_LEFT: Player.Velocity-= 1; break;
case SDLK_RIGHT: Player.Velocity += 1; break;
}
}
//If a key was released
else if( e.type == SDL_KEYUP && e.key.repeat == 0 )
{
//Adjust the velocity
switch( e.key.keysym.sym )
{
case SDLK_LEFT: Player.Velocity += 1; break;
case SDLK_RIGHT: Player.Velocity -= 1; break;
}
}
}
void Update(void)
{
// Handle events from the keyboard
// When the camera scrolls, the player input is disabled
// Also sets the player's state (see EPlayerState enumeration)
Player.HandleEvents(e);
// Calculate the deltatime
NewTime = SDL_GetTicks();
DeltaTime = (NewTime - OldTime) / 1000.0;
OldTime = NewTime;
//////////////////////////////////////////////////////////////////////////
// Whenever the player is moving
if(Player.State != PLAYER_STILL)
{
// Just a fancy way of saying: "If the player state is set to PLAYER_MOVING_LEFT, move the player to the left. Else, move the player to the right"
Player.XPosition += (Player.State == PLAYER_MOVING_LEFT ? -Player.Velocity : Player.Velocity) * (float)DeltaTime;
// How near the player has to be the left- or right side of the window for the camera scrolling to start
const float DistanceToWindowEdge = 70.0f;
// If the player has reached the left side of the window
if(Player.XPosition <= (Camera.XPosition + DistanceToWindowEdge))
{
// The camera is only allowed to scroll to the left if it's not at the start of the level
if(Camera.XPosition > 0.0f)
{
Camera.State = CAMERA_SCROLLING_LEFT;
Player.State = PLAYER_STILL;
}
}
// If the player has reached the right side of the window
else if(Player.XPosition + Player.Width >= (Camera.XPosition + WINDOW_WIDTH) - DistanceToWindowEdge)
{
// The camera is only allowed to scroll to the right if it's not at the end of the level
if(Camera.XPosition < (LEVEL_WIDTH - WINDOW_WIDTH))
{
Camera.State = CAMERA_SCROLLING_RIGHT;
Player.State = PLAYER_STILL;
}
}
// Make sure that the player can't go outside the left- and the right side of the level
if(Player.XPosition < 0.0f) Player.XPosition = 0.0f;
else if(Player.XPosition > LEVEL_WIDTH - Player.Width) Player.XPosition = LEVEL_WIDTH - Player.Width;
}
// Whenever the camera is scrolling
if(Camera.State != CAMERA_STILL)
{
const bool ScrollingLeft = (Camera.State == CAMERA_SCROLLING_LEFT);
// See the player movement code above for an explanation on how this works
Camera.XPosition += ((ScrollingLeft ? -Camera.Velocity : Camera.Velocity) * (float)DeltaTime);
/*
By making the player walk a bit by itself during the scrolling, the player won't end up outside the window edges when the scrolling is finished
The walking direction depends on the direction the camera is scrolling
The walking speed is based on the camera's velocity - a bigger division value (in this case it's 0.25f) will make the player walk faster,
which means the player will end up further away from the window edge when the scrolling is finished
A smaller division value does the opposite
Note: a value smaller than 0.22f will, with the current player width, cause an endless scrolling loop bug
*/
const float Velocity = Camera.Velocity * 0.25f;
Player.XPosition += (ScrollingLeft ? -Velocity : Velocity) * (float)DeltaTime;
// Check the distance between the camera's current- and previous position
// The calculation result depends on the direction the camera is scrolling in
const float Distance = (ScrollingLeft ? (Camera.PrevXPosition - Camera.XPosition) : (Camera.XPosition - Camera.PrevXPosition));
// If the camera has scrolled a full section width
if(Distance >= SECTION_WIDTH)
{
// Calculate the camera's final position
// Again, this calculation result depends on the direction the camera is scrolling in
const float FinalCameraPosition = (ScrollingLeft ? (Camera.PrevXPosition - SECTION_WIDTH) : (Camera.PrevXPosition + SECTION_WIDTH));
Camera.XPosition = FinalCameraPosition;
Camera.PrevXPosition = FinalCameraPosition;
Camera.State = CAMERA_STILL;
}
}
}
void Render(void)
{
// Clear the current render target
SDL_RenderClear(pRenderer);
//////////////////////////////////////////////////////////////////////////
for(int i = 0; i < NUM_SECTIONS; ++i)
{
if(BackgroundTexture[i].pTexture)
{
// Create a temporary texture quad that is relative to the camera
const SDL_Rect TextureQuad = { BackgroundTexture[i].Quad.x - (int)Camera.XPosition,
BackgroundTexture[i].Quad.y,
BackgroundTexture[i].Quad.w,
BackgroundTexture[i].Quad.h};
/*
Easy and fast culling
Check if the texture quad is inside the camera quad (i.e, if the texture is in view)
If it's not in view, just continue without rendering the texture
During camera scrolling, 2 textures will be visible at the same time
When no scrolling occurs, only 1 texture will be visible and rendered
*/
if(!QuadVsQuad(Camera.Quad, TextureQuad))
continue;
SDL_RenderCopy(pRenderer, BackgroundTexture[i].pTexture, nullptr, &TextureQuad);
}
}
// Render the player as a quad
const SDL_Rect PlayerQuad = {(int)(Player.XPosition - Camera.XPosition), WINDOW_HEIGHT - (int)Player.Height, (int)Player.Width, (int)Player.Height};
SDL_SetRenderDrawColor(pRenderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
SDL_RenderFillRect(pRenderer, &PlayerQuad);
SDL_SetRenderDrawColor(pRenderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
//////////////////////////////////////////////////////////////////////////
// Update the screen
SDL_RenderPresent(pRenderer);
}
int main(int argc, char *argv[])
{
bool Running = true;
// Initialize SDL, create the SDL window, the SDL renderer and so on
if(InitializeSDL())
{
// Create the textures and other data
if(Create())
{
while(Running)
{
Update();
Render();
// Let the processor do something else for a short while to limit the processor usage
SDL_Delay(1);
}
// Destroy all created data
Destroy();
}
// Destroy SDL, the SDL window and the SDL renderer
DeinitializeSDL();
}
return 0;
}[/details]