SDL2 - Keyboard events for character movement have unexpected behavior - c++

I'm currently working on a small game and recently tried to introduce smoother movements for the player using velocity variables: when a key is pressed, the velocity is increased in the appropriate direction; and, when the same key is released, the velocity goes back to 0. However, when moving on the y axis, the character won't stop moving as it should when the key is released, which is weird considering everything works as intended when moving on the x axis.
Here's the function responsible for keyboard events:
void Game::processEvent(){
switch(Engine::Instance().event.type){
case SDL_KEYDOWN:
switch(Engine::Instance().event.key.keysym.sym){
case SDLK_LEFT: Entities[0]->vx=-0.0001f; break; //Entities is a vector filled with pointers to objects like enemies, the player etc..
case SDLK_RIGHT: Entities[0]->vx=0.0001f; break; //This is how velocity is set
case SDLK_UP: Entities[0]->vy=-0.0001f; break;
case SDLK_DOWN: Entities[0]->vy=0.0001f; break;
}
break;
case SDL_KEYUP:
switch(Engine::Instance().event.key.keysym.sym){ //Reset velocity in a direction only if the player was previously moving in that direction
case SDLK_LEFT: if(Entities[0]->vx>0) Entities[0]->vx=0.0f; break;
case SDLK_RIGHT: if(Entities[0]->vx<0) Entities[0]->vx=0.0f; break;
case SDLK_UP: if(Entities[0]->vy<0) Entities[0]->vy=0.0f; break;
case SDLK_DOWN: if (Entities[0]->vy>0) Entities[0]->vy=0.0f; break;
}
break;
default:
break;
}
}
And the function used to update objects' position:
void Entity::updatePos(){
x += vx; //Velocity is added to the sprite's coords
y += vy;
dstrect = {static_cast<int>(x*800), static_cast<int>(y*800), static_cast<int>(w*800), static_ cast<int>(h*800)}; //Workaround to make sprites' coords and dimensions always relative to the screen, not great I know XD that's why x and y are floats between 0 and 1.
}
I've been scratching my head for a while as to why vy isn't reset properly when vx is. Any idea?

