SDL window updates slowly if I don't move my mouse - c++

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;
}

Related

SDL2 PointInRect If Statement not working

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.

SDL2 Wont draw Rectangles Correctly

I'm creating a window and drawing a box but for some reason instead of drawing a box, the screen is just changed to that color. I have attached a photo of how the window looks and I will attach the source code.
#include <iostream>
#include <SDL.h>
#undef main
using namespace std;
int SCREEN_WIDTH = 650;
int SCREEN_HEIGHT = 650;
int main() {
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
std::cout << "SDL_Init Error: " << SDL_GetError() << std::endl;
return 1;
}
SDL_Window *window = SDL_CreateWindow("Cells", 100, 100, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (window == nullptr) {
std::cout << "SDL_CreateWindow Error: " << SDL_GetError() << std::endl;
SDL_Quit();
return 1;
}
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (renderer == nullptr) {
std::cout << "SDL_CreateRenderer Error: " << SDL_GetError() << std::endl;
SDL_DestroyWindow(window);
SDL_Quit();
return 1;
}
SDL_Event event;
bool quit = false;
while (!quit) {
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
quit = true;
}
}
SDL_RenderClear(renderer);
// renderTextures
SDL_Rect fillRect = { 122, 122, 122, 122};
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderFillRect(renderer, &fillRect);
SDL_RenderPresent(renderer);
}
return 0;
}
Doesn't Draw Correctly
SDL_RenderClear uses current draw colour, which you modified, so your clear and rectangle colour is the same. Set different clear colour (the one you want at background where nothing else is drawn) with e.g.
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
// now draw your rectangles with different col

SDL_BlitSurface is throwing segmentation fault error

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!

SDL MOUSEMOTION yrel is not giving a proper value

I created this easy function and it seems there is something wrong with it that I really don't see what it is...
Evnt.motion.yrel output crazy number.
void EnthropyGenerator::OpenWindow()
{
SDL_Window *EnthropyGeneratorWindow;
SDL_Renderer* Renderer;
SDL_Init(SDL_INIT_VIDEO);
EnthropyGeneratorWindow =
SDL_CreateWindow("Enthropy Generator",
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
WindowSizeX,
WindowSizeY,
SDL_WINDOW_SHOWN |
SDL_WINDOW_BORDERLESS |
SDL_WINDOW_INPUT_GRABBED);
if (EnthropyGeneratorWindow == NULL)
{
ServerEngine::FatalError("Could not create window: " +
(std::string)SDL_GetError());
}
Renderer = SDL_CreateRenderer(EnthropyGeneratorWindow, -1, 0);
SDL_SetRenderDrawColor(Renderer, 10, 255, 0, 255);
SDL_RenderClear(Renderer);
SDL_RenderPresent(Renderer);
bool NeedMoreEntropy = true;
SDL_Event Evnt;
while (NeedMoreEntropy)
{
while (SDL_PollEvent(&Evnt))
{
if (Evnt.type == SDL_MOUSEMOTION)
{
std::cout << Evnt.motion.xrel << " and "
<< Evnt.motion.yrel << std::endl;
std::cout << m_EnthropyNeed << std::endl;
UpdateMousePosition(Evnt.motion.xrel, Evnt.motion.yrel);
AddEnthropy(m_MouseX, m_MouseY);
if (m_EnthropyNeed == 256)
{
NeedMoreEntropy = false;
}
}
}
}
SDL_DestroyWindow(EnthropyGeneratorWindow);
SDL_Quit();
}
void EnthropyGenerator::UpdateMousePosition(int deltaX, int deltaY)
{
m_MouseX += deltaX;
m_MouseY += deltaY;
}
void EnthropyGenerator::AddEnthropy(int deltaX, int deltaY)
{
m_EnthropyNeed++;
}
The output in the console is: 0 and 1985359926 and so on.
The output
It seems like something is not initialized or looks like a bad pointer. How can I fix this problem?

SDL_Texture move animation

