I am trying to display a menu using the Menu class. But in the function draw() of that class, the SDL_BlitSurface is not working and is throwing a segmentation default while executing this line :
SDL_BlitSurface(el.surfaceNormal, NULL, surface, &el.rect);
Here is the code of the function:
void Menu::draw(SDL_Surface* surface) {
for(int i=0; i<menuElementList.size(); i++){
auto el = menuElementList.at(i);
SDL_BlitSurface(el.surfaceNormal, NULL, surface, &el.rect);
}
}
The struct I am using in vector:
struct menuElement{
SDL_Surface* surfaceNormal;
SDL_Surface* surfaceHover;
SDL_Rect rect;
std::string text;
};
Normally, I should be able to render all of the surface present in the vector, but for a reason I can't figure, it is not working.
The code to populate the vector:
void Menu::addMenu(std::string name, int x, int y) {
menuElement m;
TTF_Font* Sans = TTF_OpenFont("OpenSans-Light.ttf", 25);
if(!Sans){
std::cout << TTF_GetError() << std::endl;
}
SDL_Color White = {255, 255, 255};
SDL_Surface* surfaceMessage = TTF_RenderText_Solid(Sans, name.c_str(), White);
m.surfaceNormal = surfaceMessage;
SDL_Color Red = {255, 0, 0};
SDL_Surface* hoverMessage = TTF_RenderText_Solid(Sans, name.c_str(), Red);
m.surfaceHover = hoverMessage;
SDL_Rect rect;
rect.x = rect.y = 50;
m.rect = rect;
m.text = name;
SDL_FreeSurface(surfaceMessage);
SDL_FreeSurface(hoverMessage);
menuElementList.push_back(m);
}
I know that the vector gets populated because I can display the text attribute of each element in it. What's wrong?
Well the error is pretty simple you would have realized if had debugged a bit, here is a MVCE that you SHOULD have done.
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <iostream>
#include <vector>
#include <string>
#define HEIGHT 600
#define WIDTH 800
using namespace std;
struct menuElement{
SDL_Surface* surfaceNormal;
SDL_Surface* surfaceHover;
SDL_Rect rect;
std::string text;
};
std::vector<menuElement> menuElementList;
void add_element(std::string name, int x, int y) {
menuElement m;
TTF_Font* Sans = TTF_OpenFont("OpenSans-Light.ttf", 25);
if(!Sans){
std::cout << TTF_GetError() << std::endl;
}
SDL_Color White = {255, 255, 255};
SDL_Surface* surfaceMessage = TTF_RenderText_Solid(Sans, name.c_str(), White);
m.surfaceNormal = surfaceMessage;
SDL_Color Red = {255, 0, 0};
SDL_Surface* hoverMessage = TTF_RenderText_Solid(Sans, name.c_str(), Red);
m.surfaceHover = hoverMessage;
SDL_Rect rect{x, y, 50, 50}; // You were never using x and y before now it draws at that position with a rect of size 50x50
m.rect = rect;
m.text = name;
// SDL_FreeSurface(surfaceMessage); << how is this not crashing in your system when blitting, I don't know
// SDL_FreeSurface(hoverMessage);
menuElementList.push_back(m);
}
int main() {
SDL_Init(SDL_INIT_VIDEO);
TTF_Init();
SDL_Window *window = SDL_CreateWindow("TextFail", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WIDTH, HEIGHT, SDL_WINDOW_SHOWN);
SDL_Surface *surface = SDL_GetWindowSurface(window);
bool quit = false;
SDL_Event event;
add_element("ello", 20, 20);
add_element("Bye", 40, 40);
while (!quit) {
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
quit = true;
}
}
for (auto &el : menuElementList)
SDL_BlitSurface(el.surfaceNormal, NULL, surface, &el.rect);
SDL_UpdateWindowSurface(window);
}
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
I commented the 2 lines that are messing with you, even if the vector gets populated its getting populated with uninitialized data since you FREE the surfaces, you should do that when freeing the menuElement (If you're using C++ in the menuElement destructor for example or on a free_menu_element(menuElement *e) { .. } function if C)
Remember when you copy pointers you copy the direction of memory they point to and not its contents.
You were also never using x and y!
Related
I recently tried to implement OneLoneCoder's simple 3d Graphics Engine, from his Code-It-Yourself series, on SDL2. So I made some triangles that move on the window, but these triangles would appear to move at a slower framerate when I left my mouse completely still, and then move at their normal framerate if I started to move my mouse.
You see, to animate these triangles I used some while loops, something like this:
while (true) {
// Black Background
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
// Custom function that draws a triangle using SDL_RenderDrawLine()
drawTriangle(renderer, triangle);
SDL_RenderPresent(renderer); // updates the pixels on the renderer
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT)
goto Exit;
}
}
Pretty standard, right?
Here is an example of some code that has this exact same issue:
#include<iostream>
#include"SDL.h"
struct vec {
float x, y;
vec ( float x, float y)
: x(x), y(y)
{}
vec () {}
};
struct triangle {
vec p[3];
triangle() {}
};
void drawTriangle(SDL_Renderer* r, const triangle& tri) {
SDL_RenderDrawLine(r, tri.p[0].x, tri.p[0].y, tri.p[1].x, tri.p[1].y);
SDL_RenderDrawLine(r, tri.p[1].x, tri.p[1].y, tri.p[2].x, tri.p[2].y);
SDL_RenderDrawLine(r, tri.p[2].x, tri.p[2].y, tri.p[0].x, tri.p[0].y);
}
int main(int argc, char **argv) {
uint16_t width = 400;
uint16_t height = 300;
if (SDL_Init(SDL_INIT_VIDEO) > 0) { std::cout << "SDL_INIT FAILED\nERROR: " << SDL_GetError() << std::endl; }
SDL_Window* window = NULL;
SDL_Renderer* renderer = NULL;
SDL_Event event;
window = SDL_CreateWindow("SDL Test :)", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_SHOWN);
if (window == NULL) { std::cout << "SDL_WINDOW FAILED\nERROR: " << SDL_GetError() << std::endl; }
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_SOFTWARE);
triangle myTri;
myTri.p[0] = vec( width * 0.5, 0);
myTri.p[1] = vec( width * 0.333, height * 0.2);
myTri.p[2] = vec( width * 0.666, height * 0.2);
while (true) {
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
myTri.p[0].y += 0.01f;
myTri.p[1].y += 0.01f;
myTri.p[2].y += 0.01f;
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
drawTriangle(renderer, myTri);
SDL_RenderPresent(renderer);
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT)
goto Exit;
}
}
Exit:
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
I'm making a little game as a small project but I can't get an if statement to do anything. If I make it !statement it works though. I run this if statement to find which cube on the "grid" (An array or cubes I render in a for loop I didn't show) the mouse clicked on. I use C++ and SDL2 on a Mac. This is my code:
#include <iostream>
#include <SDL2/SDL.h>
void RenderRects(SDL_Renderer *renderer);
void ToggleRect(int MouseX, int MouseY);
struct Grid
{
bool IsActive;
SDL_Rect Rect;
};
Grid grid[228960];
int main()
{
bool IsRunning = true;
bool IsRunningSim;
SDL_Init(SDL_INIT_EVERYTHING);
SDL_Window *window = SDL_CreateWindow("My Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1000, 780, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
int h, w;
SDL_MaximizeWindow(window);
SDL_GetRendererOutputSize(renderer, &w, &h);
while (IsRunning)
{
// std::cout << w << std::endl;
// std::cout << h << std::endl;
SDL_Event ev;
while (SDL_PollEvent(&ev))
{
if (ev.type == SDL_QUIT)
{
IsRunning = false;
}
if (ev.type == SDL_MOUSEBUTTONDOWN)
{
int x, y;
SDL_GetMouseState(&x, &y);
ToggleRect(x, y);
}
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
//rendering
RenderRects(renderer);
SDL_RenderPresent(renderer);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
void RenderRects(SDL_Renderer *renderer)
{
for (int i = 0; i < 1440; i += 10)
{
for (int j = 0; j < 795; j += 10)
{
SDL_Rect Rect = {i, j, 10, 10};
grid[i].Rect = Rect;
SDL_SetRenderDrawColor(renderer, 100, 100, 100, 225);
SDL_RenderDrawRect(renderer, &grid[i].Rect);
}
}
}
void ToggleRect(int MouseX, int MouseY)
{
SDL_Point MousePos;
MousePos.x = MouseX;
MousePos.y = MouseY;
for (int i = 0; i < 228961; i++)
{
if (SDL_PointInRect(&MousePos, &grid[i].Rect)) //This is the if that doesn't work.
{
std::cout << i << std::endl;
}
}
}
I have fixed this. I had to change my method of drawing since it was drawing over the rect and then showing after I changed its color. There was also an issue with generating the Rects that was probably effect it.
So I'm trying to write text to an SDL_Window but I can't get the text to show up. And i've tried messing around with different colors and all of that stuff.
Here's my code
#include <SDL/SDL.h>
#include <SDL/SDL_ttf.h>
#include <GL/glew.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv)
{
if (SDL_Init(SDL_INIT_VIDEO) == (-1))
{
printf("Could not initialize SDL: %s.\n", SDL_GetError());
exit(-1);
}
int WINWIDTH = 640;
int WINHEIGHT = 480;
SDL_Window* window = SDL_CreateWindow("Text Test", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WINWIDTH, WINHEIGHT, SDL_WINDOW_OPENGL);
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
TTF_Font* Sans = TTF_OpenFont("Fonts\\arial.ttf", 24);
SDL_Color White = { 255, 0, 0 };
SDL_Surface* surfaceMessage = TTF_RenderText_Solid(Sans, "put your text here", White);
SDL_Texture* Message = SDL_CreateTextureFromSurface(renderer, surfaceMessage);
SDL_Rect Message_rect;
Message_rect.x = 0;
Message_rect.y = 0;
Message_rect.w = 100;
Message_rect.h = 100;
SDL_RenderCopy(renderer, Message, NULL, &Message_rect);
system("pause");
exit(0);
}
I have tried multiple ways of creating text but none of it works.
Today I started a C++/SDL2 Snake clone, and I've been looking for ways to make my code neater, -particularly with classes. I tried to put all the SDL code used for the window/display in a class. When I run the code, the window closes instantly. From the error tests I set up in display.cpp, it also tells me through the console that SDL_UpdateWindowSurface() (in display.update()) is always returning -1. Why does this happen when I rearrange my code like this?
With this code I load an image in main() and display it through my class' function applySurface(). The idea is to have classes/objects for the game's grid/board, the snake, etc., -each calling applySurface() for their own images. Feel free to tell me if this is a bad idea altogether.
main.cpp:
#include <SDL.h>
#include "display.h"
SDL_Event event;
SDL_Surface* image = nullptr;
int main(int argc, char* args[])
{
Display display;
display.loadImage("image.bmp");
if (SDL_Init(SDL_INIT_EVERYTHING) == -1)
return false;
bool quit = false;
while (!quit)
{
while (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT)
quit = true;
}
display.applySurface(0, 0, image, display.windowSurface);
display.update();
}
SDL_FreeSurface(image);
SDL_Quit();
return 0;
}
display.h:
#pragma once
#include <SDL.h>
#include <string>
#include <iostream>
class Display
{
public:
SDL_Window* window;
SDL_Surface* windowSurface;
Display();
SDL_Surface *loadImage(std::string fileName);
void applySurface(int x, int y, SDL_Surface *source, SDL_Surface *destination, SDL_Rect *clip = nullptr);
void update();
~Display();
private:
const int WINDOW_WIDTH = 612;
const int WINDOW_HEIGHT = 632;
const int SCREEN_BPP = 2;
};
display.cpp:
#pragma once
#include "display.h"
Display::Display()
{
window = SDL_CreateWindow("Snake", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_SHOWN);
if (window == NULL)
std::cout << "Error: SDL_CreateWindow failed." << std::endl;
windowSurface = SDL_GetWindowSurface(window);
}
SDL_Surface* Display::loadImage(std::string fileName)
{
SDL_Surface* loadedImage = NULL;
SDL_Surface* optimizedImage = NULL;
loadedImage = SDL_LoadBMP(fileName.c_str());
if (loadedImage != NULL)
{
optimizedImage = SDL_ConvertSurface(loadedImage, windowSurface->format, 0);
SDL_FreeSurface(loadedImage);
if (optimizedImage != NULL)
SDL_SetColorKey(optimizedImage, SDL_TRUE, SDL_MapRGB(optimizedImage->format, 255, 255, 255));
}
return optimizedImage;
}
void Display::applySurface(int x, int y, SDL_Surface *source, SDL_Surface *destination, SDL_Rect *clip)
{
SDL_Rect offset;
offset.x = x;
offset.y = y;
SDL_BlitSurface(source, clip, destination, &offset);
}
void Display::update()
{
if (SDL_UpdateWindowSurface(window) == -1)
std::cout << "Error: SDL_UpdateWindowSurface() failed." << std::endl;
}
Display::~Display()
{
SDL_FreeSurface(windowSurface);
windowSurface = NULL;
SDL_DestroyWindow(window);
window = NULL;
}
This is a valid use of classes to structure your code. SDL_Init needs to come before any other SDL functions, which means you're best off moving SDL_Init to the top of main or adding it to the display constructor. If you add it to the beginning of the display constructor this means that you can only have one display class object running at a time, which would likely be fine in this case.
When I run this it just shows a black screen, and if i Put SDL_GetError() at the end it prints a blank line.....
Any ideas on how to fix this?
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <iostream>
class character {
public: SDL_Rect src, cur;
public: SDL_Texture *image;
void setSrc(int x, int y, int w, int h) {
src.x = x;
src.y = y;
src.w = w;
src.h = h;
}
void setCur(int x, int y, int w, int h) {
src.x = x;
src.y = y;
src.w = w;
src.h = h;
}
};
int main(int argc, char* argv[]) {
bool in = true;
character p1, p2, ball;
SDL_Window *window = 0;
SDL_Renderer *renderer = 0;
SDL_Surface *screen, *imageLoader;
SDL_Init(SDL_INIT_VIDEO);
window = SDL_CreateWindow("Pong",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_SHOWN);
renderer = SDL_CreateRenderer(window, -1, 0);
p1.setSrc(0, 0, 100, 500);
p1.setCur(0, 0, 100, 500);
imageLoader = IMG_Load("/home/donaldo/Documents/Games/Images/player.bmp");
p1.image = SDL_CreateTextureFromSurface(renderer, imageLoader);
SDL_RenderCopy(renderer, p1.image, &p1.src, &p1.cur);
SDL_RenderPresent(renderer);
SDL_Delay(3000);
return 0;
}
You have a typo in your setCur() function. I assume you want to be setting the cur rectangle, not src again.
Also, not related but it's good practice to free the surface and texture you allocated when you're done with them.