3D / FPS Camera Issues - SDL2 - c++

I got a problem with my code, I'm trying to make a First Person 3D Camera.
I use SDL_GetKeyboardState(NULL) to get the pressed keys.
When I press one of the defined keys nothing happens, why?
Camera (Controll):
void Control(float movevel, float mousevel, bool mi, SDL_Window* window)
{
if (mi) //if the mouse is in the screen
{
int MidX = 640 / 2; //middle of the screen
int MidY = 480 / 2;
SDL_ShowCursor(SDL_DISABLE); //we don't show the cursor
int tmpx, tmpy;
SDL_GetMouseState(&tmpx, &tmpy); //get the current position of the cursor
camYaw += mousevel*(MidX - tmpx); //get the rotation, for example, if the mouse current position is 315, than 5*0.2, this is for Y
camPitch += mousevel*(MidY - tmpy); //this is for X
lockCamera();
//SDL_WarpMouse(MidX, MidY); //move back the cursor to the center of the screen
SDL_WarpMouseInWindow(window, MidX, MidY);
const Uint8* kstate = SDL_GetKeyboardState(NULL);
if (kstate[SDLK_w])
{
if (camPitch != 90 && camPitch != -90)
{ //if we are facing directly up or down, we don't go forward, it will be commented out, when there will be gravity
moveCamera(movevel, 0.0); //move forward
}
moveCameraUp(movevel, 0.0); //move up/down
}
if (kstate[SDLK_s])
{
//same, just we use 180 degrees, so we move at the different direction (move back)
if (camPitch != 90 && camPitch != -90)
{
moveCamera(movevel, 180.0);
}
moveCameraUp(movevel, 180.0);
}
if (kstate[SDLK_a])
{ //move left
moveCamera(movevel, 90.0);
}
if (kstate[SDLK_d])
{ //move right
moveCamera(movevel, 270);
}
}
glRotatef(-camPitch, 1.0, 0.0, 0.0);
glRotatef(-camYaw, 0.0, 1.0, 0.0);
}
Main (Loop)
while (running)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
SDL_Event ev;
while (SDL_PollEvent(&ev))
{
switch (ev.type)
{
case SDL_QUIT:
running = false;
break;
case SDL_KEYDOWN:
int x, y;
SDL_GetMouseState(&x, &y);
Main::handleKeys(ev.key.keysym.scancode, x, y);
break;
}
}
Main::update(window);
Main::render(window);
SDL_GL_SwapWindow(window);
}
Main (Update):
void Main::update(SDL_Window* window)
{
Control(0.2, 0.2, mousein, window);
UpdateCamera(0.2); //move the camera to the new location
}

you should call the glRotatef functions before you translate the camera, otherwise it will be rotated about the origin and not its position

