[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ] [ Search: ]

4.2.4.6 Player Class

The Player class manages the player and player movement including collision detection.

 
class Player
{
private:
  AppMazing* app;

  csRef<iCollider> player_collider;

  // For the movement system.
  csVector3 desired_location;
  csVector3 desired_lookat;
  float amount_moved;
  float amount_rotated;
  csVector3 start_location;
  csVector3 start_lookat;

public:
  Player (AppMazing* app);

  bool InitCollisionDetection ();

  void StartMovement (const csVector3& dir);
  void StartRotation (const csVector3& rot);
  void MoveAndRotateCamera (float elapsed_seconds);
};

First it keeps track of the ‘player_collider’. This will be used for detecting collision detection. Then it keeps track of where the player wants to go too and in what direction it wants to look (‘desired_location’ and ‘desired_lookat’). The function StartMovement() initiates a movement in some direction (relative). The function StartRotation() initiates a rotation in some rotational direction. It is important to note that these functions don't actually do the movement and the rotation. This is done in the function MoveAndRotateCamera(). This function takes the elapsed seconds since the last frame as a parameter. This makes sure that movement speed will be equal on fast and slow hardware. i.e. the movement speed is independent from framerate. That's a very important principle to maintain in games.

 
Player::Player (AppMazing* app)
{
  Player::app = app;

  desired_location.Set (0, 0, 0);
  desired_lookat.Set (0, 0, 1);
  start_location.Set (0, 0, 0);
  start_lookat.Set (0, 0, 1);
  amount_moved = 0;
  amount_rotated = 0;
}

bool Player::InitCollisionDetection ()
{
  float ps = PLAYER_SIZE / 2.0;
  csTriangleMeshBox* box = new csTriangleMeshBox (csBox3 (
  	csVector3 (-ps, -ps, -ps), csVector3 (ps, ps, ps)));
  player_collider = app->GetCollisionDetectionSystem ()
  	->CreateCollider (box);
  box->DecRef ();
  if (player_collider == 0) return false;
  return true;
}

First we have the constructor. In this constructor we set all the defaults for the movement system. In InitCollisionDetection() we set up the collider that the collision detection system is going to use. Basically we represent the player as a box. The class csTriangleMeshBox can be used to quickly create an implementation of iTriangleMesh for a box.

 
void Player::StartMovement (const csVector3& dir)
{
  iCamera* camera = app->GetCamera ();

  amount_moved = 0;
  csReversibleTransform camera_trans = camera->GetTransform ();
  start_location = camera_trans.GetOrigin ();

  // Calculate the spot where we want to move too depending on elapsed
  // time and current direction the camera is facing.
  csVector3 world_dir = camera_trans.This2OtherRelative (dir);
  desired_location = start_location + MOVE_DISTANCE * world_dir;

  // First we find all meshes that are near the path that we want to move.
  // To do that we take the center point of our movement vector and
  // calculate all objects that touch the circle around that point with
  // radius (MOVE_DISTANCE+PLAYER_SIZE) / 2.0.
  csRef<iMeshWrapperIterator> it = app->GetEngine ()->GetNearbyMeshes (
  	camera->GetSector (), (start_location + desired_location) / 2.0,
	(MOVE_DISTANCE + PLAYER_SIZE) / 2.0);

  if (!it->HasNext ())
  {
    // We have no objects in the iterator so there can be no collision.
    // We can move freely.
    return;
  }

  // If we have meshes then we will calculate collision detection for our
  // object along the path we want to move.
  desired_location = start_location;
  csVector3 test_location = start_location + STEP_DISTANCE * world_dir;
  iCollideSystem* cdsys = app->GetCollisionDetectionSystem ();
  float current_move = STEP_DISTANCE;
  while (current_move <= MOVE_DISTANCE)
  {
    camera_trans.SetOrigin (test_location);

    // Test collision with our iterator.
    it->Reset ();
    while (it->HasNext ())
    {
      iMeshWrapper* mesh = it->Next ();
      csColliderWrapper* collide_wrap = csColliderWrapper::GetColliderWrapper (
    	  mesh->QueryObject ());
      if (collide_wrap)
      {
        csReversibleTransform mesh_trans = mesh->GetMovable ()
      	  ->GetFullTransform ();
	cdsys->ResetCollisionPairs ();
        if (cdsys->Collide (
      	      collide_wrap->GetCollider (), &mesh_trans,
	      player_collider, &camera_trans))
        {
	  // Collision, so we can stop. 'desired_location' will contain
	  // the last valid location that we can move too.
	  return;
	}
      }
    }
    desired_location = test_location;
    test_location += STEP_DISTANCE * world_dir;
    current_move += STEP_DISTANCE;
  }
}

void Player::StartRotation (const csVector3& rot)
{
  float angle = ROTATE_ANGLE;
  csOrthoTransform trans = app->GetCamera ()->GetTransform ();
  start_lookat = trans.This2OtherRelative (csVector3 (0, 0, 1));
  trans.RotateThis (rot, angle);
  desired_lookat = trans.This2OtherRelative (csVector3 (0, 0, 1));
  amount_rotated = 0;
}

The StartMovement() function will handle the movement in a certain direction. It will first calculate where the relative direction would take the player. Based on that it will try to do collision detection to move as close as possible to the desired end position. If it finds a suitable spot that is not embedded in a wall it will then store the final desired location so that MoveAndRotateCamera() can use that to do the actual movement.

The StartRotation() function is easier. It will not do any collision detection but instead just calculate where the rotation should go too.

Note that there is actually a bug in this code. Because we are ignoring collision detection when rotating and because our player is represented by a box it is actually possible to move and rotate at the same time and end up stuck in the wall.

 
void Player::MoveAndRotateCamera (float elapsed_seconds)
{
  iCamera* camera = app->GetCamera ();

  // First we move the camera.
  amount_moved += MOVECAMERA_SPEED * elapsed_seconds;
  float move_val = 1 - pow (2.0f, -amount_moved);
  csVector3 current_pos = camera->GetTransform ().GetOrigin ();
  csVector3 new_pos = start_location * (1-move_val)
  	+ desired_location * move_val;
  camera->MoveWorld (new_pos - current_pos, false);

  // Now we rotate the camera.
  amount_rotated += ROTATECAMERA_SPEED * elapsed_seconds;
  float rotate_val = 1 - pow (2.0f, -amount_rotated);
  csVector3 new_lookat = start_lookat * (1-rotate_val)
  	+ desired_lookat * rotate_val;
  camera->GetTransform ().LookAt (new_lookat, csVector3 (0, 1, 0));
}

Based on the desired end position and rotation this function will actually move the player there. It will use the actual number of seconds that have elapsed since the previous frame to ensure that movement is the same on all hardware.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

This document was generated using texi2html 1.76.