Monday 13 February 2012

Getting Started with the Programming Assignment Pt 4 Moving the Ship

In the previous post we designed different ways of linking our objects together, now to get the basic movement of the ship going I decided to modify the Advanced Game Key control demo from the ngl:: demos, I was originally going to use the format described in this post however the method I'm going to use here is a lot more flexible and is based on some code written by Rob the Bloke

This video shows the code in action

This code is spread amongst several classes but the main control codes are stored in the following structures in the file GameControls.h

enum GameControls
{
  kUp = 1 << 0,
  kDown = 1 << 1,
  kLeft = 1 << 2,
  kRight = 1 << 3,

  kUpLeft = kUp | kLeft,
  kUpRight = kUp | kRight,
  kDownLeft = kDown | kLeft,
  kDownRight = kDown | kRight,

  // nonsense controls
  kUpDown = kUp | kDown,
  kLeftRight = kRight | kLeft
};
This enum allows us to create a simple bit mask for each of the keys we require, in this case we define the main up/down & left right keys, then using a logical or we can create valid key combinations such as up and left etc. Next we are going to define a structure to contain the movement values for each key combination, for now these will be the x,y movement and rotation values in x,y and z
// motion of a spaceship for a given key combo.
struct SpaceShipMotion
{
  float offsetX;
  float offsetY;
  float rotX;
  float rotY;
  float rotZ;
};
Finally we build up a table of key combination / motion values as shown here
static const SpaceShipMotion g_motionTable[] =
{
  { 0.0f, 0.0f, 0.0f ,0.0f,0.0f}, // 0
  { 0.0f, 1.0f, -1.0f,0.0f,0.0f}, // kUp
  { 0.0f,-1.0f, 1.0f,0.0f,0.0f }, // kDown
  { 0.0f, 0.0f, 0.0f,0.0f,0.0f }, // kUpDown (nonsense)

  {-1.0f, 0.0f, 0.0f,0.0f,-1.0f }, // kLeft
  {-0.707f, 0.707f, -0.707f,0.707f,-0.707f }, // kUpLeft
  {-0.707f,-0.707f, 0.707f,0.707f,0.707f }, // kDownLeft
  {-1.0f, 0.0f, 0.0f,0.0f,0.0f }, // kUpDown (nonsense) & kLeft

  { 1.0f, 0.0f, 0.0f,0.0f,1.0f }, // kRight
  { 0.707f, 0.707f, -0.707f,-0.707f,0.707f }, // kUpRight
  { 0.707f,-0.707f, 0.707f,-0.707f,0.707f }, // kDownRight
  { 1.0f, 0.0f, 0.0f,0.0f,0.0f }, // kUpDown (nonsense) & kRight

  { 0.0f, 0.0f, 0.0f,0.0f,0.0f }, // kLeftRight (nonsense)
  { 0.0f, 1.0f, 0.0f,0.0f,0.0f }, // kUp & kLeftRight (nonsense)
  { 0.0f,-1.0f, 0.0f,0.0f,0.0f }, // kDown & kLeftRight (nonsense)
  { 0.0f, 0.0f, 0.0f,0.0f,0.0f }, // kUpDown (nonsense) & kLeftRight (nonsense)

};
This structure is very useful as we can tweak the position and rotation values for each of the movements and it is easy to update the key combinations to add more. The next stage of the code is to combine this with the SpaceShip class, the class used in the previous versions of the code have been modified to remove the move and rotate methods and been modified to use the following method
void SpaceShip::move(uint8_t _keysPressed)
{
  // note we flip the offset to reverse the key direction here
  m_pos.m_x += -g_motionTable[_keysPressed].offsetX;
  m_pos.m_y += g_motionTable[_keysPressed].offsetY;
  const static float s_rotationUpdate=20.0;
  m_rotation.m_x=s_rotationUpdate*g_motionTable[_keysPressed].rotX;
  m_rotation.m_y=s_rotationUpdate*g_motionTable[_keysPressed].rotY;
  m_rotation.m_z=s_rotationUpdate*g_motionTable[_keysPressed].rotZ;

  // clamp
  m_pos.m_x = std::max(-s_xExtents, m_pos.m_x);
  m_pos.m_y = std::max(-s_yExtents, m_pos.m_y);
  m_pos.m_x = std::min(s_xExtents, m_pos.m_x);
  m_pos.m_y = std::min(s_yExtents, m_pos.m_y);
}
The key combinations set in the GLWindow class are passed to this method as a uint8_t data type, we then uses this value to find the index into the motion table and set the SpaceShip position and rotation values. Finally the values are clamped to ensure the ship stays within the visibile area of the screen (this may change once I get some more of the game developed)
Setting the Key Values
To set the key values we add a new attribute to the GLWindow.h class
/// @brief the keys being pressed
uint8_t m_keysPressed;
Now when the keyPress and release method are called in the GLWindow class we set / free the m_keysPressed attribute to the key combinations in the GameControl.h file as shown
void GLWindow::processKeyDown(
                               QKeyEvent *_event
                              )
{
  switch(_event->key())
  {
    case Qt::Key_Up: m_keysPressed |= kUp; break;
    case Qt::Key_Down: m_keysPressed |= kDown; break;
    case Qt::Key_Left: m_keysPressed |= kLeft; break;
    case Qt::Key_Right: m_keysPressed |= kRight; break;
  }
}
When any of the keys are pressed the m_keysPressed attribute will set the correct key flag in the data structure by using a logical or. This means that if the flag is not set it will be set, however if already set it will remain active. When the key is released we need to turn this flag off, which can be done as follows
void GLWindow::processKeyUp(
                             QKeyEvent *_event
                           )
{
  switch(_event->key())
  {
    case Qt::Key_Up: m_keysPressed &= ~kUp; break;
    case Qt::Key_Down: m_keysPressed &= ~kDown; break;
    case Qt::Key_Left: m_keysPressed &= ~kLeft; break;
    case Qt::Key_Right: m_keysPressed &= ~kRight; break;
  }
}
In this case we use the logical not ~ and the logical and to turn the bit flag off. Finally we call the move method of the SpaceShip before we draw as shown in the following method
void GLWindow::paintGL()
{
  // clear the screen and depth buffer
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  m_spaceShip.move(m_keysPressed);
  m_spaceShip.draw();
 
}
The code for this can be found here

1 comment: