0
\$\begingroup\$

I am making a top down 2D shooter using SDL2 for fun and I ran into a problem. I have a whole list of enemies each with their position on the screen stored in an array. Now you only see a portion of the map on your screen, because the whole map is too large. I want to have the player be able to move around the map, in which case I have to update the position of all the enemies. The same goes for projectiles and buildings. So I would have to loop over each of these every time the screen moves. I thought that a smarter way may be possible where you keep track of the entire map for each frame and when you render it you zoom in on only the part you need. That way you only have to keep track of one position vector, for instance the point on the map at which you want your screen to be centered. How is this problem normally tackled?

I realize that both methods will take some time, but seeing as the map is not THAT large, maybe 6x the screen, I thought it might be worthwhile. The problem is then that I am rendering the textures using SDL_RenderCopyEx and then displaying them with SDL_RenderPresent, and I don't think you can zoom in like that.

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

Create a camera class that holds the position of the camera, this could update to track your player ship.

In the render logic, for each entity you can do 2 things: 1. calculate the position of the entity related to the camera 2. if the entity is not in view, don't render.

So you don't update each entity with your camera position- just keep everything as it is in "world coordinates". You just change the render call in your entity class with the camera in mind (and add the benefit of culling entities that are outside the view). So you convert your "world coordinates" to "screen coordinates": screen.position = obj.position - camera.position

This is way better performance wise than drawing everything to a huge texture.

Added benefits is that you could add things like camera shake, briefly cue to a boss etc. to the camera class without affecting the rest of your game logic.

\$\endgroup\$
0
\$\begingroup\$

To solve this, you could create a texture the size of your entire map. SDL2 can then render to a texture instead of rendering directly to the screen. Store every object's position (player, enemies and whatnot) in absolute terms (from the top-left corner of the map), and render everything. Once this is done, you can then render a portion of the map texture to the screen, using SDL_RenderCopy[Ex]:

// at the beginning of the game:
SDL_Texture* world = SDL_CreateTexture(
    renderer,
    PIXEL_FORMAT,
    SDL_TEXTUREACCESS_TARGET, // the most important, allows you to render to this texture.
    worldWidth,
    worldHeight);
SDL_Rect camera = {0, 0, cameraWidth, cameraHeight}; // change x and y to center the camera wherever you'd like it to be

//
// in your game loop
//
SDL_SetRenderTarget(renderer, world); // render everything to the world texture

// render your enemies, player and all
SDL_RenderCopy(renderer, player, NULL, player_rect); // player_rect should hold the position on the map

SDL_SetRenderTarget(render, NULL); // switch back the renderer so that it targets the screen
SDL_RenderCopy(renderer, world, camera, NULL); // this will render only the portion of world that is bound by the camera rectangle.
SDL_RenderPresent(renderer);

this way, you don't have to offset the position of everything depending on where you camera is. You just render as if the whole map was displayed, and then copy a tiny portion of this.

The only problem with that method is that you will render objects that, in the end, are outside the camera and will not be displayed. to solve that, you can check if an object's Rect lies in the camera rect with SDL_HasIntersection(&camera, &object_rect);. If not, just by pass SDL_RenderCopy for that object.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .