0
\$\begingroup\$

I'm trying to program a simple space shooter and my goal right now is that when the player presses down and holds a key, the ship accelerates to a max velocity.

I have that somewhat; the problem is that when starting the game I can press the directional/arrow keys on my keyboard about 4 or 5 times (in any direction) and get the desired result before further key presses have no effect on the velocity and the ship just floats.

It's like the keyboard gets disabled. Here are the relevant parts of the program:

void Ship::handleEvent( SDL_Event &e ) {
290 
291         const Uint8* keystate = SDL_GetKeyboardState(NULL);
292 
293         bool keydown = false;
294 
295         if ( e.type == SDL_KEYDOWN )
296                 keydown = true;
297 
298         if ( keydown ) {
299 
300 //              std::cout << std::boolalpha << "keydown: " << keydown << "\n" << "keyup: " << keyup << "\n";
301 
302 //              std::cout << holdTime << "\n";
303 
304                 if ( keystate[SDL_SCANCODE_UP] && std::abs(mVelY) <= SHIP_MAX_VELOCITY ) {
305 
306                         if (mVelY <= SHIP_MAX_VELOCITY) {
307                                 mVelY -= SHIP_ACCELERATION;
308                                 direction = UP;
309                         }
310                 }
311 
312                 if ( keystate[SDL_SCANCODE_DOWN] && std::abs(mVelY) <= SHIP_MAX_VELOCITY ) {
313 
314                         if (mVelY <= SHIP_MAX_VELOCITY) {
315                                 mVelY += SHIP_ACCELERATION;
316                                 direction = DOWN;
317                         }
318                 }
319 
320                 if ( keystate[SDL_SCANCODE_LEFT] && std::abs(mVelX) <= SHIP_MAX_VELOCITY ) {
321 
322                         if (mVelX <= SHIP_MAX_VELOCITY) {
323                                 mVelX -= SHIP_ACCELERATION;
324                                 direction = LEFT;
325                         }
326                 }
327 
328                 if ( keystate[SDL_SCANCODE_RIGHT] ) {
329 
330                         if (mVelX <= SHIP_MAX_VELOCITY) {
331                                 mVelX += SHIP_ACCELERATION;
332                                 direction = RIGHT;
333                         }
334                 }
335 
336         }
337 
338 // std::cout << std::boolalpha << "keydown: " << keydown << "\n" << "keyup: " << keyup << "\n";
339 
340 }