EDIT: Removing the 'if' statements before setting the velocity to 0 solved the problem, guess it was unnecessary in the first place.
Which give us instead:
case SDL_KEYUP:
switch(Engine::Instance().event.key.keysym.sym){
case SDLK_LEFT: Entities[0]->vx=0; break;
case SDLK_RIGHT: Entities[0]->vx=0; break;
case SDLK_UP: Entities[0]->vy=0; break;
case SDLK_DOWN: Entities[0]->vy=0; break;
default: break;
(I can't flag this as the answer yet, sorry 'bout that)

Related

SDL C++ Moving a rectangle automatically

I'm trying to create a smaller game and I need to get a rectangle to move left to right within the window. Note that this is not the fully completed code. Below is a snippet of a created window class and the run function of it.
I can draw the rectangle, and at the moment it's also moving. However, when I'm pressing up/down it'll move to the right as well, but I want it to do it automatically. If I move the for-loop calling movement() outside of while (which I assume it should..?), the rectangle just disappears.
Another thing to note is that movement should be called through the vector pcEnhet, since I'll use it with other sprites later on.
void Spelplan::run() {
SDL_RenderClear(render);
for (PCKomponent* k : pcEnhet)
k->draw();
SDL_RenderPresent(render);
bool spela = true;
while (spela) {
SDL_Event eve;
while (SDL_PollEvent(&eve)) {
switch (eve.type) {
case SDL_QUIT:
spela = false;
break;
case SDL_KEYDOWN:
switch (eve.key.keysym.sym) {
case SDLK_DOWN:
for (PCKomponent* k : pcEnhet)
k->knappNer(eve);
break;
case SDLK_UP:
for (PCKomponent* k : pcEnhet)
k->knappUpp(eve);
break;
}//keysym switch
SDL_RenderClear(render);
for (PCKomponent* k : pcEnhet)
k->draw();
SDL_RenderPresent(render);
}//eve.type switch
for (PCKomponent* k : pcEnhet) {
k->movement();
k->draw();
}
}//while SDL_Poll
}//while spela
}
void Spelare::movement() {
area.x++;
}
void Spelare::draw() {
SDL_RenderCopy(sp->getPlan(), spelareTexture, NULL, &area);
}
Don't do the rendering in while (SDL_PollEvent(&eve)), but in while (spela). Your items have to be moved/drawn not depending on whether the input is being registered.
while (SDL_PollEvent(&eve)) {
...
}
SDL_RenderClear(render);
for (PCKomponent* k : pcEnhet) {
k->movement();
k->draw();
}
SDL_RenderPresent(render);
Remove the "pre-rendering" code you have above the bool spela = true; line, and remember to make your event loop similar to this from now on.
Note that rather than moving all the objects, you could think of them as stationary and move just the camera (viewport), with which you would calculate the absolute coordinates of the objects in their draw function.
And also, pick one from the two:
spela = false; // will render one last frame
break; // ^ will not, and does not require spela variable at all

OpenGL:How to make "Feet" go up and down?

I'm writing code that draws a polygon and gives it two feet, walks from the right until it gets to the middle, does a flip, and then lands and walks to the left. I'm having a lot of trouble figuring out how to animate his feet. All I want to do is make one go up, then come down, then the other go up, and then come down. I know all I have to do is change the Y values of his feet, but I can't figure out how.
My professor talks about key frames a lot, but wouldn't every step that my "Polyman" would take be a key frame leading to infinite amount of cases? Here is my timer function...
void TimerFunction(int value) //float plx = 7.0, ply=-3.0, linet=0.00;
{
switch(frame)
{
case 1:
dx-=0.15;
plx-=0.15; //dx=polygon, plx = one foot, pl2x = other foot
pl2x-=0.15;
if(dx<=0.0)
{
plx=0.0; //this case makes polyman walk to the middle
dx=0.0;
pl2x=0.0;
frame=2;
}
break;
case 2:
dxt+=0.05;
if (dxt<=-0.00) //this is a triangle I translate over polyman appearing as if he's opening his mouth
{
dxt=0.00;
frame=3;
}
break;
case 3:
dy+=0.2;
theta+=10.0;
thetat+=10.0;
dyt+=0.2; //this is the flip with polyman's mouth open
ply+=0.2;
pl2y+=0.2;
linet2+=10.0;
linet+=10.0;
if(dy>5.0 || theta>360.00)
{
dy=5.0;
dyt=5.0;
ply=5.0;
pl2y=5.0;
linet2=0.0;
theta=0.0;
thetat=0.0;
linet=0.0;
frame=4;
}
break;
case 4:
dy-=0.2;
dyt-=0.2;
ply-=0.2;
pl2y-=0.2;
if(dy<=-3.0) //this is polyman coming back down to earth
{
dy=-3.0;
dyt=-3.0;
ply=-3.0;
pl2y=-3.0;
frame=5;
}
break;
case 5:
dxt-=0.2;
if (dxt<-3)
{ //this is the triangle slowly translating left appearing as if he's closing his mouth
dxt-=3.0;
}
if (dxt<=-8)
{
dxt = -8;
frame = 6;
}
break;
case 6:
dx-= 0.15;
plx-= 0.15;
pl2x-=0.15; //this is polyman walking off the stage to the left
if(dx<=-8.0)
{
dx=-8.0;
plx=-8.0;
pl2x=-8.0;
}
break;
}
glutPostRedisplay();
glutTimerFunc(30, TimerFunction, 1);
}
All variables that are used in my timerfunction are global. Thanks for your time! If you need any more of my code just ask and i'll append.

SDL collision handling

I have an instance where a SDL_Rect is inside of a big SDL_Rect and i need it to make it so it cannot leave that rect but can still move. The movement of the little rect needs to be like board game movement where you click the button once and it moves a certain cords here is my code:
if( event.type == SDL_KEYDOWN) {
switch( event.key.keysym.sym ) {
case SDLK_UP:
yVel -= 10;
if (!check_collision(box,Cont))
{
std::cout<<"in the water"<<std::endl;
box.y -= yVel - 10;
}
break;
case SDLK_DOWN:
if (!check_collision(box,Cont))
{
std::cout<<"in the water"<<std::endl;
box.y -= yVel + 20;
}
else
{
yVel += 10;
}
break;
case SDLK_LEFT:
xVel -= 10;
if (!check_collision(box,Cont))
{
std::cout<<"in the water"<<std::endl;
}
break;
case SDLK_RIGHT:
xVel += 10;
if (!check_collision(box,Cont))
{
std::cout<<"in the water"<<std::endl;
}
break;
case SDLK_1:
return 2;
break;
}
}
You have inconsistencies in your SDLK_DOWN and the rest of your inputs - no velocity change happens if there are no collisions.
Your input code is changing coordinates, it shouldn't be like that. Make your input code manage the "intent", and have a game loop apply velocity to the object and to the collision detection.
Also, your collision checks should check if I'm at position and I move of delta, will I collide? If I do collide, what's the collision reaction? Do you want the entity to "slide" on collisions or simply stop as soon as a collision is detected?
Considering you are doing a board game, you shouldn't even have to do collision detection. Represent your board as a graph of possible positions your unit can be, translate mouse clicks into board coordinates and calculate the shortest path (use the A* algorithm) between your current position and your destination; invalid clicks (out of bounds) shouldn't translate into movement commands.

SDL poll event inverting w with z and viceversa

I'm doing a simple camera movement with the WASD keys:
switch (k) {
case SDLK_w:
this->up = true;
break;
case SDLK_s:
this->down = true;
break;
case SDLK_a:
this->left = true;
break;
case SDLK_d:
this->right = true;
break;
default:
break;
}
It's pretty self explanatory. But when I press w it simply doesn't detect that button pressing. If I press a or d or s it works. The cool thing about it is that if I just change SDLK_w to any other button (let's say SDLK_q) keeping the same exact code, it just works. It's not an issue of how I handle this->up because even if I print something on the screen inside the case SDLK_w: and I press w it doesn't print anything.
PS: obviously my w key is not broken otherwise I would have had trouble writing this post doWn.
What's wrong with w?
It could depend on the type of keyboard you are using, actually your W and Z key could be inversed depending on your keyboard layout. I recommand you try ALT+SHIFT.
Do not use SDLK_w for this as it gives you whatever key actually gives you a W letter, whereas what you want is the key that you'd expect at the W position regardless of the layout, you do this by using scancodes, in this case SDL_SCANCODE_W.

180 to -180 motion in opengl c++?

i'm using a glutTimerFunc(...) to achieve motion of robot arm, my problem is left side 0 degree to 90 is easily done,
when i try 0 to -90 degree, the arm is not stoping? i tried various methods, but all falied, can you suggest better options?
here's my timer function, void myTimerFunc(int var)
{
switch(var)
{
case 1: if(shoulder>=90)
stop=1;
if(!stop)
{
shoulder = (shoulder + 5);
glutPostRedisplay();
glutTimerFunc(100,myTimerFunc,1);
break;
}
if(shoulder<=0)
stop1=1;
if(!stop1)
{
shoulder = (shoulder - 5);
glutPostRedisplay();
glutTimerFunc(100,myTimerFunc,1);
break;
}
case 2: if(shoulder>=360)
stop2=1;
if(!stop2)
{
shoulder = (shoulder - 5);
glutPostRedisplay();
glutTimerFunc(100,myTimerFunc,2);
break;
}
// here robot arm is not stopping...........
if(shoulder<=270)
stop2 = 0;
stop3 = 1;
if(!stop3)
{
shoulder = (shoulder + 5);
glutPostRedisplay();
glutTimerFunc(100,myTimerFunc,2);
break;
}
default:break;
}
}
i'm calling this from keyboard function.......
void keyboard (unsigned char key, int x, int y)
{
switch (key)
{
case 's':
glutTimerFunc(100,myTimerFunc,1);
break;
glutTimerFunc(100,myTimerFunc,2);
break;
Looks to me like you're confused about how to treat angles. Are you using a [0, 360] scale for a full circle or [-180, +180]? I see a check against 270 in your code, but your prose mentions -90. Yes, they're "the same", but if it's not working perhaps some confusion has crept into your code.
If you're going with [-180, +180], with zero degrees as the neutral position for your robot arm, perhaps you can take advantage of symmetry and use the absolute value function. You only have to code it once that way. If it's successful for [0, +90] it'll also work for [-90, 0] as well.
It also seems to me that you're not taking advantage of object-oriented thinking. This method has motion and timing and graphical refresh all in the same method. Writing smaller methods and unit testing each one to be sure that it works is a better approach. It's called "decomposition". The moment you say "it's a big method", it suggests to me that perhaps it's too big and needs to be refactored.