I have some trouble with events in SFML. I am making a turnbased game, when the mouse has moved or left mouse button is clicked i check whos turn it is and then spawn a projectile at that objects position, when the projectile has collided with either terrain or opponent it gets destroyed and the turn is changed.
The behaviour is not what i am expecting though. When clicking shoot the projectile sometimes wont spawn at all (and it changes the turn immedeately). I have disabled all collisions so it cant be that. Im 90% sure that the issue is with how i handle events, so id really appretiate input on how i can make it better.
Of what ive learned is that you should not execute the functions in the while poll event, its only for registering what has happend most recently,so i put them outside instead. This does not solve my problem though...
sf::Event event;
sf::Vector2i mousePos;
while (_window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
_window.close();
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape)) {
_window.close();
}
if(event.type == sf::Event::MouseMoved) { // <- this is how you check to see if an event
mousePos = sf::Mouse::getPosition();
moved = true;
}
if(event.type == sf::Event::MouseButtonPressed) { // <- this is how you check to see if an event
mousePressed = true;
}
}
if (mousePressed && tank1Turn)
{
sf::Vector2f spawnPos = _t1->getPos() + sf::Vector2f(0, -150);
sf::Vector2f initVel = _t1->getInitVel();
cout << endl;
_p = new Projectile(spawnPos, initVel);
tank1Turn = false;
tank1Firing = true;
mousePressed = false;
}
if (mousePressed && tank2Turn) {
sf::Vector2f spawnPos = _t2->getPos()+sf::Vector2f(0,-150);
sf::Vector2f initVel = _t2->getInitVel();
_p = new Projectile(spawnPos, initVel);
tank2Turn = false;
tank2Firing = true;
mousePressed = false;
}
if (tank1Turn && moved) {
_t1->aim(mousePos);
moved = false;
mousePressed = false;
}
if (tank2Turn && moved) {
_t2->aim(mousePos);
moved = false;
mousePressed = false;
}
}
Consider this a comment. This isn't an answer (I don't have enough reputation to comment so I have to make this an answer) but here are some things I noticed:
You should either replace the ifs in your event loop with if-elses, or use a switch
sf::Keyboard::isKeyPressed is for real time input (put it inside your game update loop). In your event loop it should be sf::Event::KeyPressed and check which key it was with evt.key.code
You aren't checking which mouse button was pressed. Do it with evt.mouseButton.button (sf::Mouse::Button::Left for the left mouse)
Of what ive learned is that you should not execute the functions in the while poll event, its only for registering what has happend most recently,so i put them outside instead.
Where did you read that? I don't think that's true
There shouldn't just be an array of ifs below the event loop. Code should be structured better
Related
I am trying to make a program where you are allowed to select between an option of shapes, and then drawing it. To allow for multiple shapes I created a vector of a class which creates shapes (Shapes are set up with the chosen function). My problem is the mouse click is too long, so it assigns it to everything in the vector, so you can't create a new shape. Is there a problem in my logic, or is there a problem in the code?
Here is my attempt:
for (auto& it : onCanvas) {
if (Mouse::isButtonPressed(Mouse::Left)) {
if (mousepointer.getGlobalBounds().intersects(circleOption.getGlobalBounds())) {
it.chosen(circles);
}
if (mousepointer.getGlobalBounds().intersects(rectOption.getGlobalBounds())) {
it.chosen(rectangle);
}
if (mousepointer.getGlobalBounds().intersects(triOption.getGlobalBounds())) {
it.chosen(triangles);
}
if (mousepointer.getGlobalBounds().intersects(it.shape.getGlobalBounds()) || it.dragging) {
it.shape.setPosition(mousepointer.getPosition());
it.dragging = true;
}
}
if (!Mouse::isButtonPressed) {
it.dragging = false;
}
win.draw(it.shape);
}
Your source-code is a bit incomplete (what is onCanvas and mousepointer). But I guess the problem is that this snippet is called multiple times while your mouse is clicked. To avoid that you can do two thing.
In the first solution you use events, so you only add shapes when the state of the mousebutton changes (you can additionally listen to the MouseButtonReleased to simulate a full click):
if (event.type == sf::Event::MouseButtonPressed)
{
if (event.mouseButton.button == sf::Mouse::Left)
{
// Hit Detection
}
}
or second solution you remember the last state of the button (probably do the mouse check once outside of the for loop):
bool mouse_was_up = true;
if (mouse_was_up && Mouse::isButtonPressed(Mouse::Left)) {
mouse_was_up = false;
for (auto& it : onCanvas) {
// Hit Detection
}
}
else if (!Mouse::isButtonPressed(Mouse::Left))
mouse_was_up = true;
I would rather stick to the first solution because when your click is too short and your gameloop is in another part of the game logic, you can miss the click.
I'm new to SFML and I have trouble finding a solution to checking if a key is pressed during one frame. The problem I've been facing is the fact that with the Keyboard and Mouse classes, it seems impossible to use a system where one first checks for the current input state before any Update() call of objects and then after all Update() you get a previous input state for the next frame so that one can do the following:
bool Entity::KeyboardCheckPressed(sf::Keyboard::Key aKey)
{
//this part doesn't work
if (KeyboardState->isKeyPressed(aKey) and !PreviousKeyboardState->isKeyPressed(aKey))
{
return true;
}
return false;
}
But this doesn't seem to work with SFML, and other sources tell me that I'm suppose to use the Event class with its type and key.codelike the following example:
bool Entity::KeyboardCheckPressed(sf::Keyboard::Key aKey)
{
if (Event->type == sf::Event::KeyPressed)
{
if (Event->key.code == aKey)
{
return true;
}
}
return false;
}
But this results in the sf::Event::KeyPressed doing the same as KeyboardState->isKeyPressed(aKey), so then I tried the method where you set key repeat to false: window.setKeyRepeatEnabled(false);with no results what so ever. I also found out that the sf::Event::KeyPressed works only as intended inside of this part in the main.cpp:
while (window.pollEvent(event))
{
}
The problem with this is that I want to handle Input inside of my Entity objects' Update()function, and I can't put the whole Update loop inside of the while (window.pollEvent(event)). So here I am, struggling to find a solution. Any help is appreciated.
In general, if you have a thing which you can check the current state of, and you want to check if that state changed between frames, you simply use a variable, declared outside the application loop, to store the previous state, and compare it to the current state.
bool previousState = checkState();
while (true) {
// your main application loop
bool newState = checkState();
if (newState == true && previousState == false) {
doThingy("the thing went from false to true");
} else if (newState == false && previousState == true) {
doThingy("the thing went from true to false");
} else {
doThingy("no change in the thing");
}
// this is done unconditionally every frame
previousState = newState;
}
I have created a drawGrid() function that draws a squared grid along my X and Y axis, which works fine. I have then created a menu() function (called in the main()), that toggles the grid on and off, here's the code for that:
void menu(int item)
{
switch (item)
{
case MENU_SWITCH_OFF_GRID:
{
if (gridActive == true)
{
gridActive = true;
}
}
break;
case MENU_SWITCH_ON_GRID:
{
if (gridActive == true)
{
gridActive = false;
}
}
break;
default:
{ /* Nothing */ }
break;
}
glutPostRedisplay();
return;
}
}
The menu switch works fine, as I have created a global variable called gridActive without a true or false value so it doesn't reset each time, that way it can be accessed in my display() function like so:
if (gridActive != true)
{
drawGrid();
gridActive = true;
}
All of this works just fine.
What's my issue?
My issue is, whenever I click the left mouse button, my grid disappears, which I don't want. So I've made a mouse() function like this:
case GLUT_LEFT_BUTTON: if (state == GLUT_DOWN)
{
exit(0); // this has been added to see if
// my program will exit!
}
break;
To test if my program exits when I click the left mouse, and it does.
So instead of using exit(0); what code can i put here so that my grid doesn't disappear when I click the left mouse button? Or is the issue beyond that?
UPDATE:
Here's the mouse function:
void mouse(int button, int state, int x, int y)
{
// these have simply been set-up for me to use
// in the future
switch (button)
{
case GLUT_LEFT_BUTTON: if (state == GLUT_DOWN)
{
}
break;
case GLUT_RIGHT_BUTTON: if (state == GLUT_DOWN)
{
}
break;
default: break;
}
}
Based on your code:
if (gridActive != true)
{
drawGrid();
gridActive = true;
}
You only draw the grid when gridActive is false. However, after every time you draw it, you set gridActive=true which will then stop it from being drawn.
Without more of your code, it's impossible to tell exactly what's going on, but these lines may not be doing what you think they are, and that may be causing some issues.
This never does anything.
if (gridActive == true)
{
gridActive = true;
}
This:
if (gridActive == true)
{
gridActive = false;
}
is the same as:
gridActive = false;
In order to tell what's going on, though, we need to know what happens when you click your mouse button when the exit call isn't there, but you didn't post that code yet.
Also, i don't quite know what you mean by:
I have created a global variable called gridActive without a true or false value so it doesn't reset each time
but it sounds like you made an uninitialized global variable and expect that it has some specific meaning because it's uninitialized?
The original code i posted has been edited, as it seems it caused confusion. I know it was wrong but i only wanted to use it as an example. Code i want to work with is this one:
https://github.com/SFML/SFML/wiki/Source:-AnimatedSprite
You can just focus on this part:
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape)
window.close();
}
sf::Time frameTime = frameClock.restart();
// if a key was pressed set the correct animation and move correctly
sf::Vector2f movement(0.f, 0.f);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
{
currentAnimation = &walkingAnimationUp;
movement.y -= speed;
noKeyWasPressed = false;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
{
currentAnimation = &walkingAnimationDown;
movement.y += speed;
noKeyWasPressed = false;
}
Let´s assume that instead of
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
i want this program to act from parameters received from outside this code.
This would be the scheme i want to follow:
From my C++ code (let´s call it Origin) i call this programme (lets call it AnimatedSprite.exe) with this command:
ShellExecute(NULL,NULL,"C:\\AnimatedSprite.exe",NULL ,NULL,SW_SHOWDEFAULT);
This will open the AnimatedSprite.exe without sending any command.
Now i want to keep it open and send him commands, like
ShellExecute(NULL,NULL,"C:\\Try.exe",MyCommandLetter.c_str() ,NULL,SW_SHOWDEFAULT);
So that when my already opened AnimatedSprite.exe receives "MyCommandLetter" as argv (yes, i know i have to declare argc and argv in main, i have to modify the original code)
it does something like:
if (argv[1]=='a')
{
currentAnimation = &walkingAnimationUp;
movement.y -= speed;
noKeyWasPressed = false;
}
Hope it got more clear.
It does not matter if i have to use other function which is not ShellExecute, as long as i can understand how it works (with this example it would be OK).
Thanks in advance.
Ok, as I understand the question, you want the called program react on multiple arguments. In that case you should build your main not as single "if else" statement, but rather a loop over arguments with "switch case" statement, processing all given arguments. Look for example here
I'm writing a game in C++ using the Windows API which has a Splash Screen at the start, before gameplay begins, and can be paused.
I store the state of the game in an enum, game_state {PAUSED, PLAYING, SPLASHSCREEN}, and rely on Keyboard input to control the game.
The game was working properly, switching between paused and playing, but when I tried to add a splashscreen to begin the game on, the pause functionality stopped working, and I'm not sure why...
if(Keyboard.GetKey(VK_RETURN) && game_state == SPLASHSCREEN)
{
game_state = PLAYING;
Keyboard.SetKey(VK_RETURN, false);
}
if(Keyboard.GetKey(VK_RETURN))
{
if(game_state == PAUSED)
{
game_state = PLAYING;
}
else
{
game_state = PAUSED;
}
Keyboard.SetKey(VK_RETURN, false);
}
//If Paused, go to Pause Screen
if(game_state == PAUSED)
{
pauseScreen();
}
//If Splash Screen, go to Splash Screen
if(game_state == SPLASHSCREEN)
{
splashScreen();
}
//If not paused, do game processing
if(game_state == PLAYING)
{
gamePlay();
}
GetKey() returns true if the key is held down.
game_state is an enum global containing the current state of the game.
SetKey() sets the specified key as down (true) or up (false)
Oh, and all splashScreen() pauseScreen() and gamePlay() do are display sprites representing each state (at the moment)
SetKey
void Keyboard::SetKey(WPARAM key, bool key_down)
{
if(key_down)
{
m_keys[key] = true;
}
else
{
m_keys[key] = false;
}
}
GetKey
bool Keyboard::GetKey(WPARAM key)
{
if(m_keys[key])
{
m_keys[key] = false;
return true;
}
else
{
return false;
}
}
Remove m_keys[key] = false; from the Keyboard::GetKey method. As it is being set to false in the first check, it prevents the next check from seeing that it was pressed.
Calling GetKey() sets the key as released - since it checks to see if the key is pressed and the state is splashscreen before checking anything else - the key will always be released when checking it again.
Alter GetKey or alter the way the code is written.
My best guess is that your splash screen has the focus and it will take over the message loop, then you don't get the key event. Just a guess, can't really know without seeing the window creation/registration code of your splash and main windows.