Use SDL_PumpEvents() to update the state array. (From the SDL_GetKeyboardState() wiki).
I believe that if you do:
SDL_PumpEvents();
SDL_GetKeyboardState(NULL);
you should get the results you are looking for. (You need to SDL_PumpEvents every loop as well as SDL_GetKeyboardState obviously)
(EDIT)
Another option:
case SDL_KEYDOWN:
switch(event.key.keysym.sym){
case SDLK_w
(... code)
break;
case SDLK_s:
(... code)
break;
case SDLK_a:
(... code)
break;
case SDLK_d:
(... code)
break;
You can also send the event.key.keysym.sym to a function and use it their ofcourse.

Related

Fade in and fade out object on a specific time

int state = 1;
bool turbines_visible = true;
// move the hot air balloon up
// make the square go up
void update(int value) {
// 1 : move up
if (state == 1) {
squareY += 1.0f;
if (squareY > 650.0) {
state = 2;
squareX = -400.0f;
squareY = 200.0f;
}
}
// 2 : move right
else if (state == 2) {
squareX += 1.0f;
if (squareX > 500.0) {
state = 3;
squareX = 0.0f;
squareY = 600.0f;
}
}
// 3 : move down
else if (state == 3) {
squareY -= 1.0f;
if (squareY < 0.0) {
state = 0;
}
}
glutTimerFunc(25, update, 0);
turbines_visible = !turbines_visible;
}
void display() {
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
switch (state) {
case 0:
drawBackground1();
break;
case 1:
drawBackground1();
break;
case 2:
drawBackground2();
break;
case 3:
drawBackground1();
break;
}
glPushMatrix();
glTranslatef(squareX, squareY, squareZ);
// display spray
drawSpray();
// display hot air balloon
drawAirBalloon();
glPopMatrix();
if (turbines_visible) {
// display first (left) wind turbine
drawLeftTurbine();
// display first (right) wind turbine
drawRightTurbine();
}
// display rain
drawRain();
calcFPS();
counter++;
glFlush();
glutSwapBuffers();
glutPostRedisplay();
}
The hot air balloon travels up perfectly fine, but the wind turbines keep faded in and out really fast, which is what I don't want. I want it to be visible in the first scene, invisible in the second scene, and visible again in the third scene. I know that the problem is with the glutTimerFunc code because it is using 25 milliseconds, but I need it for my hot air balloon. I would appreciate if someone could help me solve this problem.
Click here to see the full code
Scene 1
Click here to see GIF
Scene 2
Click here to see GIF
Scene 3
Click here to see GIF
I want it to be visible in the first scene, invisible in the second scene, and visible again in the third scene.
The condition for turbines_visible has to be
turbines_visible = !turbines_visible;
turbines_visible = state != 2;

Using SFML to draw stuff [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I'm trying to create a scribble clone for a uni project.
My lines are basically a lot of dots. If I draw too quick, the line breaks and I have single dots.
Also, I can't figure out how to draw lines ONLY when a mouse button is pressed.
I tried to put mouseMoved into a while loop until mouseButton is released but that didn't seem to work. I ended up in an infinite loop.
Here is my code so far:
while (window.isOpen())
{
while (window.pollEvent(event))
{
int mouseButtonX = event.mouseButton.x;
int mouseButtonY = event.mouseButton.y;
int mouseMoveX = event.mouseMove.x;
int mouseMoveY = event.mouseMove.y;
setBrushSize(5);
brush.setRadius(brushSize);
brush.setPosition(mouseMoveX - brushSize, mouseMoveY - brushSize);
brush.setFillColor(sf::Color::Transparent);
brush.setOutlineColor(sf::Color::Green);
brush.setOutlineThickness(2);
switch (event.type) {
case (sf::Event::Closed):
window.close();
break;
case (sf::Event::KeyPressed):
if (event.key.control && event.key.code == sf::Keyboard::X) {
cout << "closing";
window.close();
}
if (event.key.code == sf::Keyboard::R) {
cout << "printed";
brushColor = setBrushColor(255, 0, 0);
}
if (event.key.code == sf::Keyboard::G) {
brushColor = setBrushColor(0, 255, 0);
}
if (event.key.code == sf::Keyboard::B) {
brushColor = setBrushColor(0, 0, 255);
}
if (event.key.code == sf::Keyboard::C) {
for (int i = 0; i < points.size(); i++) {
points.clear();
}
it = 0;
}
break;
case(sf::Event::MouseButtonPressed):
points.push_back(point);
points[it].setRadius(brushSize);
points[it].setFillColor(brushColor);
points[it].setPosition(mouseButtonX - brushSize, mouseButtonY - brushSize);
it++;
cout << "drawPoint: Pressed X = " << mouseButtonX << " Y = " << mouseButtonY << endl;
break;
case(sf::Event::MouseMoved):
points.push_back(point);
points[it].setRadius(brushSize);
points[it].setFillColor(brushColor);
points[it].setPosition(mouseMoveX - brushSize, mouseMoveY - brushSize);
it++;
cout << "drawPoint: Moved X = " << mouseMoveX << " Y = " << mouseMoveY << endl;
break;
}
}
window.clear(sf::Color(255, 247, 204));
window.draw(SkechyT);
window.draw(close);
window.draw(brush);
window.draw(color);
window.draw(clear);
for (int i = 0; i < points.size(); i++) {
window.draw(points[i]);
}
//window.draw(point);
window.display();
}
}
int getBrushSize() {
return brushSize;
}
void setBrushSize(int num) {
brushSize = num;
}
sf::Color setBrushColor(int r, int g, int b) {
return sf::Color(r, g, b);
}
~Visualizer();
};
While you can modify a sf::VertexArray on the fly (basically building a vector drawing app), you can also use a sf::RenderTexture as an actual drawing canvas.
Considering you tried drawing lots of small points, I'd assume you're aiming for the latter. What's important here is the fact that you don't necessarily have to clear render textures between drawing calls and can therefore preserve whatever has been drawn before.
Combined with the original goal – drawing – this becomes very easy.
All you have to do is draw the changes (e.g. when moving the cursor), finalize the render texture (by calling display()), and then presenting it using any drawable (like sf::Sprite.
I've quickly scribbled together the following example, which should show the concept rather well (and you won't run into an endless loop other than the actual main loop):
#include <SFML/Graphics.hpp>
#include <vector>
int main(int argc, char **argv) {
sf::RenderWindow window(sf::VideoMode(800, 600), L"SFML Drawing – C to clear, PageUp/PageDown to pick colors", sf::Style::Default);
// Set a specific frame rate, since we don't want to
// worry about vsync or the time between drawing iterations
window.setVerticalSyncEnabled(false);
window.setFramerateLimit(100);
// First we'll use a canvas to basically store our image
sf::RenderTexture canvas;
canvas.create(800, 600);
canvas.clear(sf::Color::White);
// Next we'll need a sprite as a helper to draw our canvas
sf::Sprite sprite;
sprite.setTexture(canvas.getTexture(), true);
// Define some colors to use
// These are all with very low alpha so we
// can (over-)draw based on how fast we move the cursor
const std::vector<sf::Color> colors = {
sf::Color(255, 0, 0, 8),
sf::Color(255, 255, 0, 8),
sf::Color(0, 255, 0, 8),
sf::Color(0, 255, 255, 8),
sf::Color(0, 0, 255, 8),
sf::Color(255, 0, 255, 8)
};
// We'll need something to actually draw
// For simplicity, I'm just drawing a circle shape
// but you could also draw a line, rectangle, or something more complex
const float brush_size = 25;
sf::CircleShape brush(brush_size, 24);
brush.setOrigin(brush_size, brush_size); // Center on the circle's center
sf::Vector2f lastPos;
bool isDrawing = false;
unsigned int color = 0;
// Apply some default color
brush.setFillColor(colors[color]);
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
switch (event.type) {
case sf::Event::Closed:
window.close();
break;
case sf::Event::KeyPressed:
switch (event.key.code) {
case sf::Keyboard::C:
// Clear our canvas
canvas.clear(sf::Color::White);
canvas.display();
break;
case sf::Keyboard::PageUp:
// Get next color
color = (color + 1) % colors.size();
// Apply it
brush.setFillColor(colors[color]);
break;
case sf::Keyboard::PageDown:
// Get previous color
color = (color - 1) % colors.size();
// Apply it
brush.setFillColor(colors[color]);
break;
}
break;
case sf::Event::Resized:
{
// Window got resized, update the view to the new size
sf::View view(window.getView());
const sf::Vector2f size(window.getSize().x, window.getSize().y);
view.setSize(size); // Set the size
view.setCenter(size / 2.f); // Set the center, moving our drawing to the top left
window.setView(view); // Apply the view
break;
}
case sf::Event::MouseButtonPressed:
// Only care for the left button
if (event.mouseButton.button == sf::Mouse::Left) {
isDrawing = true;
// Store the cursor position relative to the canvas
lastPos = window.mapPixelToCoords({event.mouseButton.x, event.mouseButton.y});
// Now let's draw our brush once, so we can
// draw dots without actually draging the mouse
brush.setPosition(lastPos);
// Draw our "brush"
canvas.draw(brush);
// Finalize the texture
canvas.display();
}
break;
case sf::Event::MouseButtonReleased:
// Only care for the left button
if (event.mouseButton.button == sf::Mouse::Left)
isDrawing = false;
break;
case sf::Event::MouseMoved:
if (isDrawing)
{
// Calculate the cursor position relative to the canvas
const sf::Vector2f newPos(window.mapPixelToCoords(sf::Vector2i(event.mouseMove.x, event.mouseMove.y)));
// I'm only using the new position here
// but you could also use `lastPos` to draw a
// line or rectangle instead
brush.setPosition(newPos);
// Draw our "brush"
canvas.draw(brush);
// Finalize the texture
canvas.display();
break;
}
}
}
// Clear the window
window.clear(sf::Color(64, 64, 64));
// Draw our canvas
window.draw(sprite);
// Show the window
window.display();
}
return 0;
}
Once running, you can start drawing using the left mouse button. C will clear the canvas and Page Up and Page Down allow you to pick a different color:
Edit: And just to mention it, in the example above, rather than drawing a circle, you can just draw a sf::VertexArray with sf::Lines and two vertices: lastPos and newPos. This way you'll always draw a continuous line. (But you'd obviously have to save/update lastPos with the value of newPos once you're done.)