357 void Ship::move() {
358 
359                 mPosX += mVelX;
360                 shipCollBox.x = mPosX;
361 
362                 if ( mPosX < 0 || mPosX + SHIP_WIDTH > LEVEL_WIDTH || checkCollision( shipCollBox, wall1 ) || checkCollision( ship    CollBox, wall2) || checkCollision( shipCollBox, wall3 ) ) {
363                         mPosX -= mVelX;
364                         shipCollBox.x = mPosX;
365                 }
366 
367                 mPosY += mVelY;
368                 shipCollBox.y = mPosY;
369                 std::cout << "mVelY: " << mVelY << "\n" << "mPosY: " << mPosY << "\n";
370 //              SDL_Delay(100);
371         if ( mPosY < 0 || mPosY + SHIP_HEIGHT > LEVEL_HEIGHT || ( checkCollision( shipCollBox, wall1 ) || checkCollision( shipColl    Box, wall2) || checkCollision( shipCollBox, wall3 ) ) ) {
372                         mPosY -= mVelY;
373                         shipCollBox.y = mPosY;
374                 }
375 }
379 void Ship::render( int camX, int camY ) {
380 
381         switch (direction) {
382 
383                 case UP: shipTextureUp.render( mPosX - camX, mPosY - camY ); break;
384                 case DOWN: shipTextureDown.render(mPosX - camX, mPosY - camY ); break;
385                 case LEFT: shipTextureLeft.render(mPosX - camX, mPosY - camY ); break;
386                 case RIGHT: shipTextureRight.render(mPosX - camX, mPosY - camY ); break;
387         }
388 }
445 int main( int argc, char* args[] ) {
446 
447         bool quit = false;
448         SDL_Event e;
449         Ship ship;
450         Projectile bullet;
451         SDL_Rect camera = { 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT };
452         init();
453         loadMedia();
454 
455         while( !quit ) {
456 
457                 while ( SDL_PollEvent( &e ) !=0 ) {
458                         if( e.type == SDL_QUIT ) {
459                                 quit = true;
460                         }
461 
462                         ship.handleEvent( e );
463                         bullet.handleEvent( e, ship.getDirection(), ship.getPosX(), ship.getPosY() );
464                 }
465 
466                 bullet.update();
467                 ship.move();
468                 camera.x = ( ship.getPosX() + Ship::SHIP_WIDTH / 2 ) - SCREEN_WIDTH / 4;
469                 camera.y = ( ship.getPosY() + Ship::SHIP_HEIGHT / 2 ) - SCREEN_HEIGHT / 4;
470 
471                 if (camera.x < 0)
472                         camera.x = 0;
473                 if (camera.y < 0)
474                         camera.y = 0;
475                 if (camera.x > LEVEL_WIDTH - camera.w)
476                         camera.x = LEVEL_WIDTH - camera.w;
477                 if (camera.y > LEVEL_HEIGHT - camera.h)
478                         camera.y = LEVEL_HEIGHT - camera.h;
479 
480                 // Clear the screen
481                 SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );
482                 SDL_RenderClear( gRenderer );
483 
484                 bullet.render();
485                 backgroundTexture.render( 0, 0, &camera );
486                 ship.render( camera.x, camera.y );
487 
488                 // Update screen
489                 SDL_RenderPresent( gRenderer );
490                 SDL_Delay(10);
491         }
492 
493         close();
494 
495         return 0;
496 }
                                                                                          
```
\$\endgroup\$
2
  • \$\begingroup\$ I don't have time to write a more detailed answer for now, but you could take a look at the previous Q&A we have involving SDL_GetKeyboardState. Maybe you'll see a pattern emerging and get an idea on how you could approach the issue. \$\endgroup\$
    – Vaillancourt
    Sep 26, 2022 at 11:04
  • 1
    \$\begingroup\$ A good debugging tactic is to pretend you're the CPU, and walk through your code line by line, executing it in your head, or writing down the values of variables on each iteration. Imagine you press down enough times that mVelY is just a hair shy of SHIP_MAX_VELOCITY. Now walk through this function, pressing down again. What's the value of mVelY? Now walk through the loop one more time, this time pressing up. What does your code say should happen? \$\endgroup\$
    – DMGregory
    Sep 26, 2022 at 12:52

1 Answer 1

0
\$\begingroup\$

Try simplifying your if conditions, and enforce the max speed by clamping instead:

if ( keystate[SDL_SCANCODE_UP] ) {
    mVelY = std::max(mVelY - SHIP_ACCELERATION, -SHIP_MAX_VELOCITY);
    direction = UP;
}

if ( keystate[SDL_SCANCODE_DOWN] ) {
    mVelY = std::min(mVelY + SHIP_ACCELERATION, SHIP_MAX_VELOCITY);
    direction = DOWN;
}

This ensures you can accelerate to your max speed exactly, never exceeding it by any loose change, and it ensures you don't skip processing key presses if you're already travelling at max speed.

Bonus: shorter, simpler, less repetitive code with fewer branches.

There are other problems in this code though. You're handling an SDL_KEYDOWN event, but then applying effects from any keys that are currently held down. So if I press and hold say the UP button, then rapidly tap an unrelated key like Q, I'll get more vertical acceleration each time I press Q. That's not what you want.

Either read the event's keysym field to determine the single key that was pressed in this event, or move this code to your update loop to react to all keys equally, once per frame.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .