SDL C Program freezes on sdl_blitsurface - sdl

I'm having an issue with a program I'm working on. Occasionally, it will just freeze. No errors or anything.
The game is a multiplayer game where you fly a ship around. Pictures of other players and powerups move in and out of view depending on your location. For the most part, it works great, but under certain circumstances, it locks up.
I've tracked it down to when it BLITs one surface onto another. (SDL_BlitSurface).
If I comment out the single line of code where it blits (SDL_BlitSurface), and replace the graphic with a simple circle, it'll never freeze under any circumstances. But, comment out the circle and replace it with blitting the graphic again, and it'll randomly freeze. The frustrating part is, sometimes it will, sometimes it won't. Sometimes the graphic will sit on screen for a few moments and then freeze, sometimes it'll freeze the moment it shows up. Sometimes, it won't freeze at all. I simply cannot track it down to anything in particular.
I have ample amount of code that checks for NULL surfaces and it doesn't seem to stop it.
I also have it set up to output information about all the graphics to a file (such as width, height, location in memory, x, y, etc) and nothing seems out of the ordinary.
My main questions are, what about surfaces can cause SDL_BlitSurface to freeze? And what other checks can I add for surfaces to make sure it doesn't try to blit bad surfaces?
The code is too long to list, but here is how it works:
class Player
{
Player();
int x;
int y;
int xvel;
int yvel;
SDL_Surface *DrawScreen;
SDL_Surface *ShipPic;
void check_player_dist();
void check_powerup_dist();
void update();
};
class PowerUp
{
int x;
int y;
int type;
SDL_Surface *Powerup_Pic;
};
Player::Player()
{
Apply_Surface(0, 0, PlayerShipPics, ShipPic);
}
Player::Update(Player p[], PowerUp pu[])
{
x += xvel;
y += yvel;
for (int i = 0; i < Num_Players; i++)
{
if (check_on_screen(p[i].x, p[i].y) == true)
{
Apply_Surface(x - p[i].x, y - p[i].y, p[i].ShipPic, DrawScreen);
}
}
for (int i = 0; i < Num_PowerUps; i++)
{
if (check_on_screen(pu[i].x, pu[i].y) == true)
{
Apply_Surface(x - pu[i].x, y - pu[i].y, pu[i].Pic, DrawScreen);
}
}
}
int main()
{
SDL_Surface *Screen;
Player players[4];
PowerUp powerups[200];
Num_Players = 4;
Num_PowerUps = 200;
while (quit == false)
{
for (int i = 0; i < Num_Players; i++)
{
players[i].update(players, powerups);
switch (i)
{
case 0: ScreenX = 0; ScreenY = 0; break;
case 1: ScreenX = ScreenWid / 2; ScreenY = 0; break;
case 2: ScreenX = 0; ScreenY = ScreenHigh / 2; break;
case 3: ScreenX = ScreenWid / 2; ScreenY = ScreenHigh / 2; break;
}
Apply_Surface (ScreenX, ScreenY, players[i].DrawScreen, Screen);
}
if (SDL_Flip(Screen) == -1)
{
return -1;
}
}
}
void Apply_Surface (int x, int y, SDL_Surface* Source, SDL_Surface* Destination, SDL_Rect* Clip)
{
SDL_Rect Offset;
Offset.x = x;
Offset.y = y;
if ((Source != NULL) && (Destination != NULL))
{
SDL_BlitSurface (Source, Clip, Destination, &Offset );
}
}
I've noticed it generally freezes when two or more players are near each other and it tries to draw the same power-up on both of their screens. But again...not always!