Drawing Line is flickering in SDL2 C++. How To Modify My Game Loop?

My line drawing works fine, but the line is flickering.
I was thinking to change the FPS of my loop, but I think I only have to add my RenderPresent() code in another line or add some new code for rendering the line. I tried a lot of possibilities and nothing worked.
What can I do to stop the line flickering?
If you need more information please write a comment.
// Global variables
SDL_Event event; // Event object
int mouse_x = 0; // Actual Mouse Coordinate X
int mouse_y = 0; // Actual Mouse Coordinate Y
int mouse_last_x = 0; // The coordinate X by last click
int mouse_last_y = 0; // The coordinate Y by last click
bool dragged = false; // Boolean after I clicked on some place
bool running = true; // Makes the game loop run
void GameWin::loop() //Game Loop
{
while (running)
{
SDL_GetMouseState(&mouse_x, &mouse_y); // Get Mouse Coordninates
SDL_PollEvent(&event);
SDL_SetRenderDrawColor(GameWin::renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); // Draw The Background Black
update();
render();
switch (event.type)
{
case SDL_QUIT:
running = false;
break;
case SDL_MOUSEBUTTONDOWN: // Mouse Click Event
if (event.button.button == SDL_BUTTON_LEFT)
{
mouse_last_x = mouse_x; // After Left Click Save my Mouse Coordingates
mouse_last_y = mouse_y;
dragged = true;
}
break;
case SDL_MOUSEMOTION:
if (dragged)
{
SDL_SetRenderDrawColor(GameWin::renderer, 137, 255, 85, SDL_ALPHA_OPAQUE); // Set The Color Line To Green
SDL_RenderDrawLine(GameWin::renderer, mouse_last_x, mouse_last_y, mouse_x, mouse_y); // Draw The Line
SDL_RenderPresent(GameWin::renderer); // Render Line
}
break;
case SDL_MOUSEBUTTONUP:
if (dragged)
{
dragged= false;
mouse_last_x = mouse_x;
mouse_last_y = mouse_y;
}
break;
default:
break;
}
}
}
// My render method
void GameWin::render()
{
SDL_RenderClear(GameWin::renderer);
SDL_RenderPresent(GameWin::renderer);
}
First of all, you don't check if any event happened at all. You call SDL_PollEvent, and then process event - it might be ok, or it might be completely wrong as there is no guarantee there was an event in queue. SDL_PollEvent returns 0 if queue is empty - which means it didn't fill your event structure with meaningful data.
Second, don't combine event handling with drawing. Fetch all events that happened between iterations, then draw once.
Basically it should be something like:
#include "SDL.h"
// Global variables
SDL_Event event; // Event object
int mouse_x = 0; // Actual Mouse Coordinate X
int mouse_y = 0; // Actual Mouse Coordinate Y
int mouse_last_x = 0; // The coordinate X by last click
int mouse_last_y = 0; // The coordinate Y by last click
bool dragged = false; // Boolean after I clicked on some place
bool running = true; // Makes the game loop run
int main(int argc, char **argv) {
SDL_Init(SDL_INIT_VIDEO);
SDL_Window *w = NULL;
SDL_Renderer *renderer = NULL;
SDL_CreateWindowAndRenderer(640, 480, 0, &w, &renderer);
while (running)
{
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
SDL_RenderClear(renderer);
SDL_GetMouseState(&mouse_x, &mouse_y); // Get Mouse Coordninates
while(SDL_PollEvent(&event)) {
switch (event.type)
{
case SDL_QUIT:
running = false;
break;
case SDL_MOUSEBUTTONDOWN: // Mouse Click Event
if (event.button.button == SDL_BUTTON_LEFT)
{
mouse_last_x = mouse_x; // After Left Click Save my Mouse Coordingates
mouse_last_y = mouse_y;
dragged = true;
}
break;
case SDL_MOUSEBUTTONUP:
if (dragged)
{
dragged= false;
mouse_last_x = mouse_x;
mouse_last_y = mouse_y;
}
break;
default:
break;
}
}
if(!running) break;
if (dragged)
{
SDL_SetRenderDrawColor(renderer, 137, 255, 85, SDL_ALPHA_OPAQUE); // Set The Color Line To Green
SDL_RenderDrawLine(renderer, mouse_last_x, mouse_last_y, mouse_x, mouse_y); // Draw The Line
}
SDL_RenderPresent(renderer);
}
}

