OgreOde 3rd Person Camera
Table of contents
Introduction
I spent some time to make my Camera collide correctly with the environment in OgreOde, like its known from any commercial computer game. My first thought was to give the camera a geometry and a body in OgreOde, so it collides with any object. After I made it not to move other objects but instead glide along them, I spent really a lot of time finding the right parameters for the cameras body and movement, and after all I gave it up. If anyone of you has this idea, I suggest you drop it 😊
The conclusion came, when I began to play World of Warcraft. We are about to implement a camera, that behaves just like this one, except some minor changes. Its pretty much of the GuildWars camera, too.
As I developed this camera while I was working on a bigger project, you will not be able to adapt it completely, but I expect you to work around that on your own 😉 It is developed with OgreOde, but it should be no problem to use it with ode or another physics engine. I expect you have worked with OgreOde and cameras in Ogre before, so I will focus the new elements.
Concept
Its pretty easy.
- Take a ray, throw it from the point of the camera to the center, the players head or similiar. - Check for collisions between the ray and every object that is to move the camera -If there is a collision, move the camera along the ray to the point of collision + some distance
Additional Features: Load and save Settings, chasing if wanted, x rotation bounds
Class Declaration
Declare your cameras class and derive it from OgreOde::CollisionListener as its going to handle the collisions of the ray. You can not work with CollidingObject, because its part of my project. I use it to derive all objects that will have a collision callback by OgreOde.
class Camera : public virtual CollidingObject, public OgreOde::CollisionListener {
We will need the following members:
private: typedef CollidingObject inherited; protected: bool CollisionInThisFrame; Ogre::Camera* MyCamera; //the ogre camera Ogre::SceneNode* LookAtNode; //the node we are really looking at Ogre::SceneNode* RotationCenterNode; //the center of rotation, this node will be given by the user on creation Ogre::SceneNode* TargetPositionNode; //the position we want to be at, but maybe are not yet (chasing) Ogre::SceneNode* TargetLookAtNode; //the point we look at, but maybe dont yet double TightnessPos; //how fast does the camera reach target position double TightnessLook; //how fast does the camera look at the target position double MaxDistance; //how much can you zoom out double MinDistance; //how much can you zoom in double Distance; //whats the current distance between rotation center and camera double TargetDistance; //whats the target distance (chasing) double MinCamRot; //the bounds for x rotation double MaxCamRot; OgreOde::RayGeometry* Ray; //the ray we use for the collision detection ObjectManager* ObjManager; //this is part of my engine, you cant use it, more on that later Ogre::Vector3 Scale; //the scale vector of the parent node, so the camera will work same on all sizes you set for your player
and we have these functions:
public: Camera(Ogre::SceneNode* CenterParentNode, OgreOde::Space* Space, ObjectManager* Objects); ~Camera(void); bool virtual FrameStarted(double TimePassed); bool virtual FrameEnded(double TimePassed); void virtual Zoom(const double Value); //Move the camera relative along the ray bool virtual WriteToIni(IniFile& Ini) const {return true;} //save the camera to a file bool virtual LoadFromIni(const std::basic_string<wchar_t>& ObjectID, IniFile& Ini); //load from a file bool virtual collision(OgreOde::Contact *Contact); //collision callback void RotateX(double Value); //rotate the camera along x axis, use this instead of rotating the //rotation center if you dont want the player to rotate inline Ogre::Camera* GetCamera(void) {return MyCamera;} //maybe you need the original Ogre object sometime private: bool MoveChase(const double TimePassed); bool MoveFixed(const double TimePassed); void InitBase(void); void InitNodes(Ogre::Vector3 CamCenter, Ogre::Vector3 LookAtPosition); bool InitCamera(void); bool InitViewport(void); void CollideRay(void); };
Constructor
Initialize your members. The constructor gets the node of the parent object, like your player, the space including the ode objects that the camera is going to collide with and a pointer to my ObjectManager class. As I said you cant use it, you need to find a way to let the camera access your objects.
::Camera::Camera(Ogre::SceneNode* CenterParentNode, OgreOde::Space* Space, ObjectManager* Objects) : inherited(), MyCamera(0), LookAtNode(0), TargetLookAtNode(0), TargetPositionNode(0), RotationCenterNode(0), TightnessPos(0.0), TightnessLook(0.0), MaxDistance(2.0), MinDistance(0.0), Ray(0), MinCamRot(0.0), MaxCamRot(0.0), Distance(1.0), TargetDistance(1.0), CollisionInThisFrame(false), Scale() {
Create a child of the parent center node, so it may has its own rotation that does not effect the player
RotationCenterNode = CenterParentNode->createChildSceneNode("Camera Pos"); //Camera never rotates around the z axis: RotationCenterNode->setFixedYawAxis(true);
Our Ray. You see that I cast the camera to CollidingObject and then make it user object of the Ray. I do this with every Geometry and its parent colliding class, so you can make your own collision callback behaviour for every object. To use this camera you somehow have to identify your camera in the ray .
Ray = new OgreOde::RayGeometry(0, HardwareManager::getSingleton().GetWorld(), Space); Ray->setUserObject(static_cast<CollidingObject*>(this)); Ray->disable();
Save a pointer to the class that handles all the objects we are going to collide with.
ObjManager = Objects; Scale = CenterParentNode->getScale(); }
Destructor
Clean up a little bit:
::Camera::~Camera(void) { delete Ray; }
Create Camera and Viewport
These two functions create a camera and a viewport in ogre, I expect you are familiar with that.
bool ::Camera::InitCamera(void) { // Create the camera if (MyCamera) HardwareManager::getSingleton().GetScene()->destroyCamera(MyCamera); MyCamera = HardwareManager::getSingleton().GetScene()->createCamera("PlayerCam"); if (!MyCamera) return false; MyCamera->setNearClipDistance(5); //note: Position is Ogre::SceneNode* and comes from CollidingObject Position->attachObject(MyCamera); return true; } bool ::Camera::InitViewport(void) { // Create one viewport, entire window static Ogre::Viewport* vp = HardwareManager::getSingleton().GetWindow()->addViewport(MyCamera); if (!vp) return false; //Pink is our background color vp->setBackgroundColour(Ogre::ColourValue(255,0,255)); // Alter the camera aspect ratio to match the viewport MyCamera->setAspectRatio(Ogre::Real(vp->getActualWidth()) / Ogre::Real(vp->getActualHeight())); return true; }
Init the SceneNodes
Now we need several scene nodes for our camera. The RotationCenterNode is what it says and is created within the constructor, so we dont need to handle it here. We will only adjust its position to the given vector, so you can set it to the players head e.g. Then we have a TargetLookAtNode and a TargetPositionNode, these are the positions where the camera should be and look at, but maybe is not yet. LookAtNode and Position are the real ones and will be updated later. You will be able to choose, if you want a static or chasing camera.
void ::Camera::InitNodes(Ogre::Vector3 CamCenter, Ogre::Vector3 LookAtPosition) { //set position of camera rotation center: RotationCenterNode->setPosition(CamCenter); //Destroy the nodes, if they exist if (LookAtNode) HardwareManager::getSingleton().GetScene()->destroySceneNode(LookAtNode->getName()); if (TargetLookAtNode) HardwareManager::getSingleton().GetScene()->destroySceneNode(TargetLookAtNode->getName()); if (TargetPositionNode) HardwareManager::getSingleton().GetScene()->destroySceneNode(TargetPositionNode->getName()); //create LookAtNode LookAtNode = HardwareManager::getSingleton().GetScene()->getRootSceneNode()->createChildSceneNode(); //the targets are children of the center node and as such always rotated automatically to the correct positions TargetLookAtNode = RotationCenterNode->createChildSceneNode(); TargetLookAtNode->setPosition(LookAtPosition); TargetPositionNode = RotationCenterNode->createChildSceneNode(); //note: Position is Ogre::SceneNode* and comes from CollidingObject //Real PositionNode shall always look at real LookAtNode Position->setAutoTracking(true, LookAtNode); //and it should never rotate around the z axis. Position->setFixedYawAxis(true); }