Well, I figured out what it was.
I was using the SDL_GFX library along with my game. Many of the images were created using rotozoomSurface(), which is a function of SDL_GFX.
Turns out there's a bug in it where, under certain circumstances that I don't know, it'll create a bad surface that will work "most" of the time, but under the right conditions, will crash. Such as, being placed at a particular x & y coordinate on the screen. (Don't know for sure). The rotated/zoomed images would work about 95% of the time, so it was very difficult to pin point what the issue was.
The work around was, when the image was created, just SDL_BlitSurface() it onto another surface under controlled conditions, such as putting it at coordinates (0, 0). Then, delete the rotated and zoomed surface, and just use the new "safe" surface.
Works great after that.
Hopefully this will help anyone who's using SDL_GFX and cannot figure out why their program is crashing.
Example:
Before:
SDL_Surface *original = SDL_CreateRGBSurface(SDL_SWSURFACE, Ship_Width, Ship_Height, Screen_BPP, 0, 0, 0, 0);
Apply_Surface(0, 0, ShipsPic, original, &bounds);
SDL_Surface *finished = rotozoomSurface(original, pic_angle, zoom, SMOOTHING_ON);
SDL_FreeSurface(original);
return finished;
After (fixed):
SDL_Surface *original = SDL_CreateRGBSurface(SDL_SWSURFACE, Ship_Width, Ship_Height, Screen_BPP, 0, 0, 0, 0);
Apply_Surface(0, 0, ShipsPic, original, &bounds);
SDL_Surface *temp = rotozoomSurface(original, pic_angle, zoom, SMOOTHING_ON);
SDL_Surface *finished = SDL_CreateRGBSurface(SDL_SWSURFACE, temp->w, temp->h, Screen_BPP, 0, 0, 0, 0);
Apply_Surface(0, 0, temp, finished);
SDL_FreeSurface(temp);
SDL_FreeSurface(original);
return finished;
And for what it's worth, the Apply_Surface() function:
void Apply_Surface (int x, int y, SDL_Surface* Source, SDL_Surface* Destination, SDL_Rect* Clip)
{
SDL_Rect Offset;
Offset.x = x;
Offset.y = y;
if ((Source != NULL) && (Destination != NULL))
{
SDL_BlitSurface (Source, Clip, Destination, &Offset );
}
}

There's not really enough information to figure out what exactly is going on. Computers don't like to do things "sometimes," they either do them or not, so it leads me to believe that maybe there's some variable that's doing something it shouldn't.
Just in case, what does your Apply_Surface() function look like? I assume that's where you're doing your actual blitting, and if that's where you're having your problems, that would be useful for those of us trying to figure out your dilemma.

Related

How do I resize a texture in SDL?

I have this function:
void Texture::render(int x, int y, int w, int h, SDL_Renderer *&renderer, double angle, SDL_Point* center, SDL_RendererFlip flip)
{
// Set a destination value to -1 to keep the current value
if (x < 0) { x = rect.x; }
if (y < 0) { y = rect.y; }
if (w < 0) { w = rect.w; }
if (h < 0) { h = rect.h; }
// Create destination rectangle
SDL_Rect dstRect = { x, y, w, h };
// Render to screen
SDL_RenderCopyEx(renderer, texture, &rect, &dstRect, angle, center, flip);
}
It works. It creates an image of the correct size at the location I want. But I want to add a chunk of code where it resizes the texture itself to be the size given in the destRect.
So, anyone who finds this and reads the conversation I had with Nelfeal in the comments will see that I had a misunderstanding of how SDL_RenderCopyEx works. There's no need to resize the texture. If you need to do something like that, you can just use the dstRect when you copy it.
Actually, as far as I can find, there isn't a method to resize the actual texture itself. I'm sure one exists, but it's definitely not something people are commonly using. Which is usually a sign that it's a bad idea.
I've tweaked my code to try and simplify it, for anybody who's trying to do something similar to me:
void render(SDL_Renderer *&renderer, SDL_Rect *dstRect=NULL, SDL_Rect &srcRect=NULL, double angle=0.0, SDL_Point* center=NULL, SDL_RendererFlip flip=SDL_FLIP_NONE);
void Texture::render(SDL_Renderer *&renderer, SDL_Rect *dstRect, SDL_Rect *srcRect, double angle, SDL_Point* center, SDL_RendererFlip flip)
{
// Check to see if a destination was provided
bool check = false;
if (dstRect == NULL)
{
check = true;
dstRect = new SDL_Rect();
dstRect->x = 0;
dstRect->y = 0;
dstRect->w = SCREEN_WIDTH;
dstRect->h = SCREEN_HEIGHT;
}
// Check to see if the entire texture is being copied
if (srcRect == NULL) { srcRect = &rect; }
// Render to screen
SDL_RenderCopyEx(renderer, texture, srcRect, dstRect, angle, center, flip);
// Free dstRect
if (check) delete dstRect;}
And it looks like this when using the function:
bgTex.render(renderer);
blobTex.render(renderer, &blobDstRect);

While loop causing lag for SDL_Rect and SDL_Textures?

I'm making a game using C++ and SDL, the game is a Space Invaders type of game. It's all been going smoothly until I added a background image in the while loop, this is the render and init code for the background:
SZ_Background.cpp:
void SZ_Background::Init(SDL_Renderer* pRenderer)
{
int w, h;
SDL_GetRendererOutputSize(pRenderer, &w, &h);
bg_img.x = 0;
bg_img.y = 0;
bg_img.h = h;
bg_img.w = w;
SDL_Surface* background_img = IMG_Load("content/bg_img.bmp");
SDL_Texture* background_img_tex = SDL_CreateTextureFromSurface(pRenderer, background_img);
SDL_RenderCopy(pRenderer, background_img_tex, NULL, &bg_img);
}
void SZ_Background::Render(SDL_Renderer* pRenderer)
{
int w, h;
SDL_GetRendererOutputSize(pRenderer, &w, &h);
bg_img.x = 0;
bg_img.y = 0;
bg_img.h = h;
bg_img.w = w;
SDL_Surface* background_img = IMG_Load("content/bg_img.bmp");
SDL_Texture* background_img_tex = SDL_CreateTextureFromSurface(pRenderer, background_img);
SDL_RenderCopy(pRenderer, background_img_tex, NULL, &bg_img);
}
main.cpp - The loop:
if (GameState == 3)
{
printf("INFO: Game State: %d - Game Screen Loaded\n", GameState);
mainEnemies.gameover = false;
mainBG.Init(game_renderer);
gOver.Init(game_renderer);
while (!done)
{
aTimer.resetTicksTimer();
SDL_SetRenderDrawColor(game_renderer, 0, 0, 20, SDL_ALPHA_OPAQUE);
SDL_RenderClear(game_renderer);
mainBG.Render(game_renderer);
gOver.Update(game_renderer);
gOver.Input();
gOver.Render(game_renderer);
SDL_RenderPresent(game_renderer);
if (gOver.rMenu == true)
{
SDL_RenderClear(game_renderer);
SDL_RenderPresent(game_renderer);
GameState = 0;
break;
}
if (aTimer.getTicks() < DELTA_TIME)
{
SDL_Delay(DELTA_TIME - aTimer.getTicks());
}
}
}
mainBG is the background.
The issue is that you're initializing background_img and background_img_tex every time you call SZ_Background::Render:
SDL_Surface* background_img = IMG_Load("content/bg_img.bmp");
SDL_Texture* background_img_tex = SDL_CreateTextureFromSurface(pRenderer,background_img);
That's not necessary, you already initialize them in SZ_Background::Init, and that's all you need to do. The way it is now, not only might it slow down the program by loading that background from disk every frame, but it's also leaking memory every time (mostly your RAM for SDL_Surface, and your GPU's memory for SDL_Texture). Remove those IMG_Load and SDL_CreateTextureFromSurface calls in Render and it should be better.

SDL Pong Movement Bug

The problem with my code is that I am making a pong game in SDL 2.0 in c++. I did everything until creating the movement. When the player paddle moves, it leaves behind a trail in the same color as the paddle. I watched some videos on YouTube, but when they do the movement it's nice and clear and for me to fix this but I need to recolor the background every time the player moves, which makes it being all flashy and if I hold the button I don't see the paddle at all.
#include<iostream>
#include<SDL2/SDL.h>
#include<SDL2/SDL_image.h>
#include<windows.h>
#define width 800
#define height 600
using namespace std;
bool run = true;
class Player{
private:
SDL_Window* window = SDL_CreateWindow("Pong!", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_RESIZABLE);
SDL_Surface* Screen = SDL_GetWindowSurface(window);
Uint32 screen_color = SDL_MapRGB(Screen->format, 0, 0, 0);
Uint32 In_game_RGB = SDL_MapRGB(Screen->format, 255, 255, 255);
SDL_Rect Pl;
SDL_Rect AI;
SDL_Rect Ball;
SDL_Rect ClearP;
SDL_Rect ClearAI;
public:
Player(){
//Player parameters
Pl.x = 60;Pl.y = 225;Pl.w = 25;Pl.h = 200;
//AI parameters
AI.x = 720;AI.y = 225;AI.w = 25;AI.h = 200;
//Ball parameters
Ball.x = width/2;Ball.y = height/2+10;Ball.w = 25;Ball.h = 25;
//Recoloring parameters
ClearP.x = 0;ClearP.y = 0; ClearP.w = 375;ClearP.h = height;
ClearAI.x = 425;ClearAI.y = 0;ClearAI.w = 375;ClearAI.h = height;
//Make the screen color black
SDL_FillRect(Screen, NULL, screen_color);
}
void scrUpdate(){
SDL_UpdateWindowSurface(window);
}
void drawPlayer(){
SDL_FillRect(Screen, &Pl, In_game_RGB);
}
void drawComputer(){
SDL_FillRect(Screen, &AI, In_game_RGB);
}
void ball(){
SDL_FillRect(Screen, &Ball, In_game_RGB);
}
void Movement(){
if(GetAsyncKeyState(VK_DOWN)){
Pl.y += 2;
SDL_FillRect(Screen,&ClearP,screen_color);
}
if(GetAsyncKeyState(VK_UP)){
SDL_FillRect(Screen,&ClearP,screen_color);
Pl.y -= 2;
}
}
};
void EventCheck(){
SDL_Event event;
if(SDL_PollEvent(&event)){
if(event.type == SDL_QUIT){
run = false;
}
}
}
int main( int argc, char *argv[] )
{
SDL_Init(SDL_INIT_EVERYTHING);
Player Play;
//Player Computer();
while(run){
Play.scrUpdate();
Play.drawPlayer();
Play.drawComputer();
Play.ball();
Play.Movement();
EventCheck();
}
SDL_Quit();
return EXIT_SUCCESS;
}
It would help to show some code or an example of what you have been doing, or a link to one of the videos you have been watching:
Youtube tutorial
but I suggest taking a look at:
screen = SDL_SetVideoMode(800, 600, 32, SDL_HWSURFACE); and SDL_Flip(screen) as those have to do with screen buffering and drawing.
Another possibilty is that you are running an outdated version of SDL, or an incompatible one with your current system.
To be able to give a more complete and proper answer, I'd highly suggest adding more information about your code, screenshots of results and your version of SDL and operating system.
Also, you said it was flashy when you hold the paddle. I think it must be that you are performing your logic to move the paddle and you redraw the paddle once it's still. If you are redrawing the entire screen constantly, consider double buffering.

SDL2 double buffer not working, still tearing