glutPassiveMotionFunc problems

Im kinda new to glut and opengl and I'm trying to make camera movement on mouse movement but when trying to get the mouse position on the screen I assumed the method you want to pass the x and y to should just be referenced in the glutPassiveMotionFunc() parameter. But I'm getting errors when trying to give the function the CameraMove method. I know I'm passing the method wrong but Im not sure how.
void helloGl::CameraMove(int x, int y)
{
oldMouseX = mouseX;
oldMouseY = mouseY;
// get mouse coordinates from Windows
mouseX = x;
mouseY = y;
// these lines limit the camera's range
if (mouseY < 60)
mouseY = 60;
if (mouseY > 450)
mouseY = 450;
if ((mouseX - oldMouseX) > 0) // mouse moved to the right
angle += 3.0f;`enter code here`
else if ((mouseX - oldMouseX) < 0) // mouse moved to the left
angle -= 3.0f;
}
void helloGl::mouse(int button, int state, int x, int y)
{
switch (button)
{
// When left button is pressed and released.
case GLUT_LEFT_BUTTON:
if (state == GLUT_DOWN)
{
glutIdleFunc(NULL);
}
else if (state == GLUT_UP)
{
glutIdleFunc(NULL);
}
break;
// When right button is pressed and released.
case GLUT_RIGHT_BUTTON:
if (state == GLUT_DOWN)
{
glutIdleFunc(NULL);
//fltSpeed += 0.1;
}
else if (state == GLUT_UP)
{
glutIdleFunc(NULL);
}
break;
case WM_MOUSEMOVE:
glutPassiveMotionFunc(CameraMove);
break;
default:
break;
}
}
Assuming helloGl is a class. Then the answer is, you can't. A function is not the same as a method. The thing is that glutPassiveMotionFunc() expects:
void(*func)(int x, int y)
But what you're trying to give it is:
void(helloGl::*CameraMove)(int x, int y)
In other words a thiscall. This doesn't work because a thiscall basically has an additional hidden argument in contrast to a cdecl. In all it's simplicity you can imagine your CameraMove() as:
void CameraMove(helloGl *this, int x, int y)
As you can see, that isn't the same. So the solution is then to move CameraMove() out of your helloGl class or making the method static.

