I am making a game where a nozzle of a tank rotates around when space is pressed to shoot enemies. However, right in the beginning when the space is pressed, it seems to stop for a few milliseconds and then continues without any problems. How can I make it so that the rotations is smooth and consistent as soon as the space is pressed, right from the start? Here is a minimal reproducible example:
#include "SDL.h"
#include <iostream>
class Nozzle
{
public:
void draw(SDL_Renderer* renderer, int cx, int cy, int l)
{
float x = ((float)cos(angle) * l) + cx;
float y = ((float)sin(angle) * l) + cy;
SDL_RenderDrawLine(renderer, cx, cy, (int)x, (int)y);
}
void plusAngle(float a)
{
angle += a;
}
private:
float angle = 0.0f;
};
int main(int argc, char* argv[])
{
SDL_Window* window = SDL_CreateWindow("RGame", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1200, 600, false);
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0);
SDL_Event event;
Nozzle nozzle;
bool running = true;
while (running)
{
while (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT)
running = false;
if (event.type == SDL_KEYDOWN)
{
if (event.key.keysym.sym == SDLK_SPACE)
nozzle.plusAngle(0.1f);
}
}
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderClear(renderer);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
nozzle.draw(renderer, 600, 300, 70);
SDL_RenderPresent(renderer);
}
SDL_DestroyWindow(window);
SDL_Quit();
return 1;
}
if (event.type == SDL_KEYDOWN)
{
if (event.key.keysym.sym == SDLK_SPACE)
nozzle.plusAngle(0.1f);
}
This is not how you do controls in a game.
If you open a text editor and hold a key, you'll see one letter being typed, then, after a delay, a steady stream of the same repeated letter. And SDL does the same thing, it gives you fake repeated "key down" events in this manner. This is normally used for editing text, not for game controls. (Those repeated events are marked by event.key.repeat == true).
What you should do is to create something like bool space_key_down, set it to true when you get SDL_KEYDOWN for the space key, and to false when you get SDL_KEYUP for the same key. Then, outside of the event loop, if the variable is set, you rotate your nozzle.
Or you can use SDL_GetKeyboardState. SDL does this thing automatically for every key, and you can access the list of flags it maintains using this function.
Also, while we're at it, you normally don't want to use keycodes (.sym == SDLK_SPACE) for game controls. Prefer scancodes (.scancode == SDL_SCANCODE_SPACE). The difference only becomes apparent on exotic layouts (e.g. AZERTY): keycodes represent the letters printed on the keycaps, while scancodes represent physical key locations. For example, on AZERTY you want to use ZQSD instead of WASD. If you use scancodes, it will happen automatically (SDL_SCANCODE_W will represent Z, and so on).
scaling rotation with frame-rate isn't really what I am looking for. Its a different problem
You need to solve this problem too. If you don't want the rotation speed to depend on FPS (bad thing), you must either mutliply the rotation angle by the frame length (it works, but it's easy to make mistakes this way), or make sure your game logic runs the fixed amount of times per second regardless of the FPS (I prefer this solution). See Fix Your Timestep!.
Related
To keep it short, I have this class Display_Frame. Within this Display_Frame, there are 2 rectangles. The first being its own rectangle, and the second being its internal rectangle. The first rectangle is for its positioning and dimensions on a screen, and the second (internal rectangle) is for information within that first rectangle. If the internal rectangle is bigger than the first rectangle, I initiate a scrollbar on the side to scroll for the information. This is where my problem is discovered. If I use this approach for every object on my screen, ie, the main display is its own Display_Frame, then any other object on the screen can also fit within another Display_Frame but is fitted in the screen via the main screens one. When I then scroll, How can I make sure that only the farthest branch of Display_Frame's is scrolled, and not any others.
For example, lets say I have a textbox that fits inside its own Display_Frame. Its own Display_Frame is also embedded within the main screens Display_Frame too. Now lets say I want to scroll in the textbox but only if the mouse is within it. That's easy enough to detect, however how can I make it so that when I do scroll inside the textbox, the main screens Display_Frame doesn't scroll, only the textbox's Display_Frame 's internal rect is moved instead.
In general terms for this, how can I efficiently detect and restrict my Display_Frame's to only scroll if I have scrolled on the farthest down branch of Display_Frame's?
Here is some code displaying my issue:
#include <SDL2/SDL.h>
class Display_Frame
{
public:
SDL_Rect m_Display_Frame_Rect;
SDL_Rect m_Internal_Rect;
void Handle_Events(SDL_Point mousePos, bool scrolledDown, bool scrolledUp);
};
void Display_Frame::Handle_Events(SDL_Point mousePos, bool scrolledDown, bool scrolledUp)
{
if (SDL_PointInRect(&mousePos, &m_Display_Frame_Rect))
{
if (scrolledDown){
m_Display_Frame_Rect.y += 20;
}
if (scrolledUp){
m_Display_Frame_Rect.y -= 20;
}
}
}
int main(int argc, char* argv[])
{
SDL_Init(SDL_INIT_VIDEO);
SDL_Rect display = {0,0,1278,718};
SDL_Window* window = SDL_CreateWindow("Test", 0, 30, display.w+2, display.h+2, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE );
SDL_Renderer* renderer = SDL_CreateRenderer( window, -1, SDL_RENDERER_ACCELERATED );
SDL_Rect testRect = {100, 100, 100, 100};
SDL_Point mousePos = {0,0};
SDL_Event event;
bool running = true;
bool scrolledUp = false, scrolledDown = false;
Display_Frame mainDisplay;
mainDisplay.m_Display_Frame_Rect = display;
mainDisplay.m_Internal_Rect = display;
Display_Frame smallDisplay;
smallDisplay.m_Display_Frame_Rect = testRect;
smallDisplay.m_Internal_Rect = testRect;
while (running)
{
scrolledDown = false;
scrolledUp = false;
while (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT){
running = false;
break;
}
if (event.type == SDL_MOUSEMOTION)
{
mousePos = {event.motion.x, event.motion.y};
}
if (event.type == SDL_MOUSEWHEEL){
if (event.wheel.y > 0){ ///Scrolling up here
scrolledUp = true;
}
if (event.wheel.y < 0){ ///Scrolling down here
scrolledDown = true;
}
}
}
SDL_SetRenderDrawColor(renderer, 255,255,255,255);
SDL_RenderClear(renderer);
mainDisplay.Handle_Events(mousePos, scrolledDown, scrolledUp);
smallDisplay.Handle_Events(mousePos, scrolledDown, scrolledUp);
SDL_SetRenderDrawColor(renderer, 0,0,0,255);
SDL_RenderDrawRect(renderer, &mainDisplay.m_Display_Frame_Rect);
SDL_RenderDrawRect(renderer, &smallDisplay.m_Display_Frame_Rect);
SDL_RenderPresent(renderer);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
Every time I attempt to run my application it creates a window perfectly fine but when moving the mouse about it becomes obvious that it freezes briefly for a few seconds. I also had this issue in a previous SDL project but didn't fix it as it wasn't very vital but I could never find a solution for it.
I tried looking up my issue but couldn't find anything that matches the issue I'm facing, despite this I attempted a few things that I felt could work like slightly different implementations of an event loop.
I'm not 100% sure on what EVERY line of SDL related code does but I took it from a previous project so I knew what it did at one point but it's not too hard to work out.
int width = 160;
int height = 144;
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* window = SDL_CreateWindow("Window Title", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width * 4, height * 4, SDL_WINDOW_SHOWN);
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
SDL_Texture* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, width, height);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
bool quit = false;
while (!quit)
{
SDL_Event E;
while (SDL_PollEvent(&E))
{
if (E.type == SDL_QUIT)
quit = true;
}
}
I expect the window to display smoothly and not have this freezing occur every few seconds. I'm not sure if it only occurs when the mouse is moving or if it happens to freeze every few seconds despite this as the cursor is the only way I have to test it. I saw one post that talked about flooding the event queue but that solution didn't seem to work for me.
Any help is appreciated.
So I'm starting SDL2 and ran through a few tutorials and decided that I would try to make a simple game and a clone of Pong seemed like the simplest. But I can't seem to be able to make the right hand side paddle draw on the window. At the moment I'm using SDL_FillRenderRect, one for each paddle but that just seems to make the left hand side one appear and not the right hand side. Here's my current code:
#include <iostream>
#include "Pong.h"
#include "Paddle.h"
//Screen size
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
bool quit = false;//Has user quit?
Paddle paddleLeft;
Paddle paddleRight;
int Pong::setup(){
//Initilize SDL
SDL_Init(SDL_INIT_EVERYTHING);
window = SDL_CreateWindow("Pong",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
SCREEN_WIDTH,
SCREEN_HEIGHT,
SDL_WINDOW_SHOWN);
if (window == nullptr){
std::cout<<"SDL_CreateWindow error: "<<SDL_GetError()<<std::endl;
SDL_Quit();
return -1;
}
SDL_ShowCursor(0);
renderer = SDL_CreateRenderer(window,
-1,
SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (renderer == nullptr){
std::cout<<"SDL_CreateRenderer error: "<<SDL_GetError()<<std::endl;
SDL_Quit();
return -1;
}
int wH, wW;
//Setting the paddle to the centre of the left hand side of the screen.
SDL_GetWindowSize(window, &wW, &wH);
paddleLeft.setY((wH / 2) - (paddleLeft.getPaddleHeight() / 2));
paddleRight.setY((wH / 2) - (paddleLeft.getPaddleHeight() / 2));
//Setting the x, y, height and width of the left paddle
leftR.x = 20;
leftR.y = paddleLeft.getY();
leftR.h = paddleLeft.getPaddleWidth();
leftR.w = paddleLeft.getPaddleHeight();
//Setting the x, y, height and width of the right paddle
rightR.x = 620;
rightR.y = paddleLeft.getY();
rightR.h = paddleLeft.getPaddleWidth();
rightR.w = paddleLeft.getPaddleHeight();
return 1;//If setup fails then return -1 otherwise return 1
}
void Pong::updateGame(){
SDL_Event event;
while (SDL_PollEvent(&event)){
if (event.type == SDL_QUIT){
quit = true;
}
if (event.type == SDL_KEYDOWN){
switch (event.key.keysym.sym){
case SDLK_w: paddleLeft.setY(paddleLeft.getY() - paddleLeft.getSpeed()); break;
case SDLK_s: paddleLeft.setY(paddleLeft.getY() + paddleLeft.getSpeed()); break;
}
}
}
}
void Pong::render(){
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer);
leftR.x = paddleLeft.getX();
leftR.y = paddleLeft.getY();
rightR.x = paddleRight.getX();
rightR.y = paddleRight.getY();
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderFillRect(renderer, &leftR);
SDL_RenderFillRect(renderer, &rightR);
SDL_RenderPresent(renderer);
}
void Pong::cleanup(){
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
}
void Pong::run(){
while (!quit){
updateGame();
render();
}
cleanup();
}
Could someone please point me in the correct direction for drawing the right paddle? I would also appreciate it if there are anyother errors in my code or ways to make it more efficent and what not if that could also be pointed out. Thanks in advance.
Read this closely:
rightR.x = 620;
rightR.y = paddleLeft.getY();
rightR.h = paddleLeft.getPaddleWidth();
rightR.w = paddleLeft.getPaddleHeight();
right and left mixed up due to copy-paste programming. I'm not saying it's bad, I do it too to reduce harm on my hands. You should just check things like this twice.
Also, in your case, you should start using OOP concepts. Don't perform everything in non-member functions, make member functions that perform on instances instead. I don't know where the values of getY, getPaddleWidth and getPaddleHeight come from, but it's clear to me that there shouldn't be anything other than SDL_Rect holding them.
I even think that SDL is a waste of time and I can't remember what's C++ about it. It's pure C and you're not going to learn anything useful. SFML is far better choice.
I'm trying to display a texture onto the screen but all I'm getting is a black window.
No SDL Errors are being reported. There's a good chance that I'm missing something stupid, but I can't see it. Hopefully another set of eyes will help. Feel free to ask for more code/info.
main.cpp
SDL_Window * window;
SDL_Renderer * renderer;
SDL_Texture * grass;
SDL_Rect g_dst;
SDL_Event event;
Game app;
SDL_Init(SDL_INIT_EVERYTHING);
window = SDL_CreateWindow("tmp", 100, 100, 640, 480, SDL_WINDOW_SHOWN);
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
g_dst.x = g_dst.y = 0;
g_dst.w = 640;
g_dst.h = 480;
grass = IMG_LoadTexture(renderer, "grass.bmp");
while (app.isRunning()) {
app.pollEvents(&event);
app.render_init();
app.render(grass, NULL, &g_dst);
app.render_end();
}
//SDL_Quit() is handled by the Game class' destructor
Game.cpp
//Only functions used for rendering are shown
void render_init(Uint8 red=0, Uint8 green=0, Uint8 blue=0, Uint8 alpha=255)
{
SDL_SetRenderDrawColor(renderer, red, green, blue, alpha);
SDL_RenderClear(renderer);
}
void render(SDL_Texture * texture, SDL_Rect * src, SDL_Rect * dest) {
SDL_RenderCopy(renderer, texture, src, dest);
}
void render_end() { SDL_RenderPresent(renderer); }
First of all, you're initializing everything? please don't do that frequently, mind you that you're also initializing MANY unnecessary stuffs like for game controllers, etc. if the app gets bigger then the efficiency and the possibility of this app running at a smoot speed is at stake.
I also noticed that you are declaring variables in the .cpp file, do that in the header file and just recall the header to the cpp file that will be using it.
You want to render the grass right? and render it as much as the screens size.
(I'll just assume that you used this in the game.cpp part, which is the very first file, thus, not regarding any classes made)
int winWidth = 680; //The reason for this is just in case you make the window resizable
int winHeight = 480; //then the texture would also resize along with the window
SDL_Window *window = window = SDL_CreateWindow("The Space Project", 100, 100, winWidth, winHeight, SDL_WINDOW_SHOWN);
SDL_Renderer *renderer = NULL; //I've set this to NULL so that we can know if
the reason as to why your image is not rendering is because the renderer is not properly working.
renderer = SDL_CreateRenderer( window, -1, SDL_RENDERER_ACCELERATED);
if(renderer == NULL)
{
cout >> "Renderer is not working" >> endl;
//This shows a line at the command prompt that your renderer doesn't have any output, thus, only having a NULL as an equivalent
}
SDL_Texture* grass= NULL;
grass= IMG_LoadTexture(renderer, "grass.bmp"); //As you can see, I've set the grass to Null again
if(grass == NULL)
{
cout >> "Grass have failed to initialize" >> endl;
/*I don't normally do this but it's very important if you really need trouble shooting guides
but this time, were here to check IF the grass.bmp entered the SDL_Texture grass, so if the system can't find the .bmp file then it would show this error
since the grass (SDL_Texture) still doesn't have anything inside it (NULL)*/
}
SDL_Rect grass_rect;
grass_rect.x = 0;
grass_rect.y = 0;
grass_rect.w = winWidth;
grass_rect.h = winHeight;
//Loop part, I'll skip some of it
while (!quit && mainEvent->type != SDL_QUIT) //!quit is just an imaginary Boolean I've typed)
{
SDL_PollEvent(mainEvent); //Let's say you created the event already
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, grass, NULL, &grass_rect);
//The NULL part is also similar to a rect but it's a limiting type, we didn't assign anything to it
since I assumed that you wanted the whole image to be rendered
SDL_RenderPresent(renderer);
}
I revised the code and made it more efficient since your code called for useless extras which might result in lower performance.
I also notice that you tried calling for color changes?
use this
SDL_SetTextureColorMod(texture, red-value, green-value, blue-value);
and put it in the loops part under the render present of the same texture.
SDL_SetTextureColorMod(grass, 250, 250, 250);
Doing this would set all color values to 250, thus, having a white color, this change your texture color to white.
You're also wasting space on making the app.is running(), you could easily replace it with a boolean, which consumes much less space or you could omit it if you don't have an exit button inside the application and just make your loop read the SDL_QUIT, this saves space for the file, mind the efficiency.
If this still doesn't work then try replacing the image your using, make a simple one on paint name it something like "grass.png" or anything then try it again.
Don't forget to put the file in the proper folder, in the DEBUG folder if you haven't specified a folder, and also put it in the app folder so it would also read it when it executes as an .exe file and not as part of the debug command.
I've got some code (below) that uses SDL_ttf and would like to:
Be able to render text (from the TTF) in alignment (to a buffer or array as well) like a console would (having each character printed in it's individual cell).
Be able to utilize a blinking cursor (possibly render and un-render an underscore, maybe?)
Be able to let the user input text from the keyboard and render each char on the screen right when it is typed (using SDLK_charhere).
Getting back to #1: I'm thinking about getting the width of the previous character printed on the screen (from the TTF) and using its width (in pixels) to print the next character right after the previous character, plus 2 pixels. <-- PLEASE TELL ME IF THE SPACING BETWEEN CHARS IN A REGULAR WIN32 CONSOLE IS A DIFFERENT SIZE IN PIXELS.
Here's the code that needs to be modified:
#include "include/SDL/SDL.h"
#include "include/SDL/SDL_ttf.h"
int currentX = 0;
int currentY = 0;
int newW;
int newH;
SDL_Surface* screen;
SDL_Surface* fontSurface;
SDL_Color fColor;
SDL_Rect fontRect;
SDL_Event event;
TTF_Font* font;
//Initialize the font, set to white
void fontInit(){
TTF_Init();
font = TTF_OpenFont("dos.ttf", 12);
fColor.r = 0; // 255
fColor.g = 204; // 255
fColor.b = 0; //255
}
//Print the designated string at the specified coordinates
void PrintStr(char *c, int x, int y){
fontSurface = TTF_RenderText_Solid(font, c, fColor);
fontRect.x = x;
fontRect.y = y;
SDL_BlitSurface(fontSurface, NULL, screen, &fontRect);
SDL_Flip(screen);
}
int main(int argc, char** argv)
{
// Initialize the SDL library with the Video subsystem
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE);
//Create the screen
screen = SDL_SetVideoMode(320, 480, 0, SDL_SWSURFACE);
//Initialize fonts
fontInit();
PrintStr("", 0, 0);
do {
// Process the events
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_KEYDOWN:
switch (event.key.keysym.sym) {
// Escape forces us to quit the app
case SDLK_ESCAPE:
event.type = SDL_QUIT;
break;
default:
break;
}
break;
default:
break;
}
}
SDL_Delay(10);
} while (event.type != SDL_QUIT);
// Cleanup
SDL_Quit();
return 0;
}
This isn't a trivial thing to do, but it sounds like a good learning project! You're maybe going to need a few thousand lines of code rather than the few tens you have up there. Perhaps start by considering these questions and their answers.
Do you want a fixed width font, or a variable width font?
How can you buffer the rendered text in an intelligent way? (glyphs or lines for eg.)
How can you buffer the text itself in an intelligent way?
How can you translate key presses into text?
What is translating and driving all this?
All these will need to be considered along with the most important question of all:
Do I want to do this?
If you do do this, it will teach you a lot about programming, but it might not give you the best full screen console you're looking for.