i want to make interpolation for my mini game. There are 2 SDL_Textures, when i click on first and then on second, they positions are swapped, but without move animation.
So how to make animated place swapping for SDL_Textures ?
void MainLoop() {
SDL_Event Event;
float t = 0.0f;
float dt = 0.1f;
float currentTime = 0.0f;
float accumulator = 0.0f;
while (Running)
{
while(SDL_PollEvent(&Event)) {
OnEvent(&Event);
}
const float newTime = SDL_GetTicks();
float frameTime = newTime - currentTime;
const Uint32 maxFrameTime = 1000; // 1 sec per frame is the slowest we allow
if( frameTime > maxFrameTime) {
frameTime = maxFrameTime;
}
currentTime = newTime;
accumulator += frameTime;
while( accumulator >= dt )
{
this->UpdatePositions(); // simulate a "frame" of logic at a different FPS than we simulate a frame of rendering
accumulator -= dt;
t += dt;
}
Render();
}
Main Render method:
void Puzzle::Render() const {
SDL_RenderClear(renderer);
for (int i = 0; i < blocksNum; ++i)
{
Rectangle texRect = normalizeTexCoords(blockRect(blocks[i]));
Rectangle scrRectOrigin = blockRect(i);
DrawRect(scrRectOrigin, i, texRect);
}
SDL_RenderPresent(renderer);
}
void DrawRect(const Rectangle& screenCoords, int textureId, const Rectangle& textureCoord) {
SDL_Texture *texture = blockTexture(blocks[textureId]);
renderTexture(texture, renderer, (int)screenCoords.left, (int)screenCoords.top, (int)blockWidth, (int)blockHeight);
//SDL_DestroyTexture(texture2);
}
void renderTexture(SDL_Texture *tex, SDL_Renderer *ren, int x, int y, int w, int h) {
// Destination rectangle position
SDL_Rect dst;
dst.x = x; dst.y = y; dst.w = w; dst.h = h;
SDL_RenderCopy(ren, tex, NULL, &dst);
}
And in my UpdatePosition method i want to make my SDL_Textures moving, but it still swapping without animation.
void Puzzle::UpdatePositions()
{
if (secondSwappedBlock != -1) {
Rectangle firstRect = blockRect(blocks[firstSwappedBlock]);
firstRect.left += speed * deltaTime;
firstRect.right += speed * deltaTime;
SDL_Texture *texture = blockTexture(blocks[firstSwappedBlock]);
renderTexture(texture, renderer, (int)firstRect.left, (int)firstRect.top, (int)blockWidth, (int)blockHeight);
}
}
void Puzzle::SwapBlocks(const int first, const int second) const
{
if (first != second)
{
const int t = blocks[first];
blocks[first] = blocks[second];
blocks[second] = t;
}
}
Here is a minimal (yet fully working) example:
#include "SDL.h"
#include <assert.h>
/* linear interpolation between {start.x; start.y} and {end.x; end.y} */
static void rect_lerp(SDL_Rect *out, const SDL_Rect *start, const SDL_Rect *end, float f) {
float t = 1.0f - f;
out->x = (float)start->x * t + (float)end->x * f;
out->y = (float)start->y * t + (float)end->y * f;
}
int main(int argc, char **argv) {
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
SDL_Window *window = SDL_CreateWindow("example",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
640,
480,
SDL_WINDOW_SHOWN);
assert(window);
SDL_Renderer *renderer = SDL_CreateRenderer(window, 0, SDL_RENDERER_ACCELERATED);
assert(renderer);
int quit = 0;
int animate = 0;
Uint32 animation_start_time;
/* whole animation will play in e.g. one seconds */
const Uint32 animation_time_total = 1000;
SDL_Rect rect0 = {
.x = 0, .y = 0, .w = 128, .h = 128
};
SDL_Rect rect1 = {
.x = 256, .y = 256, .w = 128, .h = 128
};
while(!quit) {
/* time of current frame */
Uint32 tcurrent = SDL_GetTicks();
SDL_Event event;
while(SDL_PollEvent(&event)) {
if(event.type == SDL_KEYUP) {
if(event.key.keysym.sym == SDLK_ESCAPE) {
quit = 1;
break;
} else if(event.key.keysym.sym == SDLK_SPACE) {
animate = 1;
animation_start_time = tcurrent;
}
}
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
SDL_Rect r0 = rect0, r1 = rect1;
/* if not within animation - leave coordinates as they are */
if(animate) {
if(tcurrent > animation_start_time + animation_time_total) {
/* animation ends by now */
/* swap rect0 and rect1 and stop animation */
SDL_Rect t = rect0;
rect0 = rect1;
rect1 = t;
animate = 0;
/* need to update r0 and r1 too */
r0 = rect0;
r1 = rect1;
} else {
/* animation is incomplete - interpolate coordinates */
/* calculate current animation percentage - in range [0; 1] */
float factor = ((float)(tcurrent - animation_start_time)) / animation_time_total;
rect_lerp(&r0, &rect0, &rect1, factor);
rect_lerp(&r1, &rect1, &rect0, factor);
}
}
/* r0 and r1 now have correct coordinates, draw */
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderDrawRect(renderer, &r0);
SDL_SetRenderDrawColor(renderer, 255, 0, 255, 255);
SDL_RenderDrawRect(renderer, &r1);
SDL_RenderPresent(renderer);
}
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
For sake of simplicity it draws rectangles instead of textures, but that could be easily changed.
As you can see, in that example I'm keeping old coordinates for as long as animation incomplete, and fixating it only when it is fully gone. This easily allows to replay it back and don't have problems with precision; but - if you'll try to press space when animation already playing, it will just restart.
Another way could be updating coordinates on each animation step (may have different update frequency then redraw); in that case, you'll need to keep current coordinates, destination coordinates (the ones that you'll reach once animation is complete) and, probably, start coordinates (to eliminate precision problems).