How can I set gravity using this code?

I am trying to make a game and am stuck on gravity..... In the following code a rectangle stands for a player and when I press up key it moves in y-axis but when I activate gravity on it (i.e resetting its previous position) it does not animate (i.e. It does not jumps) instead it just stays in its position. I am using SFML library of C++ and that's a game development tool. Please Help!
#include <SFML/Graphics.hpp>
int main(){
sf::RenderWindow window(sf::VideoMode(800, 600, 32), "Gravity");
sf::RectangleShape rectangle;
rectangle.setSize(sf::Vector2f(100, 100));
rectangle.setFillColor(sf::Color::Black);
rectangle.setPosition(sf::Vector2f(10, 350));
while(window.isOpen())
{
sf::Event Event;
while(window.pollEvent(Event))
{
if(Event.type == sf::Event::Closed)
{
window.close();
}
}
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
{
rectangle.move(0, -1);
}
if(rectangle.getPosition().y >= 350-1)
{
rectangle.setPosition(0, 350);
}
window.display();
window.clear(sf::Color::Cyan);
window.draw(rectangle);
}
}
Theoretically your code would work, but there's one significant problem:
Your initial position is 350.
Now your "jumping code" (which will allow the player to fly indefinitely!) triggers and your position is changed to 349.
However, your code keeping the player from dropping off the screen (y >= 350-1) essentially resolves to the check y >= 349, which will be true, so your position is permanently reset to 350.
To solve this, just remove the -1 or replace the >= operator with >.
While your approach should be working (once the fix above is applied), you should rethink your strategy and store a velocity in addition to a position. I've recently written the following example code. It's far from being perfect, but it should teach you a few basics for a jump and run game (not necessarily the only way to do such things):
Allow the player to jump.
Apply gravity.
Allow the player to determine jump height based on how long he holds down a key.
#include <SFML/Graphics.hpp>
int main(int argc, char **argv) {
sf::RenderWindow window;
sf::Event event;
sf::RectangleShape box(sf::Vector2f(32, 32));
box.setFillColor(sf::Color::White);
box.setOrigin(16, 32);
box.setPosition(320, 240);
window.create(sf::VideoMode(640, 480), "Jumping Box [cursor keys + space]");
window.setFramerateLimit(60);
window.setVerticalSyncEnabled(false);
// player position
sf::Vector2f pos(320, 240);
// player velocity (per frame)
sf::Vector2f vel(0, 0);
// gravity (per frame)
sf::Vector2f gravity(0, .5f);
// max fall velocity
const float maxfall = 5;
// run acceleration
const float runacc = .25f;
// max run velocity
const float maxrun = 2.5f;
// jump acceleration
const float jumpacc = -1;
// number of frames to accelerate in
const unsigned char jumpframes = 10;
// counts the number of frames where you can still accelerate
unsigned char jumpcounter = 0;
// inputs
bool left = false;
bool right = false;
bool jump = false;
while (window.isOpen()) {
while (window.pollEvent(event)) {
switch(event.type) {
case sf::Event::KeyPressed:
case sf::Event::KeyReleased:
switch (event.key.code) {
case sf::Keyboard::Escape:
window.close();
break;
case sf::Keyboard::Left:
left = event.type == sf::Event::KeyPressed;
break;
case sf::Keyboard::Right:
right = event.type == sf::Event::KeyPressed;
break;
case sf::Keyboard::Space:
jump = event.type == sf::Event::KeyPressed;
break;
}
break;
case sf::Event::Closed:
window.close();
break;
}
}
// logic update start
// first, apply velocities
pos += vel;
// determine whether the player is on the ground
const bool onground = pos.y >= 480;
// now update the velocity by...
// ...updating gravity
vel += gravity;
// ...capping gravity
if (vel.y > maxfall)
vel.y = maxfall;
if (left) { // running to the left
vel.x -= runacc;
}
else if (right) { // running to the right
vel.x += runacc;
}
else { // not running anymore; slowing down each frame
vel.x *= 0.9;
}
// jumping
if (jump) {
if (onground) { // on the ground
vel.y += jumpacc * 2;
jumpcounter = jumpframes;
}
else if (jumpcounter > 0) { // first few frames in the air
vel.y += jumpacc;
jumpcounter--;
}
}
else { // jump key released, stop acceleration
jumpcounter = 0;
}
// check for collision with the ground
if (pos.y > 480) {
vel.y = 0;
pos.y = 480;
}
// check for collision with the left border
if (pos.x < 16) {
vel.x = 0;
pos.x = 16;
}
else if (pos.x > 624) {
vel.x = 0;
pos.x = 624;
}
// logic update end
// update the position
box.setPosition(pos);
window.clear();
window.draw(box);
window.display();
}
return 0;
}