I'm controlling a sprite on screen using glutkeyboardfunc and keyDown's.
Eg. When I implement the following code the sprite successfully jumps up:
if(keyDown[119]){//w key = up
sprite_y +=40.0f;
spriteT=spriteJ;
}
Whilst in the air, the sprite returns using this code:
if(sprite_y>0){ gravity-=10*delta; sprite_y+=gravity; }
else{ gravity=0; }
The problem I'm having is, if you keep your finger down on the w key, the sprite jumps forever and keeps going up.
I understand this is because I'm using the keyDown method so I tried this:
if(keyDown[119]){//w key = up
sprite_y +=40.0f;
spriteT=spriteJ;
keyDown[119]=0;//reset keyDown to keyUp
}
But I had no luck...
Is there a way I can limit the keyDown to a certain time period or only act as if it was pressed once? Thanks.
edit I dont quite understand how this question is worthy of a vote down. Did you even read it?
You can use a variable to represent the previous state of the key, and then see if it has changed.
For example:
//Initialise a bool called keyWasDown to false
if (keyDown[119]){
if (!keyWasDown){
//Do jumping code
keyWasDown = true;
}
else {
keyWasDown = false;
}
Related
I have been looking for ways to detect when a key has been pressed but only once, but the only things I can find are GetAsyncKeyState and GetKeyState. I am making a rhythm game for fun and I use a while(true) statement to get everything done. Is there anyway to detect when a key is pressed once? (I'm also using GLFW if that helps)
You could store the state of the previous key presses, if it was not pressed in the last frame and is now, that would mean that the key is held down.
Here's an example with the LMB:
bool previousMouseState = false;
if (GetKeyState(VK_LBUTTON) < 0) {
if (!prevMouseState) {
previousMouseState = true;
//Mouse clicked.
}
} else previousMouseState = false;
I have a cube that rotates by itself and one that rotates when a key is being pressed. The problem is that the one that rotates by itself always rotates faster(much faster) than the one that rotates only when a key is being pressed.
while(!quit())
{
handleInput();
update();
render();
}
When I put my rotation here:
update()
{
cube.rotation += 0.09f;
}
The cube rotates fast.
But when i put it here:
handleinput()
{
while(pollevent(&event))
{
if(event.type == keydown)
{
if(event.key.symbol == KEY_r )
{
cube.rotation += 0.09f;
}
}
}
It is much slower. Though I assume that both should be getting called once each loop when the key is being held down.
And I don't know what this question must be tagged.
Your keydown event is simply fired less frequently than your game logic is updated.
A common solution is to keep track of the keyboard's state.
SDL way
I suggest storing the keyboard state as an array of booleans, which size is the number of keys you will check against, e.g. bool[SDL_KEYLAST].
So you'll have to modify it whenever an event is fired. Finally, at each loop, check for the saved keyboard state.
Example code:
handleInput()
{
while(pollEvent(&ev))
{
if(event.type = keydown)
ks[event.key.symbol] = true;
else if(event.type = keyup)
ks[event.key.symbol] = false;
}
}
update()
{
if(ks[KEY_R]) cube.rotation += 0.05f;
}
SDL2 way
const Uint8* ks = SDL_GetKeyboardState(NULL).
This function returns the keyboard state array, which is not a bool[] but a Uint8[] containing 0/1 values (so you can still check on false/true values).
But also, you'll have to check against SDL_Scancode index values if you want to use that array, which are listed here: https://wiki.libsdl.org/SDL_Scancode
handleinput()
{
//pump events so that SDL can update the keyboard state array...
}
update()
{
const Uint8* ks = SDL_GetKeyboardState(NULL);
if(ks[SDL_SCANCODE_R]) cube.rotation += 0.05f;
}
Reference: https://wiki.libsdl.org/SDL_GetKeyboardState
dmg is likely correct.
Pollevent doesn't just iterate over a list of keys currently pressed, you're iterating over a list of events to handle, and it only gets updated at your repeat rate. You probably want to internally handle the state of the button, and toggle it on and off with the events, handling the rotation in the update (better separation of functionality, anyway).
I am making a 2D game where we are supposed to control the character through arrow keys.
if((win.GetInput().IsKeyDown(sf::Key::Down)))
{
y = y + Speed;
}
if((win.GetInput().IsKeyDown(sf::Key::Left)))
{
x = x - Speed;
}
I have set Speed to 10. And then a i use the Sprite.SetPosition(x,y) to actually animate my character.
Everything works fine. But the problem is whenever i press an arrow key, the character moves for 1/2 seconds, stops for about 1/2 seconds and then moves again smoothly. This happens whenever i press any arrow key.
And yes, i am using a while loop on top to handle multiple events simultaneously.
I hope my question was clear enough. Please help me out!
Thanks.
I think you're not handling events the right way. What you're doing here is checking on each event (which could be keyboard input or not) whether the sf::Key::Down key is pressed (and the same for sf::Key::Left).
Firstly, it's not effective, because you don't get the result you want.
Secondly, it performs useless checks admitting that the events could be mouse moves, mouse clicks or anything else : checking whether those keys are pressed in such cases is pointless for your program.
I can't see your whole code, but you should try something of this taste as your main loop :
bool isMovingLeft = false;
bool isMovingDown = false;
sf::Event event;
while (win.IsOpen())
{
// While window catches events...
while(win.GetEvent(event))
{
// If the caught event is a click on the close button, close the window
if (event.Type == sf::Event::Closed)
win.Close();
// If it's a key press, check which key and move consequently
else if (event.Type == sf::Event::KeyPressed)
{
if(event.Key.Code == sf::Key::Left)
isMovingLeft = true;
else if(event.Key.Code == sf::Key::Down)
isMovingDown = true;
}
// If it's a key release, stop moving in the following direction
else if (event.Type == sf::Event::KeyReleased)
{
if(event.Key.Code == sf::Key::Left)
isMovingLeft = false;
else if(event.Key.Code == sf::Key::Down)
isMovingDown = false;
}
}
// Now that we have caught events, we move the lil' thing if we need to.
if(isMovingLeft)
x = x - SPEED;
if(isMovingDown)
y = y - SPEED;
win.Clear();
// Draw things on the screen...
win.Display();
}
In this code, the whole process is split in two parts :
We first intercept the user input to see if we need to change the moving state of the thing.
Then, once every event has been caught and thoroughly analyzed, we move the thing if it has to. It is done through two bools (that you may need to increase to four if you want a four-direction control. If you want to handle diagonal directions, it would be wiser to use an enum than eight bool, which begins to be rather memory-consuming for such a simple task.)
Note : you will maybe notice that I changed "Speed" to "SPEED". I can't see if it was a define, a const var or simply a var from the code you have given, but the best option would be one of the two first ones. I prefer using #define for such things, to make constants easily reachable (as they're put in the preprocessor) and the fully capped writing make it more differentiable from classic vars once in the code. But that's just coding style we're talking of here :)
I know that in order to kill invaders in C++, I need to make a collider.
However, nothing will ever kill the invaders in that game.
Here's the code in the header:
bool DoCollision(float Xbpos, float Ybpos, int BulWidth, int BulHeight, float Xipos, float Yipos, int InvWidth, int InvHeight);
This is the function I'm initializing:
bool Game::DoCollision(float Xbpos, float Ybpos, int BulWidth, int BulHeight, float Xipos, float Yipos, int InvWidth, int InvHeight) {
if (Xbpos+BulWidth < Xipos || Xbpos > Xipos+InvWidth) return false;
if (Ybpos+BulHeight < Yipos || Ybpos > Yipos+InvHeight) return false;
return true;
}
And this is what happens if somebody presses the space key:
if (code == 57) { //Space
myKeyInvader.MeBullet.Active = true;
myKeyInvader.MeBullet.Xpos = myKeyInvader.Xpos + 10;
myKeyInvader.MeBullet.Ypos = myKeyInvader.Ypos - 10;
myKeyInvader.MeBullet.yvuel = 0.2;
myKeyInvader.MeBullet.BulletP->CopyTo(m_Screen,myKeyInvader.Xpos,myKeyInvader.Ypos);
if (DoCollision(Invaders[counter].MyBullet.Xbpos,Invaders[counter].MyBullet.Ybpos,Invaders[counter].MyBullet.BulWidth,
Invaders[counter].MyBullet.BulHeight,Invaders[counter].Xipos,Invaders[counter].Yipos,Invaders[counter].InvWidth,Invaders[counter].InvHeight)) {
//myKeyInvader.Ypos = 100;
Invaders[counter].Active = false;
printf("Collide!\n");
}
}
Does anybody know what's going wrong?
The problem isn't C++. The problem is how you are using it. The only way you'll get a kill with your code as written is if the invader is right on top of you. But that's too late. The alien invader has already killed you.
What you need to do is make those bullets into objects that you propagate over time, just like your invaders are objects that you propagate over time. The response to the user pressing a space key should be to add a new instance of a bullet to the set of active bullets. Each of those active bullets has a position that changes with time. On each time step, you should advance the states of the active invaders per the rules that dictate how invaders move and advance the states of the active bullets per the rules that dictate how bullets move. Remove bullets when they reach the top of the screen, and if an alien invader reaches the bottom of the screen, game over.
After propagating, removing off-screen bullets, and checking for game over, you want to check for collisions between each of the N bullets with each of the M invaders. When a collision is detected, remove the bullet from the set of active bullets and delete the alien invader from the set of active invaders. And of course you'll want some nifty graphics to show the user that another alien bit the dust.
Aside: Being an NxM problem, this check might be the biggest drain on CPU usage. You can speed this up with some simple heuristics.
You could manage the collections of alien invaders and bullets yourself, carefully using new and delete so as to prevent your invaders and bullets from killing your program with a memory leak. You don't have to do this. C++ gives you some nifty tools to manage these collections. Use one of the C++ standard library collections instead of rolling your own collection. For example, std::vector<AlienInvader> invaders; or std::list<AlienInvader> invaders, and the same for bullets. You'll be deleting from the middle a lot, which suggests that std::list or std::deque might be more appropriate than std::vector here.
You test the collision for the fired item just when they are created
Shouldn't be the test collision done in the main loop for each existing item at each frame ?
Don't worry, C++ has got all you need to kill invaders :)))
It's not easy to give advice based on so little code, but here the only logical error seems to be you test for collision only when space is pressed; you should test for it in an outside loop probably:
if (code == 57) { //Space
myKeyInvader.MeBullet.Active = true;
myKeyInvader.MeBullet.Xpos = myKeyInvader.Xpos + 10;
myKeyInvader.MeBullet.Ypos = myKeyInvader.Ypos - 10;
myKeyInvader.MeBullet.yvuel = 0.2;
myKeyInvader.MeBullet.BulletP->CopyTo(m_Screen,myKeyInvader.Xpos,myKeyInvader.Ypos);
}
From a logical point of view, pressing Space should fire a bullet: the starting position for the bullet is set, and so is its speed on the Y axis (so that it goes up).
The code that check for collision should go outside of this if block. In fact, this block of code is executed only if you're still pressing space -that is: still firing-. Should collision be checked only if you're "still firing"? Do the fact that you fired a bullet and started waiting for it to destroy the invader interfere in some way with the fact that this bullet can reach the invader and, indeed, destroy it? Of course not!
if (DoCollision(Invaders[counter].MyBullet.Xbpos,Invaders[counter].MyBullet.Ybpos,Invaders[counter].MyBullet.BulWidth,
Invaders[counter].MyBullet.BulHeight,Invaders[counter].Xipos,Invaders[counter].Yipos,Invaders[counter].InvWidth,Invaders[counter].InvHeight)) {
//myKeyInvader.Ypos = 100;
Invaders[counter].Active = false;
printf("Collide!\n");
}
You want collision to be checked in an outside loop, the same that probably also contains the checks for key presses. In this way, even if you're just looking at the screen and waiting, the program keeps testing the condition and, when it's fulfilled, code associated with the event of collision is executed (that is: an invader is "inactivated").
You say //Space , is that what it is or should it be 32 (if ASCII) instead of 57? Does the program flow into the if==57 block?
Your code looks fine, but you need two loops around the collision checker: one for checking all invaders (not just one of them) and another one to check at every bullet position along its trajectory, not just the moment when it leaves the gun.
I will assume we have an auxiliary function that moves the bullet and returns whether it is still inside the screen:
bool BulletIsInScreen();
Then we can write the loops:
if (code == 57) { // Space
while (BulletIsInScreen()) {
for (size_t i = 0; i < counter; ++i) { // counter is the number of invaders,
// according to your comment to your own answer
myKeyInvader.MeBullet.Active = true;
myKeyInvader.MeBullet.Xpos = myKeyInvader.Xpos + 10;
myKeyInvader.MeBullet.Ypos = myKeyInvader.Ypos - 10;
myKeyInvader.MeBullet.yvuel = 0.2;
myKeyInvader.MeBullet.BulletP->CopyTo(m_Screen,myKeyInvader.Xpos,myKeyInvader.Ypos);
if (DoCollision(Invaders[i].MyBullet.Xbpos, Invaders[i].MyBullet.Ybpos,
Invaders[i].MyBullet.BulWidth, Invaders[i].MyBullet.BulHeight,
Invaders[i].Xipos, Invaders[i].Yipos,
Invaders[i].InvWidth, Invaders[i].InvHeight)) {
//myKeyInvader.Ypos = 100;
Invaders[i].Active = false;
printf("Collide!\n");
}
}
}
}
Now this should work as expected.
I'm fairly new to OpenGL, and I am writing a simple game in 2D, for fun. However, I ran into an issue I am having a hard time wrapping my head around.
It seems that whenever my keyboardUpFunc is called, that not only the key that has actually come up sent to the function, but every single key currently being pressed as well.
I'm using a simple key buffer to manage the keys, keyUp marks the key as up and is only called in this function. keyDown is called in my keyboardFunc. isDown returns a boolean value of whether or not the key is pressed. Take this code for example:
#include <iostream>
...
void keyboardUp(unsigned char key, int x, int y)
{
keys.keyUp(key);
if (keys.isDown('s') == false)
{
std::cout << "It's resetting s as well!" << std::endl;
}
// reset acceleration here, for each key
if ( (key == 'w') || (key == 's') )
{
yStep = 0.1;
}
if ( (key == 'a') || (key == 'd') )
{
xStep = 0.1;
}
std::cout << key << " is now up." << std::endl;
}
If you run this code, if you for example, hold S and D, then release the D key, you will note that S has been marked as up too, since this is the only location keyUp is being called.
Assuming my keyBuffer code is working correctly (and it is, but let me know if you want me to post it...), is there any way to get around this? Where if you were holding a key, and then pressed another key, the application would go back to what you were doing when you were just holding the original key? Instead of marking both as up? Or is this not feasible with GLUT?
Not very clear what is going wrong.. But where/how exactly are you calling this function ?? Directly in the Main Game loop, or are you checking certain conditions in an 'update' function. I ask because you need to check for input every run of the infinite loop, and if you are using a boolean to determine if a key is down, you should essentially reset it after its corresponding action has been performed. Anyway, just my 2 cents.
I changed my implementation of the keyboard buffer, and what I was describing above now works. The difference is that before I was using a vector to pile on the keys that were being pressed, whereas now, I am using a fixed-size array of boolean values.
Apparently, a vector implementation of a key buffer won't work properly with GLUT.