Sunday, January 18, 2015

Anansi: Keyboard Controls


The keyboard controls will be the usual for that kind of games:

  • Cursor up = movement up
  • Cursor down = movement down
  • Cursor left = movement left
  • Cursor right = movement right
  • Space = primary weapon
  • Control = secondary weapon

The difficulty we are facing is that JavaFX has great event handlers which fire when you interact with them, i. e. on KeyPressed, KeyReleased, etc. but no means to check if a key is down at a given point in time.

My current solution is simply to register the KeyPressed and KeyReleased events in a BitSet. This way the keyboard code is pretty much generic regarding the event handlers. One problem we are facing is that we don't get the KeyCode as int out of the key event. We'll simply use the ordinal value of the KeyCode instead. We don't use impl_getCode of the KeyCode class, because the method is flagged to be removed.

Let's start coding!

Add controls for the scene:
   // keyboard control
   addInputControls( scene);

Registration for the key events:
 // note: ordinal may be not appropriate, but we don't have a method in keycode to get the int code, so it'll have to do
 BitSet keyboardBitSet = new BitSet();

Add the keyboard event handler:
private void addInputControls( Scene scene) {
 
 // keyboard handler: key pressed
 scene.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<>() {
  @Override
  public void handle(KeyEvent event) {
   
   keyboardBitSet.set(event.getCode().ordinal(), true);
   
  }
 });
 
  // keyboard handler: key up
 scene.addEventFilter(KeyEvent.KEY_RELEASED, new EventHandler<>() {
  @Override
  public void handle(KeyEvent event) {
   
   keyboardBitSet.set(event.getCode().ordinal(), false);
   
  }
 });
 
}


We create variables for the player ship movement
 double playerShipSpeed = 4.0;
 double playerShipDeltaX = 0.0;
 double playerShipDeltaY = 0.0;


Calculate ship movement bounds outside of the AnimationTimer, since we only need to do this once:
// calculate movement bounds of the player ship
// allow half of the ship to be outside of the screen 
double playerShipMinX = 0 - playerShip.getImage().getWidth() / 2.0;
double playerShipMaxX = SCENE_WIDTH - playerShip.getImage().getWidth() / 2.0;
double playerShipMinY = 0 - playerShip.getImage().getHeight() / 2.0;
double playerShipMaxY = SCENE_HEIGHT - playerShip.getImage().getHeight() / 2.0;

In the AnimationTimer's handle method we evaluate the keyboard input and calculate the movement of the player ship:
// get keyboard input
// ---------------------------
// note: ordinal may be not appropriate, but we don't have an method in keycode to get the int code, so it'll have to do

// evaluate keyboard events
boolean isUpPressed = keyboardBitSet.get(KeyCode.UP.ordinal()); 
boolean isDownPressed = keyboardBitSet.get(KeyCode.DOWN.ordinal()); 
boolean isLeftPressed = keyboardBitSet.get(KeyCode.LEFT.ordinal()); 
boolean isRightPressed = keyboardBitSet.get(KeyCode.RIGHT.ordinal());
boolean isSpacePressed = keyboardBitSet.get(KeyCode.SPACE.ordinal());
boolean isControlPressed = keyboardBitSet.get(KeyCode.CONTROL.ordinal());
             
// vertical direction
if( isUpPressed && !isDownPressed) {
 playerShipDeltaY = -playerShipSpeed;
} else if( !isUpPressed && isDownPressed) {
 playerShipDeltaY = playerShipSpeed;
} else {
 playerShipDeltaY = 0d;
}

// horizontal direction
if( isLeftPressed && !isRightPressed) {
 playerShipDeltaX = -playerShipSpeed;
} else if( !isLeftPressed && isRightPressed) {
 playerShipDeltaX = playerShipSpeed;
} else {
 playerShipDeltaX = 0d;
}
             

Then we calculate the new position of the player ship and move it. We allow the player ship to be only a little bit outside of the playfield.
// move player ship
// ---------------------------
double newX = playerShip.getLayoutX() + playerShipDeltaX;
double newY = playerShip.getLayoutY() + playerShipDeltaY;

// check bounds
// vertical
if( Double.compare( newY, playerShipMinY) < 0) {
  newY = playerShipMinY;
} else if( Double.compare(newY, playerShipMaxY) > 0) {
  newY = playerShipMaxY;
}

// horizontal
if( Double.compare( newX, playerShipMinX) < 0) {
  newX = playerShipMinX;
} else if( Double.compare(newX, playerShipMaxX) > 0) {
  newX = playerShipMaxX;
}

playerShip.setLayoutX( newX);
playerShip.setLayoutY( newY);

Now we have a playfield with a scrolling background and a player ship which we can control with the keyboard. In case anyone wants to get the whole code of this tutorial, please let me know.

And keep on coding!

No comments:

Post a Comment