I need a double buffer because i'm starting to notice tearing when I want to move my texture made tile map around the screen via mouse click.
I'm using SDL2 and this is a SDL2 specific question, check out my code that produces tearing, whats wrong?
//set up buffer and display
bufferTexture = SDL_CreateTexture(renderer,SDL_PIXELFORMAT_RGBA8888,SDL_TEXTUREACCESS_STREAMING,800,600);
displayTexture = SDL_CreateTexture(renderer,SDL_PIXELFORMAT_RGBA8888,SDL_TEXTUREACCESS_TARGET,800,600);
while(running)
{
handleEvents();
SDL_SetRenderTarget(renderer,bufferTexture); // everything goes into this buffer
SDL_RenderClear(renderer); // clear buffer before draw
gTileMovement.updateMapCoordinates();
for(int i = 0; i < MAP_ROWS; i++)//rows
{
for(int j = 0; j < MAP_COLUMNS; j++)//columns
{
x = (j * 100) - (i * 100);
y = ((i * 100) + (j * 100)) / 2;
drawTiles(i,j,x,y);
}
}
//move from buffer to display texture
memcpy(&displayTexture,&bufferTexture,sizeof((&bufferTexture)+1));
//change render target back to display texture
SDL_SetRenderTarget(renderer,displayTexture);
//show it all on screen
SDL_RenderPresent(renderer);
}
for all it matters here is my drawTiles function too, is this not conventional? :
void drawTiles(int i,int j,int x,int y)
{
//updates based on a mouse clicks xy coords
gTileMovement.updateMapCoordinates();
if(tileMap[i][j] == 1) // grass?
{
gSpriteSheetTexture.render(x+gTileMovement.getUpdatedX(),y+gTileMovement.getUpdatedY(),&gSpriteClips[1]);
}
if(tileMap[i][j] == 0) // wall?
{
gSpriteSheetTexture.render(x+gTileMovement.getUpdatedX(),y+gTileMovement.getUpdatedY(),&gSpriteClips[0]);
}
if(tileMap[i][j] == 2) // tree?
{
gSpriteSheetTexture.render(x+gTileMovement.getUpdatedX(),y+gTileMovement.getUpdatedY(),&gSpriteClips[2]);
}
}
Which followes into how I SDL_RenderCopy the tiles through a class. This copies the textures onto the current targeted renderer does it not? Which is the buffer texture if i'm not mistaken.
void LTexture::render(int x, int y, SDL_Rect * clip)
{
SDL_Rect renderQuad = {x, y, mWidth, mHeight};
if(clip != NULL)
{
renderQuad.w = clip->w;
renderQuad.h = clip->h;
}
SDL_RenderCopy(renderer, mTexture, clip, &renderQuad);
}

Displaying a moving object in sdl

I have a problem with my SDL program. My goal is to make a dot move along a line. I have all the coordinates saved in a data file. So I just wanted to read them from the file and display the dot at the right position.
The dot class (which is named linefollower) looks like this.
class Linefollower
{
private:
int x, y;
char orientation;
public:
//Initializes the variables
Linefollower();
void set(int m_x, int m_y, char m_orietnation);
void show();
char get_orientation();
};
Linefollower::Linefollower()
{
x = 0;
y = 0;
orientation = 'E';
}
void Linefollower::set(int m_x, int m_y, char m_orientation)
{
x = m_x;
y = m_y;
orientation = m_orientation;
}
void Linefollower::show()
{
//Show the linefollower
apply_surface(x, y, linefollower, screen );
}
char Linefollower::get_orientation()
{
return orientation;
}
The apply_surface function.
void apply_surface( int x, int y, SDL_Surface * source, SDL_Surface* destination)
{
//Temporary rectangle to hold the offsets
SDL_Rect offset;
//Get the offsets
offset.x = x;
offset.y = y;
//Blit the surface
SDL_BlitSurface( source, NULL, destination, &offset);
}
The loop which ought to display the animation looks like this.
//While the user hasn't quit
while( quit == false )
{
//Apply the surface to the screen
apply_surface( 0, 0, image, screen );
fin.read((char*) &my_linefollower, sizeof my_linefollower);
if(my_linefollower.get_orientation() == 'Q')
break;
my_linefollower.show();
//Upadate the screen
if( SDL_Flip( screen ) == -1 )
{
return 1;
}
SDL_Delay(200);
}
Now I was expecting, that I get a moving dot on the screen, but the only thing which I got is the background (image) for a few seconds untill the if(my_linefollower.get_orientation() == 'Q')
break; was true. What do I do wrong?
PS: I guess it is worth noticing that I am a beginner in SDL and I took most of the code from a tutorial. Learning it exactly would be a waste of time for me, since it is unlikely that I am going to use it again any time soon.
First, you should change your offset in apply_surface to something like this:
SDL_Rect offset = { x, y, 0, 0 };
SDL_Rect doesn't have a constructor to set your members to 0 by default, so you get uninitialized memory for your width and height.
Also, you should check what linefollower contains, if it's a valid SDL_Surface. Removing your file reading code and manually controlling a Linefollower will allow you to easily find where the error comes from.
Use a debugger to validate your x and y coordinates.
Other than that, you code should work, although your window will be unresponsive because you're not pumping events through SDL_PollEvent.