A really cool feature in video games is a camera that follows the action. Something like a movie camera following the actors around in the film. Looking at the car from a short distance, or a spot view of an airplane in a flight simulator.
This feature is not difficult to implement. Since we have the camera implemented (as a class in our simple game engine) the infrastructure is almost ready.
The first thing we have to do is instantiate a new camera that stays behind our main actor at certain distance so that it can see the back of the player. For the purpose of this sample I assume the player is driving a car, represented by two boxes. In future installations we will see how to load real 3D models. The position of the camera can be calculated as follows:
1. Normalize the velocity of the car so that it has unit length
2. Multiply this vector by the distance you want between the object and the camera.
3. Subtract this from the object position.
The direction of view is from camera location to the vehicle and the orientation remain the same as the object orientation. There is one disadvantage in this approach though. Because the camera is some distance behind the vehicle, when that turns the camera moves very fast. This makes the game look bad because the player cannot concentrate on the action. So we have to implement some adjustment mechanism to allow the camera to adjust to the movement of the vehicle.
In order to solve this movement problem we must define a new more flexible camera type of object. So we derive it from the camera class we already have in order to inherit the basic functionality. We also make some functions of the basic class 'virtual' in order to override them and inject our new functionality. You see that we make changes in the basic library as we go along, changes that make the library more flexible as new challenges arise.
To begin with the changes we re-implement three functions: 'move_to', 'look_at' and 'orient' in order to bypass the direct setting of view parameters of the base camera. These functions set the target view parameters 'tLocation', 'tForward', 'tUp' and 'tLookAt'. Then we define a 'frame_move' function which we call upon every frame move in our game to allow the camera to adjust its view parameters according to the target parameters.
Here is the new camera class code
// camera that follows the action from a distance class c_moving_camera : public cCamera{ clf_vector3D tLocation; // the tagret location clf_vector3D tForward; // target forward direction clf_vector3D tUp; // target up vector clf_vector3D tLookAt; // target point of interest float m_speed; // camera speed public: c_moving_camera() : cCamera(), tLookAt(0,0,0), m_speed(0.01f) { } c_moving_camera(cCamera& cp) : cCamera(cp){ } c_moving_camera(c_moving_camera& cp) : cCamera(cp){ } virtual ~c_moving_camera(){} // override setup, and use it to set target locations virtual void move_to(clf_vector3D& pos){ tLocation = pos; } virtual void look_at(clf_vector3D& lookat){ tLookAt = lookat; tForward = tLookAt-tLocation; tForward.normalize(); } virtual void orient(clf_vector3D& up){ tUp = up; } // actual camera location is set gradualy using camera speed void frame_move(float fElapsed){ clf_vector3D dist(tLocation - vLocation); if (dist.length() < 0.001f) return; // here we set only the camera position clf_vector3D pos(vLocation + m_speed*dist); cCamera::move_to(pos); // and leave the rest cCamera::look_at(tLookAt); cCamera::vUp = tUp; } };
It is not difficult to modify the code in order to support rotational movement for the 'forward' and 'up' vectors.
In the 'frame_move' function of our game we add the following code to update the moving camera.
// position the moving camera // 1. get moving object velocity vector clf_vector3D s(g_person->m_vel); // and make it unit length s.normalize(); // 2. create the view distance vector s.scale(15.f); // 3. and calculate the camera position clf_vector3D loc(g_person->m_pos - s); // raise the view camera at a certain height loc.y = 2.f; // apply all these to the moving camera g_viewer.move_to(loc); g_viewer.look_at(g_person->m_pos); g_viewer.orient(clf_vector3D(0,1,0)); // and let it move g_viewer.frame_move(fElapsed);
Finally in the 'frame_render' we use either the driver's camera or the moving camera:
// position the viewer if (g_view_type == 0) g_viewer.apply(); else g_person->apply_view();
In the sample program that demonstrates this camera the car model is actually two boxes that move around instead of a real car model. Half of the boxes are under the ground. In a future article we will load and use a real 3D model of a car. The main goal of this series is to develop a small driving simulator, with a simple flight simulator following it.