Related
I am creating a program, and I have a rectangle. Basically, I am creating a custom window inside the SDL2 window, which is a Rect, with another Rect being its toolbar. I am struggling for hours trying to figure how to detect how deep is the mouse within the Rect, but I am failing every time. The most obvious equation for me was int deep = mousePos.x - x, but it flickers between two positions every time I move my mouse. I then have tried a LOT of other calculations, but none of them worked. Either they flickered between two positions with descending values, or they were completely static and didn't move, or always moved a lot in a specific direction. I have visually represented the calculations, which were mostly correct, but the flickering between two positions is always ruining it. Thanks for any help. I am providing source code, too.
SOURCE:
//
// main.cpp
// Open
//
// Created by Fildom on 28.12.2021.
//
// Library includes
#include <SDL2/SDL.h>
#include <stdio.h>
bool isdown = false;
// Screen rendering helper
void on_render(SDL_Window* window, SDL_Renderer* renderer);
// Concatenation (probably not spelt correctly but idrc) for easier use
const char * concat(const char * one, const char * two) {
char * buffer = new char[strlen(one) + strlen(two) + 1];
strcpy(buffer, one);
strcat(buffer, two);
return buffer;
}
// Main method, required for performing application run
int main(int argc, const char * argv[]) {
SDL_Renderer *renderer = NULL; // Initialize the renderer
SDL_Event event = { 0 }; // Create a null event
SDL_Window *win = NULL; // Initialize a window
int exit = 0; // If exit is 1, win closes
// Window pre-modifiers
const char * appName = "test";
// SDL VIDEO mode initialization and error check
if(SDL_Init(SDL_INIT_VIDEO) == -1) {
printf("SDL_Init() failed with \"%s.\"", SDL_GetError());
return 1;
}
// Create the window and load it into a previously defined variable
win = SDL_CreateWindow(concat(appName, " - Initialization in progress"), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_SHOWN);
// Window creation was unsuccessfull
if(!win) {
printf("SDL_CreateWindow() failed with \"%s.\"", SDL_GetError());
return -1;
}
// Creating renderer
renderer = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED);
// If renderer failed to load...
if(!renderer) {
printf("SDL_CreateRenderer() failed with \"%s.\"", SDL_GetError());
return -1;
}
// Everything has gone OK, thus the window can be renamed
SDL_SetWindowTitle(win, appName);
// Game loop, as said previously, false = 0, true = 1.
// while !exit |
// while not exit <- |
// while exit is 0 (false) <-
while (!exit) {
// Event loop
if (SDL_WaitEvent(&event)) {
// Event types
switch(event.type) {
case SDL_QUIT:
exit = 1; // Exit = 1, thus app is being exitted
break;
case SDL_KEYDOWN:
if(event.key.keysym.sym == SDLK_ESCAPE) exit = 1; // If ESC is pressed
break;
case SDL_MOUSEBUTTONUP:
isdown = false;
break;
case SDL_MOUSEBUTTONDOWN:
isdown = true;
break;
case SDL_MOUSEMOTION:
break;
case SDL_WINDOWEVENT:
switch(event.window.event) {
case SDL_WINDOWEVENT_CLOSE: // macOS and/or other OSes rely on right click + Quit to fully exit out of an application. This makes it easier by just hitting the close button.
exit = 1;
break;
}
break;
default: break;
}
}
// Render the screen
on_render(win, renderer);
// Swap buffers to display
SDL_RenderPresent(renderer);
}
// Cleanup
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(win);
SDL_Quit();
return 0;
}
class Window {
public:
int x, y, w, h;
SDL_Color winc, wintc;
bool draggable;
int titleh;
Window(int wx, int wy, int ww, int wh, SDL_Color window_color = {255, 255, 255, 255}, SDL_Color window_title_color = {200, 200, 200, 255}) {
x = wx;
y = wy;
w = ww;
h = wh;
winc = window_color;
wintc = window_title_color;
draggable = true;
titleh = 50;
}
int tx, ty = 0;
void Render(SDL_Renderer* renderer) {
SDL_Rect _t;
_t.x = x;
_t.y = y;
_t.w = w;
_t.h = h;
SDL_Rect title;
title.x = x;
title.y = y;
title.w = w;
title.h = titleh;
SDL_SetRenderDrawColor(renderer, winc.r, winc.g, winc.b, winc.a);
SDL_RenderFillRect(renderer, &_t);
SDL_SetRenderDrawColor(renderer, wintc.r, wintc.g, wintc.b, wintc.a);
SDL_RenderFillRect(renderer, &title);
int mx, my;
SDL_PumpEvents();
SDL_GetMouseState(&mx, &my);
SDL_Point ms;
ms.x = mx;
ms.y = my;
if (SDL_PointInRect(&ms, &title) and isdown) {
x = mx - tx;
y = my - ty;
tx = x;
ty = y;
}
}
};
Window test1 = Window(200, 100, 300, 200);
void on_render(SDL_Window* window, SDL_Renderer* renderer) {
SDL_Rect wind = { 0 };
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
SDL_GetWindowSize(window, &wind.w, &wind.h);
test1.Render(renderer);
}
I ended up doing it in a different way. Instead of using mousePosition.x and y, I used relative X and Y which worked out perfectly.
code for that is
mousePosition.relX and mousePosition.relY;
I'm aware that there are several similar questions to this one already, but I've read through them and none of them seem to cover my specific problem...
I'm trying to dereference a public pointer from a struct, but it's throwing a SIGSEV Segmentation fault, even though the pointer does point to a value
It prints 1 & 2 but does not get any further. If I change it to reference the pointer rather than the value, the error moves to wherever I reference the value, so I know the issue is with dereferencing, not the rest of the line.
If you need to see any more of my code in order to find the issue, just ask. I just didn't want to make you trawl through hundreds of lines unnecesarily...
#include <vector>
#include "Entities.cpp"
struct Chunk {
public:
std::vector<BaseEntity>* environment; // this is what I'm trying to access
Chunk() {
environment = new std::vector<BaseEntity>(1); // this is where init
environment->push_back(Grass()); // adding something to it works fine
}
~Chunk() {
delete(environment);
}
};
class World {
public:
Chunk* loaded[3][3];
World() {
for (int x = 0; x < 3; x++)
for (int y = 0; y < 3; y++) {
loaded[x][y] = new Chunk();
}
}
~World() {
for (int x = 0; x < 3; x++)
for (int y = 0; y < 3; y++) {
delete(loaded[x][y]);
}
}
};
And here's the code that accesses it (in another class & file)
void render(SDL_Renderer* gRenderer, Atlas* atlas, World* world, int SCREEN_WIDTH, int SCREEN_HEIGHT) {
printf("1");
for (int x = 0; x < 3; x++)
for (int y = 0; y < 3; y++) {
printf("2");
std::vector<BaseEntity> env =
*(world->loaded[x][y]->environment); // here is where the error occurs
printf("3");
printf('0' + env.size() + "");
printf("a");
for (const auto& a : env) {
printf("b");
drawTexture(gRenderer, atlas, a.atlasID,
SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2);
}
printf("4");
}
printf("5");
}
This is the code that calls it:
int main(int argv, char** args) {
SDL_Init( SDL_INIT_VIDEO );
//The window we'll be rendering to
SDL_Window* gWindow = SDL_CreateWindow(
title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );
if (gWindow == NULL) {
printf("window error");
}
//Create renderer for window
SDL_Renderer* gRenderer = SDL_CreateRenderer( gWindow, -1, SDL_RENDERER_ACCELERATED );
if (gRenderer == NULL) {
printf("renderer error");
}
bool quit = false; // quit flag
//Event handler
SDL_Event e;
//nice little rendering class
Renderer* renderer = new Renderer();
//World
World* world = new World();
//Texture Atlas
Atlas* atlas = new Atlas(SDL_GetWindowSurface( gWindow), gRenderer);
//While application is running
while( !quit )
{
//Handle events on queue
while( SDL_PollEvent( &e ) != 0 )
{
switch(e.type) {
case SDL_QUIT: //User requests quit
quit = true;
break;
}
}
//Clear screen
//SDL_SetRenderDrawColor( gRenderer, 0, 0xFF, 0, 0xFF );
SDL_RenderClear( gRenderer );
renderer->render(gRenderer, atlas, world, SCREEN_WIDTH, SCREEN_HEIGHT);
//Update the surface
SDL_RenderPresent(gRenderer);
//SDL_UpdateWindowSurface( gWindow );
}
delete (world, renderer, atlas);
return 0;
}
This allocates a std::vector with one element of BaseEntity
environment = new std::vector<BaseEntity>(1); // this is where init
then another element of Grass is appended
environment->push_back(Grass()); // adding something to it works fine
but maybe this is the first problem, since the vector only holds BaseEntitys, see What is object slicing?
This accesses the first element of the given vector, and this is not the appended Grass but the default constructed BaseEntity object from new std::vector<BaseEntity>(1)
std::vector<BaseEntity> env = *(world->loaded[x][y]->environment); // here is where the error occurs
Accessing the pointer shouldn't be a problem, because it seems to be initialized properly.
But maybe copying the BaseEntity is the real problem here. I would look into the (copy) constructor of BaseEntity (and maybe Grass) and see, if there is some problematic code.
I don't see an immediate problem with your code (except copying the vector is obviously inefficient), but I do see a problem with your printf debugging.
You should use fprintf(stderr, ...) for debug printfs, so as to avoid stdio buffering, or call fflush(NULL).
The fact that 3 is not printed does not necessarily mean that printf("3") is unreached.
You should avoid printf debugging altogether, and learn to use a real debugger (GDB or LLDB) instead.
If you run your program with Address Sanitizer (-fsanitize=address); it will likely point you straight at the bug.
Finally, when writing in C++, avoid C-style arrays, and especially multi-level arrays. Use vectors instead.
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.
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.
(See "Edit 2" below for the solution.)
I need to create SDL surfaces from scratch, instead of loading them from a file. Unfortunately, SDL_BlitSurface() seems to render all colors as black when used with the surface generated through SDL_CreateRGBSurface(). This is my code:
int main(int argc, char** argv)
{
SDL_Surface* screen = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE);
SDL_Surface* layer = SDL_CreateRGBSurface(SDL_HWSURFACE, 100, 100,
screen->format->BitsPerPixel,
screen->format->Rmask,
screen->format->Gmask,
screen->format->Bmask,
screen->format->Amask
);
SDL_Rect rect;
rect.x = 0;
rect.y = 0;
rect.w = 100;
rect.h = 100;
Uint32 blue = SDL_MapRGB(screen->format, 0, 0, 255);
SDL_FillRect(layer, &rect, blue);
SDL_BlitSurface(screen, NULL, layer, NULL);
SDL_Flip(screen);
SDL_Delay(3000);
return 0;
}
What I get is a black screen, instead of a 100x100 blue rectangle. What I could find by Googling doesn't seem to help me, as those questions either apply to 8bit surfaces (and setting palettes — my bpp is 32 here) or are left unanswered.
So, I would like to know how should I properly blit a generated surface onto a SDL screen.
Edit: I see it was an error in the parameter ordering. The line in question should read
SDL_BlitSurface(layer, NULL, screen, NULL);
Still, I am having trouble to achieve the same effect in my more complex C++ program. I will post the relevant parts of the code here:
main.cpp:
int main(int argc, char** argv)
{
SDLScreen screen(1024, 700, "Hello, SDL!");
SDL_Event event;
SDLMenu menu;
bool shouldQuit = false;
menu.setBounds(200, 100, 200, 600);
menu.setFontName("NK211.otf");
menu.setFontSize(36);
menu.setEffect(sdlteShadowText);
menu.addItem("New game");
menu.addItem("Load game");
menu.addItem("Save game");
menu.addItem("Exit");
menu.render();
while (!shouldQuit)
{
menu.draw(screen.getSurface());
SDL_Flip(screen.getSurface());
SDL_Delay(10);
while (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT)
{
shouldQuit = true;
}
else if (event.type == SDL_KEYUP)
{
if (event.key.keysym.sym == SDLK_q)
{
shouldQuit = true;
}
}
}
}
}
SDLMenu.cpp:
void
SDLMenu::setSelectionColorRGB(int r, int g, int b)
{
SDL_VideoInfo* info = (SDL_VideoInfo*)SDL_GetVideoInfo();
selectionColor = SDL_MapRGB(info->vfmt, r, g, b);
}
void
SDLMenu::render()
{
SDLText* current = NULL;
SDL_VideoInfo* info = (SDL_VideoInfo*)SDL_GetVideoInfo();
if (!items->empty())
{
current = getItemAt(currentItem);
selectionRect = getItemRect(current);
setSelectionColorRGB(0,0,255);
selectionCanvas = SDL_CreateRGBSurface(SDL_HWSURFACE,
selectionRect->w, selectionRect->h,
info->vfmt->BitsPerPixel,
info->vfmt->Rmask,
info->vfmt->Gmask,
info->vfmt->Bmask,
info->vfmt->Amask);
SDL_FillRect(selectionCanvas, selectionRect, selectionColor);
SDL_SaveBMP(selectionCanvas, "selection.bmp"); // debug
}
for (list<SDLText*>::iterator i = items->begin();
i != items->end(); i++)
{
(*i)->render();
}
}
void
SDLMenu::draw(SDL_Surface* canvas)
{
int currentY = bounds.y;
if (selectionCanvas != NULL)
{
SDL_BlitSurface(selectionCanvas, NULL, canvas, selectionRect);
}
for (list<SDLText*>::iterator i = items->begin();
i != items->end(); i++)
{
(*i)->draw(bounds.x, currentY, canvas);
currentY += fontSize + itemGap;
}
}
SDLScreen.cpp:
SDLScreen::SDLScreen(int w, int h, string t, int d)
: width(w), height(h), depth(d), title(t)
{
SDL_Init(SDL_INIT_EVERYTHING);
SDL_WM_SetCaption(title.c_str(), NULL);
refresh();
}
void
SDLScreen::refresh()
{
screen = SDL_SetVideoMode(width, height, 32, SDL_HWSURFACE);
}
The selection rectangle for the active menu item should be blue, but it shows up in black. The file selection.bmp is also all black.
Edit 2: I found out what created the problem. The selectionRect was set relative to the screen, while the selectionCanvas had the width and height of a particular menu item. So, the filling was done out of bounds of the selectionCanvas. Adding separate SDL_Rect for filling solved the problem.
SDL_Rect fillRect;
fillRect.x = 0;
fillRect.y = 0;
fillRect.w = selectionRect->w;
fillRect.h = selectionRect->h;
SDL_FillRect(selectionCanvas, &fillRect, selectionColor);
// and later...
SDL_BlitSurface(selectionCanvas, NULL, canvas, selectionRect);
You inverted source and destination. To blit on screen, it should be
SDL_BlitSurface(layer, NULL, screen, NULL);
doc for SDL_BlitSurface