Nice! Ill wait for the code
Here are the code for the test application above.
I have written some comments that explains how the camera works, it should be pretty self explanatory. If you have any questions or issues, feel free to ask.
Click to view
#include <stdio.h>
#include <string>
#include <SDL.h>
#define WINDOW_WIDTH (800)
#define WINDOW_HEIGHT (600)
// The total length of the level (in this case the total length is two windows/screens in total)
#define LEVEL_WIDTH (WINDOW_WIDTH * 2.0f)
SDL_Window* Window = nullptr;
SDL_Renderer* Renderer = nullptr;
SDL_FPoint PlayerSize = {40.0f, 100.0f};
SDL_FRect Player = {200.0f, WINDOW_HEIGHT - PlayerSize.y, PlayerSize.x, PlayerSize.y};
SDL_FRect Camera = {0.0f, 0.0f, WINDOW_WIDTH, WINDOW_HEIGHT};
// Some rectangles to render to the screen
SDL_FRect BackgroundObject1 = { 50.0f, 50.0f, 300.0f, 50.0f};
SDL_FRect BackgroundObject2 = { 600.0f, 200.0f, 300.0f, 50.0f};
SDL_FRect BackgroundObject3 = {1200.0f, 100.0f, 300.0f, 50.0f};
float PlayerSpeed = 300.0f;
double OldTime = 0.0;
double NewTime = 0.0;
double DeltaTime = 0.0;
double Lifetime = 0.0;
bool Running = true;
bool LHeld = false;
bool RHeld = false;
void ShowMessageBox(const std::string& Message, const std::string& Title, const SDL_MessageBoxFlags MessageType)
{
const std::string FormattedMessage = Message + std::string(SDL_GetError());
SDL_ShowSimpleMessageBox(MessageType, Title.c_str(), FormattedMessage.c_str(), Window);
}
bool InitializeSDL()
{
if(SDL_Init(SDL_INIT_VIDEO) < 0)
{
ShowMessageBox("Failed to initialize SDL. ", "Error", SDL_MessageBoxFlags::SDL_MESSAGEBOX_ERROR);
return false;
}
Window = SDL_CreateWindow("SDL - Camera", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_SHOWN);
if(!Window)
{
ShowMessageBox("Failed to create SDL window. ", "Error", SDL_MessageBoxFlags::SDL_MESSAGEBOX_ERROR);
return false;
}
Renderer = SDL_CreateRenderer(Window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if(!Renderer)
{
ShowMessageBox("Failed to create SDL renderer. ", "Error", SDL_MessageBoxFlags::SDL_MESSAGEBOX_ERROR);
return false;
}
if (SDL_SetRenderDrawColor(Renderer, 0, 0, 0, 255) < 0)
ShowMessageBox("Failed to set render draw color. ", "Warning", SDL_MessageBoxFlags::SDL_MESSAGEBOX_WARNING);
if (SDL_SetRenderDrawBlendMode(Renderer, SDL_BLENDMODE_BLEND) < 0)
ShowMessageBox("Failed to set render draw blend mode. ", "Warning", SDL_MessageBoxFlags::SDL_MESSAGEBOX_WARNING);
if (SDL_RenderClear(Renderer) == 0)
SDL_RenderPresent(Renderer);
return true;
}
void DeinitializeSDL()
{
SDL_DestroyRenderer(Renderer);
SDL_DestroyWindow(Window);
SDL_Quit();
}
void HandleEvents()
{
SDL_Event Event = {};
while(SDL_PollEvent(&Event))
{
switch(Event.type)
{
case SDL_QUIT:
{
Running = false;
break;
}
default:
break;
}
}
}
void Update()
{
NewTime = (double)SDL_GetTicks();
DeltaTime = (NewTime - OldTime) * 0.001;
OldTime = NewTime;
Lifetime += DeltaTime;
const Uint8* KeyboardState = SDL_GetKeyboardState(nullptr);
LHeld = (KeyboardState[SDL_SCANCODE_LEFT] == SDL_TRUE);
RHeld = (KeyboardState[SDL_SCANCODE_RIGHT] == SDL_TRUE);
if (LHeld && !RHeld) Player.x -= PlayerSpeed * (float)DeltaTime;
else if (RHeld && !LHeld) Player.x += PlayerSpeed * (float)DeltaTime;
if (Player.x < 0.0f) Player.x = 0.0f;
else if (Player.x > (LEVEL_WIDTH - Player.w)) Player.x = LEVEL_WIDTH - Player.w;
// This makes the camera follow the player, with the player always being in the middle of the camera view
Camera.x = Player.x - (Camera.w * 0.5f);
// Make sure that the camera can't leave the level area
if (Camera.x < 0.0f) Camera.x = 0.0f;
else if (Camera.x > (LEVEL_WIDTH - Camera.w)) Camera.x = LEVEL_WIDTH - Camera.w;
}
void Render()
{
SDL_SetRenderDrawColor(Renderer, 0, 0, 0, 255);
SDL_RenderClear(Renderer);
SDL_SetRenderDrawColor(Renderer, 255, 255, 255, 255);
const SDL_FRect PlayerWorld = {Player.x - Camera.x, Player.y, Player.w, Player.h};
SDL_RenderFillRectF(Renderer, &PlayerWorld);
// Create the graphical representations of the rectangles. These will placed in world space by offsetting the object's positions by the camera's position
// When the player and the camera moves to the right, the rectangles will move to the left (and vice versa), simulating a scrolling game world
const SDL_FRect BackgroundObject1World = {BackgroundObject1.x - Camera.x, BackgroundObject1.y, BackgroundObject1.w, BackgroundObject1.h};
const SDL_FRect BackgroundObject2World = {BackgroundObject2.x - Camera.x, BackgroundObject2.y, BackgroundObject2.w, BackgroundObject2.h};
const SDL_FRect BackgroundObject3World = {BackgroundObject3.x - Camera.x, BackgroundObject3.y, BackgroundObject3.w, BackgroundObject3.h};
SDL_SetRenderDrawColor(Renderer, 255, 0, 0, 255);
SDL_RenderFillRectF(Renderer, &BackgroundObject1World);
SDL_RenderFillRectF(Renderer, &BackgroundObject2World);
SDL_RenderFillRectF(Renderer, &BackgroundObject3World);
SDL_RenderPresent(Renderer);
}
int main(int argc, char** argv)
{
if(!InitializeSDL())
return -1;
while(Running)
{
HandleEvents();
Update();
Render();
}
DeinitializeSDL();
return 0;
}
Thank you for the code!! I’ll take a look once i get some time to look!!!
Just got.my hands on the code and i love it!!! Cant thank you enough for your hardwork even though you didnt have to go through with this. But i highly appreciate it!!!
This is an old thread but I wanted to post a resource I found useful: Tiles and tilemaps overview (Mozilla) This is probably essentially what other people have covered but it’s easily adaptable and has code examples.
It looks like your question has already been answered, but I’d still like to share my exploration of 2D scenes and cameras.
My work
Like you, I’m very interested in this kind of abstraction. After googling and reading a lot of posts and investigating various SDL APIs, I finally achieved a result that I’m satisfied with:
Scene and Camera
new users can only put one embedded media item in a post. So other gif will be a normal link.
In the following, the term “window” refers to the actual drawing area, which can be modified to a specific viewport using the
SDL_SetRenderViewport
method. This is unrelated to the abstraction.
First, we abstract a scene where all renderable objects are added to the scene. We assume the scene is an infinitely large coordinate system with a fixed orientation. The position information of all objects within the scene is relative to the origin of the scene. This abstraction helps manage renderable objects in a game. When you want a character to move to the right, you only need to increase its x-coordinate, i.e., change its position in the scene. Done. As far as game logic is concerned, you don’t need to worry about its render position—no additional mental burden. This abstraction is also renderer-independent. You can use it in different programming environments, such as a web canvas.
However, we obviously can’t draw an infinitely large scene onto the screen, so we introduce the concept of a camera: a limited rectangular area that captures part of the scene and projects it onto the actual window. The position of the camera is also relative to the scene’s origin. I suppose this is why in many implementations, the camera is treated as a subclass of renderable objects, like Three.js Camera.
For example, let’s say you have a star represented as a point in the scene, located at (1000, 1000), and the camera is also moved to (1000, 1000). This star will appear in the top-left corner of the camera’s view. If the top-left corner of the camera’s view is aligned with the top-left corner of the actual window, the star will appear at window coordinates (0, 0). This means we should call SDL_RenderPoint(renderer, 0, 0)
to draw the star.
Requirement: Regardless of the object’s position in the scene, the camera should capture it onto the actual window;
What we have: The camera’s position and the object’s position within the scene;
What we want: The object’s position relative to the window;
Calculation method: (x_on_window, y_on_window) = (point_x, point_y) - (camera_x, camera_y)
Note: This method assumes that the top-left corner of the camera view is aligned with the top-left corner of the window (and typically, there’s no need to consider other cases).
void game::Renderer::render(const Scene& scene, const Camera& camera) {
Vector2 offset = { -camera.position.x, -camera.position.y };
for (auto child : scene.children) {
auto& object2d = child.get();
object2d.render(this->sdl_renderer, offset); // Calls game::Point::render(...)
}
}
void game::Point::render(Vector2 offset) const {
// (this->x, this->y) is relative to the scene, not the window
int x{ this->x + offset.x };
int y{ this->y + offset.y };
SDL_RenderPoint(sdl_renderer.get(), x, y);
}
Zooming
How do we achieve the effect of zooming the camera in and out? The answer is scaling.
If the camera’s size matches the window size, it’s rendered at a 1:1 ratio. But the camera is an abstract rectangle within the scene and shouldn’t always conform to the actual window. We might want to place a small camera in the scene to capture only a small portion of the content. Are we supposed to render just a tiny 192*108
region on the window? That would look too small. Fortunately, the SDL_SetRenderScale
method allows us to set a scaling factor. For example, if the window size is 1920*1080
, we only need to call SDL_SetRenderScale(renderer, 10, 10)
to scale the content to fit the window size. Thus, reducing the camera size simulates the effect of zooming in.
Requirement: Regardless of the camera’s size in the scene, all pixels inside the camera should be rendered fully onto the entire window;
What we have: The camera’s size in the scene and the actual window size;
What we want: The ratio of the camera size to the window size;
Calculation method: (scale_x, scale_y) = (window_w, window_h) / (camera_w, camera_h)
Note: When the scaling factors for the x and y directions are different, a horizontal or vertical stretching effect will occur, which might be interesting.
void game::Renderer::render(const Scene& scene, const Camera& camera, bool camera_scale_to_window) {
Vector2F scale{ 1.0, 1.0 };
if (camera_scale_to_window) {
int window_w{ 0 };
int window_h{ 0 };
SDL_Window* sdl_window = SDL_GetRenderWindow(this->sdl_renderer.get());
if (!sdl_window || !SDL_GetWindowSizeInPixels(sdl_window, &window_w, &window_h)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "ERROR", "Couldn't get window size.", nullptr);
}
scale.x = (float)window_w / camera.w;
scale.y = (float)window_h / camera.h;
SDL_SetRenderScale(this->sdl_renderer.get(), scale.x, scale.y);
}
Vector2 offset = { -camera.position.x, -camera.position.y };
for(auto child : scene.children) {
auto& object2d = child.get();
object2d.render(this->sdl_renderer, offset); // Call game::Point::render(...)
}
}
Better Scaling
In practice, I wasn’t very satisfied with the effect of SDL_SetRenderScale
. As shown in the image, when there’s no scaling, the drawn line is just 1 pixel wide. However, when scaling is applied, a line becomes much thicker, and a point turns into a rectangular block.
For me, this doesn’t meet my expectations. I implemented a scaling method that aligns better with what I had in mind. The principle is as follows:
Suppose there’s a point at (1, 2) in the camera’s view. When the window size is 10 times larger than the camera’s size, this point will move to (10, 20) on the window, but it shouldn’t turn into a rectangular block. The same applies to lines. We draw lines using their start and end points, so scaling a line means adjusting the positions of these points without increasing its thickness. For rectangles, there’s more to consider: the width and height should also be scaled accordingly.
Requirement: Scaling from the camera to the window should not affect the appearance of rendered objects.
If the user needs to enlarge objects (like Mario after eating a mushroom), that should be handled by increasing the object’s actual size in the scene. This is part of the game logic and is unrelated to our rendering encapsulation.
void game::Renderer::render(const Scene& scene, const Camera& camera, bool camera_scale_to_window) {
Vector2F scale{ 1.0, 1.0 };
if (camera_scale_to_window) {
int window_w{ 0 };
int window_h{ 0 };
SDL_Window* sdl_window = SDL_GetRenderWindow(this->sdl_renderer.get());
if (!sdl_window || !SDL_GetWindowSizeInPixels(sdl_window, &window_w, &window_h)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "ERROR", "Couldn't get window size.", nullptr);
}
scale.x = (float)window_w / camera.w;
scale.y = (float)window_h / camera.h;
// SDL_SetRenderScale(this->sdl_renderer.get(), scale.x, scale.y); // *
}
Vector2 offset = { -camera.position.x, -camera.position.y };
for(auto child : scene.children) {
auto& object2d = child.get();
object2d.render(this->sdl_renderer, offset, scale); // **
}
}
void game::Point::render(Vector2 offset, Vector2F scale) const {
int x{ this->x + offset.x };
int y{ this->y + offset.y };
// ***
x *= scale;
y *= scale;
SDL_RenderPoint(sdl_renderer.get(), x, y);
}
At the comment marked *
: SDL_SetRenderScale
is no longer needed.
At the comment marked **
: Pass the scaling factor to the specific rendering logic.
At the comment marked ***
: Apply scaling to the point’s position (and if rendering a rectangle, scale the rectangle’s size as well).
camera_example_gif_3|328x248
Clipping
There’s one last step: Sometimes, we zoom the camera, but not to fill the entire window. In that case, we need to clip parts that extend beyond the camera’s view to prevent them from being displayed. We use SDL_SetRenderClipRect
for this. One thing to note is that the clipping region is relative to the window, and since the view is still aligned with the window’s top-left corner, the clipping region starts at (0, 0). The size of the clipping region is the camera size multiplied by the scaling factor.
void game::Renderer::render(const Scene& scene, const Camera& camera, bool camera_scale_to_window) {
// ...
const SDL_Rect clip_rect{0, 0, (int)(scale.x * camera.w), (int)(scale.y * camera.h) };
SDL_SetRenderClipRect(this->sdl_renderer.get(), &clip_rect);
// ...
}
Additional
You can wrap scaling, camera-induced translation, and camera rotation effects into a 3x3
matrix, which is a popular approach. I’m working on it and making progress, but I haven’t fully succeeded yet.
Thnx for the response! Ill take any answers and them out. Im not advanced with sdl just yet, but i did try to do a camera with the viewport, as in using SDL_RenderSetViewport, and didnt quite work out well.
Love the code!! as i’m studying this code, my player is going past the camera width and camera height, meaning the player is passing through the camera rather than staying in the middle of the camera. can you tell me what I’m doing wrong? If I need to post code, let me know
If you used my code example correctly, the camera and player shouldn’t be able to leave the level area.
I want to note that no bounds checking is done vertically, only horizontally, which means the player and the camera can leave the level area by going upwards or downwards. It’s rather easy to do bounds checking for vertical movement though. You do it the same way as horizontal but check the Y-direction instead of the X-direction.
It’s hard to know what’s wrong with your code. You’ll have to paste it all here.
#include <iostream>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#undef main
int LEVEL_WIDTH;
int LEVEL_HEIGHT;
int wWidth;
int wHeight;
float camSpeed;
SDL_Rect positionRect;
SDL_Rect positionRect2;
float playerSpeed = 500;
double OldTime = 0.0;
double NewTime = 0.0;
double DeltaTime = 0.0;
double Lifetime = 0.0;
bool LHeld = false;
bool RHeld = false;
int main()
{
camSpeed = 50;
wWidth = 800;
wHeight = 600;
LEVEL_WIDTH = 2000;
LEVEL_HEIGHT = 1000;
SDL_Rect camera{ 0,0,wWidth,wHeight };
///Camera above
SDL_FPoint PlayerSize = { 40.0f, 100.0f };
SDL_FRect Player = { wWidth - PlayerSize.x, wHeight - PlayerSize.y, PlayerSize.x, PlayerSize.y };
Player.x = 1;
Player.y = 90;
if (SDL_Init(SDL_INIT_VIDEO))
{
printf("Error: %s", SDL_GetError());
return -1;
}
SDL_Window* window = SDL_CreateWindow("Spine SDL", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, wWidth, wHeight,
0);
if (!window) {
printf("Error: %s", SDL_GetError());
return -1;
}
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (!renderer) {
printf("Error: %s", SDL_GetError());
return -1;
}
//SDL_Texture* texture = LoadTexture("backText.png", renderer);
SDL_Texture* texture = IMG_LoadTexture(renderer, "background.png");
SDL_Texture* sample = IMG_LoadTexture(renderer, "pep.png");
bool quit = false;
uint64_t lastFrameTime = SDL_GetTicks();
while (!quit)
{
NewTime = (double)SDL_GetTicks();
DeltaTime = (NewTime - OldTime) * 0.001;
OldTime = NewTime;
Lifetime += DeltaTime;
SDL_Rect rect;
rect.x = 0;
rect.y = 0;
rect.w = 200;
rect.h = 200;
SDL_Event event;
while (SDL_PollEvent(&event) != 0)
{
if (event.type == SDL_QUIT)
{
quit = true;
break;
}
if (SDL_KEYDOWN == event.type)
{
if (SDLK_LEFT == event.key.keysym.sym)
{
//Player.x -= playerSpeed;
}
if (SDLK_UP == event.key.keysym.sym)
{
}
if (SDLK_RIGHT == event.key.keysym.sym)
{
//Player.x += playerSpeed;
}
if (SDLK_DOWN == event.key.keysym.sym)
{
}
}
}
//SDL_RenderCopy(renderer, texture, &camera, NULL);
///This is to make an object stay still. CODE IS IMPORTANT
SDL_Rect drawingRect = { positionRect.x - camera.x,positionRect.y - camera.y, positionRect.w,positionRect.h };
SDL_Rect drawingRect2 = { positionRect2.x - camera.x,positionRect2.y - camera.y, positionRect2.w,positionRect2.h };
///CODE IMPORTANT ABOVE THIS GREEN LINE
positionRect.x = Player.x;
positionRect.y = Player.y;
rect.x = camera.x;
rect.y = camera.y;
camera.x = (Player.x) - wWidth / 2;
if (Player.x > LEVEL_WIDTH)
Player.x = LEVEL_WIDTH;
if (positionRect2.x < 0.0f)
positionRect2.x = 0.0f;
else if (positionRect2.x > (LEVEL_WIDTH - positionRect2.w))
positionRect2.x = LEVEL_WIDTH - positionRect2.w;
const Uint8* KeyboardState = SDL_GetKeyboardState(nullptr);
LHeld = (KeyboardState[SDL_SCANCODE_LEFT] == SDL_TRUE);
RHeld = (KeyboardState[SDL_SCANCODE_RIGHT] == SDL_TRUE);
if (LHeld && !RHeld)
Player.x -= playerSpeed * (float)DeltaTime;
else if (RHeld && !LHeld)
Player.x += playerSpeed * (float)DeltaTime;
if (Player.x < 0.0f)
Player.x = 0.0f;
else if (Player.x > (LEVEL_WIDTH - Player.w))
Player.x = LEVEL_WIDTH - Player.w;
if (camera.x < 0)
{
camera.x = 0;
}
if (camera.y < 0)
{
camera.y = 0;
}
if (camera.x > LEVEL_WIDTH - camera.w)
{
camera.x = LEVEL_WIDTH - camera.w;
}
if (camera.y > LEVEL_HEIGHT - camera.h)
{
camera.y = LEVEL_HEIGHT - camera.h;
}
SDL_SetRenderDrawColor(renderer, 94, 93, 96, 255);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, &camera, NULL);
///
SDL_RenderCopy(renderer, sample, NULL, &rect);
SDL_RenderPresent(renderer);
}
SDL_DestroyWindow(window);
SDL_DestroyRenderer(renderer);
SDL_DestroyTexture(texture);
texture = nullptr;
SDL_Quit();
return 0;
}
this is an ugly demo I setup, but my blue box is currently moving smoothly but the box is not being centered in the camera and goes over the camera as you can see and not being set to the the middle of the camera
I’m sorry but your code is way too messy and for some reason you’re using multiple SDL_Rect’s that you’re copying data between. It’s very hard to understand your code.
Like I wrote - if you use my code that I pasted above, the camera will work like you want it to work.
Perhaps if you can explain what it is you want to achieve?
Do you want to render a texture where the player’s rect is rendered and have a scrolling background image?
Yeah sorry for that. based on what you gave me earlier, I tried to implement what you gave me into mine, then I ended up figuring out how the camera is scrolling. However, on my end, the part where camera.x is being positioned to the Player.x , the player goes over the camera width and height which it’s not suppose to do, and does not center to the camera.Also, I did try to implement Camera.y into the player.y, tried that, it did work until I deleted the the camera.y part and doing it all over again. Overall, I’m trying to achieve a 2d camera that can scroll within the player’s location whether the player is moving up or down like in top down games.
I said this to chatgpt
In this code the blue rectangle moves to the right and goes past the camera view. Why?
I have 0 faith on it giving me the correct answer but the below is what it said. Usually if you explain what you’re trying to do (to a person) and explain how you implemented it in code, you’ll figure it out before you click the reply/send button. It’s called Rubber duck debugging
The issue with the blue rectangle (which seems to represent the player) moving past the camera view stems from how the player’s position is being managed relative to the camera’s position. Let’s break down the relevant parts:
- Player Position Update: In the code, you are updating the player’s position based on keyboard input:
- Camera Position Update: After updating the player position, you update the camera based on the player’s position:
- Boundary Conditions: You have checks to limit the player and camera positions:
Issues
- Player’s Right Boundary Check: The condition
if (Player.x > LEVEL_WIDTH)
allows the player to exceed the level width without proper constraint because the player can still move past the camera’s right side before it is limited. - Camera Adjustment: The camera position is directly set based on the player’s position without a proper clamp to keep the camera within the level boundaries. When the player moves beyond the width of the level, the camera can also attempt to follow, thus “moving past” the viewable area.
Suggested Fixes
- Adjust Player Boundary Check: Instead of checking if the player’s position exceeds
LEVEL_WIDTH
, check if it exceedsLEVEL_WIDTH - Player.w
: - Clamp Camera Position: Before you set the camera’s x position, ensure it stays within bounds:
With these adjustments, the player should be constrained within the level boundaries, and the camera will also remain within the valid range, preventing the blue rectangle from moving out of view.
Pretty much the suggested fixes with the camera clamping. I figured out how I can set boundaries on level width or height thanks to provided code before comparing to mine. but still trying to figure out the clamping part. If you don’t mind, how do I proceed with the clamp idea?
In my own code, I have a canMove bool and I set it to false when I’m too far in one direction or if there’s something I’m colliding with.
My gut says this is a common enough problem that gpt is trained on it. I’m not saying it’ll give you a good answer but you won’t need to wait for someone on a forum to see and write a comment to you
Right. I don’t want to steal someone’s code and said “I did this, all me” because if I did steal code, I be lying and didn’t learn anything
I don’t think your code is messy if one just ignores the unused parts.
rect
holds the position of the player on the screen. You forget to take the player’s position into account when updating the coordinates. I think it should be:
rect.x = Player.x - camera.x;
rect.y = Player.y - camera.y;
This is something you should do after you’ve finished updating the position of the player and the camera.
(To avoid that the camera lags behind the player by one frame, you also need to make sure to not update the camera coordinates until after you’re finished updating the player coordinates)
I also think that rect
and Player
should have the same size otherwise the player will be drawn halfway outside the screen when reaching the right side of the level.
rect.w = Player.w;
rect.h = Player.h;
After making these changes it seems to work fine. This is what the final code looks like:
#include <iostream>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#undef main
int LEVEL_WIDTH;
int LEVEL_HEIGHT;
int wWidth;
int wHeight;
float camSpeed;
float playerSpeed = 500;
double OldTime = 0.0;
double NewTime = 0.0;
double DeltaTime = 0.0;
double Lifetime = 0.0;
bool LHeld = false;
bool RHeld = false;
int main()
{
camSpeed = 50;
wWidth = 800;
wHeight = 600;
LEVEL_WIDTH = 2000;
LEVEL_HEIGHT = 1000;
SDL_Rect camera{ 0,0,wWidth,wHeight };
SDL_FPoint PlayerSize = { 40.0f, 100.0f };
SDL_FRect Player = { wWidth - PlayerSize.x, wHeight - PlayerSize.y, PlayerSize.x, PlayerSize.y };
Player.x = 1;
Player.y = 90;
if (SDL_Init(SDL_INIT_VIDEO))
{
printf("Error: %s", SDL_GetError());
return -1;
}
SDL_Window* window = SDL_CreateWindow("Spine SDL", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, wWidth, wHeight,
0);
if (!window) {
printf("Error: %s", SDL_GetError());
return -1;
}
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (!renderer) {
printf("Error: %s", SDL_GetError());
return -1;
}
SDL_Texture* texture = IMG_LoadTexture(renderer, "background.png");
SDL_Texture* sample = IMG_LoadTexture(renderer, "pep.png");
bool quit = false;
while (!quit)
{
NewTime = (double)SDL_GetTicks();
DeltaTime = (NewTime - OldTime) * 0.001;
OldTime = NewTime;
Lifetime += DeltaTime;
SDL_Rect rect;
rect.x = 0;
rect.y = 0;
rect.w = Player.w;
rect.h = Player.h;
SDL_Event event;
while (SDL_PollEvent(&event) != 0)
{
if (event.type == SDL_QUIT)
{
quit = true;
break;
}
}
const Uint8* KeyboardState = SDL_GetKeyboardState(nullptr);
LHeld = (KeyboardState[SDL_SCANCODE_LEFT] == SDL_TRUE);
RHeld = (KeyboardState[SDL_SCANCODE_RIGHT] == SDL_TRUE);
if (LHeld && !RHeld)
Player.x -= playerSpeed * (float)DeltaTime;
else if (RHeld && !LHeld)
Player.x += playerSpeed * (float)DeltaTime;
if (Player.x < 0.0f)
Player.x = 0.0f;
else if (Player.x > (LEVEL_WIDTH - Player.w))
Player.x = LEVEL_WIDTH - Player.w;
camera.x = (Player.x) - wWidth / 2;
if (camera.x < 0)
{
camera.x = 0;
}
if (camera.y < 0)
{
camera.y = 0;
}
if (camera.x > LEVEL_WIDTH - camera.w)
{
camera.x = LEVEL_WIDTH - camera.w;
}
if (camera.y > LEVEL_HEIGHT - camera.h)
{
camera.y = LEVEL_HEIGHT - camera.h;
}
SDL_SetRenderDrawColor(renderer, 94, 93, 96, 255);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, &camera, NULL);
rect.x = Player.x - camera.x;
rect.y = Player.y - camera.y;
SDL_RenderCopy(renderer, sample, NULL, &rect);
SDL_RenderPresent(renderer);
}
SDL_DestroyWindow(window);
SDL_DestroyRenderer(renderer);
SDL_DestroyTexture(texture);
texture = nullptr;
SDL_Quit();
return 0;
}
Appreciate the code! I figured out the problem on why the player(aka, the blue box) was moving ahead of the camera. Turns out it was my rendercopy code. By attaching drawingRect2 to the SDL_RenderCopy where it holds the “sample” picture from above, everything worked with snap of a finger! Ill try your code since you went through the process already!!
I recommend subtracting the camera’s x from the object’s x to form the x coordinate of the destination rect to draw the sprite.