OGRE Wiki
Support and community documentation for Ogre3D
Ogre Forums
ogre3d.org
Log in
Username:
Password:
CapsLock is on.
Remember me (for 1 year)
Log in
Home
Tutorials
Tutorials Home
Basic Tutorials
Intermediate Tutorials
Mad Marx Tutorials
In Depth Tutorials
Older Tutorials
External Tutorials
Cookbook
Cookbook Home
CodeBank
Snippets
Experiences
Ogre Articles
Libraries
Libraries Home
Alternative Languages
Assembling A Toolset
Development Tools
OGRE Libraries
List of Libraries
Tools
Tools Home
DCC Tools
DCC Tutorials
DCC Articles
DCC Resources
Assembling a production pipeline
Development
Development Home
Roadmap
Building Ogre
Installing the Ogre SDK
Setting Up An Application
Ogre Wiki Tutorial Framework
Frequently Asked Questions
Google Summer Of Code
Help Requested
Ogre Core Articles
Community
Community Home
Projects Using Ogre
Recommended Reading
Contractors
Wiki
Immediate Wiki Tasklist
Wiki Ideas
Wiki Guidelines
Article Writing Guidelines
Wiki Styles
Wiki Page Tracker
Ogre Wiki Help
Ogre Wiki Help Overview
Help - Basic Syntax
Help - Images
Help - Pages and Structures
Help - Wiki Plugins
Toolbox
Freetags
Categories
List Pages
Structures
Trackers
Statistics
Rankings
List Galleries
Ogre Lexicon
Comments
History: Simple 3rd person camera
View page
Source of version: 117
(current)
!Introduction This page will cover the creation of a basic 3rd person camera class. This is the kind of camera used in games like Super Mario 64, GTA, and Skyrim. It allows the player to see their character on screen while controlling it. You should understand the material from ((Basic Tutorial 1)) and ((Basic Tutorial 2)) before attempting this camera setup. You will need to have a decent understanding of the SceneNode and Entity classes to be able to follow along. You will also have to understand how to gather input events. This is covered in ((Basic Tutorial 5)). {maketoc} !The Plan We want the camera to rotate around the player when the mouse is moved, and we want the character to walk when the WASD keys are pressed. We are going to use a collection of SceneNodes to position the camera correctly behind the character. We will do this by using the player's SceneNode as a target for the camera's SceneNode. We will also add a child to the camera node that will control the pitch of the camera. Here is a picture of how all the SceneNodes will be related: {img fileId="2298" rel="box[g]"} The __Main Node__, __Camera Node__, and __Pitch Node__ will be part of the {MONO()}Camera{MONO} class, and the __Player Node__ will be the SceneNode you create for your player. We will attach the actual {MONO()}Ogre::Camera{MONO} to the __Pitch Node__. !Starting the Camera Class Add a new class to your project called {MONO()}Camera{MONO}. Set up the header like this: {CODE(caption="Camera.h" wrap="1" colors="c++")} #ifndef CAMERA_H #define CAMERA_H #include <OgreCamera.h> #include <OgreSceneNode.h> class Camera { public: Camera(); virtual ~Camera(); void update(float dt); Ogre::SceneNode* getNode() { return node; } Ogre::Camera* getOgreCamera() { return cam; } private: void initViewports(); float spd, turn_spd, pitch_spd; Ogre::Camera* cam; Ogre::SceneNode* node; Ogre::SceneNode* cam_node; Ogre::SceneNode* pitch_node; }; {CODE} We have declared our three SceneNodes and given the class an Ogre::Camera member. We have also declared three floats to control the movement and rotation speeds of the camera. In your own project, you may want to bring these values in from outside the Camera class. For instance, you may want to use your player's speed value instead of a speed set by the Camera. For this example, we'll just allow the Camera to set them for simplicity. !Constructing the Camera Let's begin by writing the Camera's constructor. {CODE(caption="Camera::Camera" wrap = "1" colors="c++")} Camera::Camera() : spd(10), turn_spd(12), pitch_spd(4), node(0), cam_node(0), pitch_node(0) { cam = GameManager::instance().scn_mgr->createCamera("UserCamera"); cam->setNearClipDistance(.1); {CODE} First, we initialize the three speed values and the three SceneNodes. Then we ask the SceneManager to create an Ogre::Camera for us. In this example, we are using a GameManager singleton to get access to the SceneManager. If you don't like this approach, then you can pass your {MONO()}Camera{MONO} class a reference to the SceneManager. We also set the near clipping distance for the camera. The values used in this example are for a scale where 1 unit = 1 meter. You may have to adjust them to fit the scale of your project. Now we are going to set up the nodes we need to make our camera work. Continue adding to the constructor where you left off. {CODE(wrap = "1" colors="c++")} ^ node = GameManager::instance().scn_mgr->getRootSceneNode()->createChildSceneNode(); cam_node = node->createChildSceneNode(); cam_node->setPosition(0, 1.8, 3); {CODE} __Note:__ The ^ symbol will be used to indicate a code section continues from a previous one. Don't add it to your code! We first ask the SceneManager to create a child of the root SceneNode. This will be our __Main Node__ from the diagram. It will be the anchor for all of the other nodes. The next thing we do is create a child of the __Main Node__ to serve as the __Camera Node__. This will dictate where the camera is placed in relationship to the character. The position of this node will be ''relative'' to the __Main Node__ since we have made it a child of that node. So its position will act as an offset from the character's position. Again, the values used here are for a 1 unit = 1 meter scale. They may be too small for your project if it uses a larger scale. These values will place the camera the equivalent of 1.8 meters above the ground (about the average height of a person) and 3 meters behind the character. {CODE(wrap = "1" colors="c++")} ^ pitch_node = cam_node->createChildSceneNode(); pitch_node->attachObject(cam); initViewports(); } {CODE} The last node we need to create is the __Pitch Node__. It will control the camera's up and down rotation. We create this node as a child of the __Camera Node__. We attach the actual {MONO()}Ogre::Camera{MONO} to this __Pitch Node__. Since the __Pitch Node__ is a child, we can rotate it without messing up the rotation of our __Camera Node__, but it will still move along with the __Camera Node__. Finally, we call a method that sets up the viewport we will use for our Ogre::Camera. The method looks like this: {CODE(wrap="1", colors="c++")} void Camera::initViewports() { Ogre::Viewport* vp = GameManager::instance().window->addViewport(cam); vp->setBackgroundColour(Ogre::ColourValue(0, 0, 0)); cam->setAspectRatio( Ogre::Real(vp->getActualWidth()) / Ogre::Real(vp->getActualHeight())); } {CODE} If you did not use a singleton, then you will also have to give this method a reference to the Ogre::RenderWindow you create in your project. The ((Basic Tutorials)) cover setting up a viewport if you are confused by any of this code. !Updating the Camera Now we are going to build the {MONO()}update{MONO} method. {CODE(caption="Camera::update" wrap="1", colors="c++")} Ogre::Vector3 movement(0, 0, 0); Ogre::Vector3 direction = node->getOrientation() * Ogre::Vector3::NEGATIVE_UNIT_Z; direction.normalise(); {CODE} We set up two vectors to control the movement of the camera. We will build up the {MONO()}movement{MONO} vector based on what input events were reported since the last update. The {MONO()}direction{MONO} vector represents the current direction the entire setup is facing. We build this vector by getting the orientation quaternion from our __Main Node__ and multiplying it by the direction we want to be considered default. When the system is facing directly down the negative z-axis that will be considered a rotation of zero degrees. Finally, we normalise the direction vector so that its total length is one. This will allow us to use it to move the camera without distorting the speed we wish to use. If you want more information on the use of quaternions, then read through the ((Quaternion and Rotation Primer)). Quaternions are confusing at first, but they are a very powerful tool and well worth learning. The next thing we will do is disable the character's current animation. This is done through another reference in our GameManager. {CODE(wrap="1", colors="c++")} GameManager::instance().player->runAnimation(false); {CODE} The {MONO()}runAnimation{MONO} method is in a custom Player class that encapsulates our user's OgreEntity and things like animations. It simply enables/disables the current Ogre::AnimationState of our Player object. Setting up animated entities is covered in ((Intermediate Tutorial 1)). The next section of the update method will access a struct that was filled with any input events that occurred since the last update. The building of this struct will not be covered here. ((Basic Tutorial 5)) covers buffered input events with Ogre. {CODE(wrap="1", colors="c++")} const Input input = GameManager::instance().input_system->input; if (input.up) movement += direction; if (input.down) movement -= direction; {CODE} First, we get a copy of the Input struct that holds all of the input events. If there was an 'up' or 'down' event, then we simply move the character forward in the direction it was already facing or we move the character directly backwards. Usually, in many PC games these movements will be bound to the W and S keys. The advantage of using generic input events like 'up' and 'down' is that you can let the user decide what keys will generate these events - or whether they should be generated by a joystick instead of the keyboard. Next we take care of movement to the left or right - often called ''strafing''. To understand this, you need to remember a litle trigonometry. We are basically just adding a rotated {MONO()}direction{MONO} vector to our {MONO()}movement{MONO} vector. The {MONO()}direction{MONO} vector only exists in the x-z plane (the "floor plane" in Ogre). This simplifies things a bit. We only need to perform a 2d rotation. {CODE(wrap="1", colors="c++")} if (input.right) { movement.x -= direction.z; movement.z += direction.x; } if (input.left) { movement.x += direction.z; movement.z -= direction.x; } {CODE} The [http://en.wikipedia.org/wiki/Rotation_matrix|2d rotation matrix] looks like this: {CODE(wrap="1")} [cos(t) -sin(t)] [sin(t) cos(t)] {CODE} If we plug in a 90 degree turn to the left and right, then we get these values: {CODE(wrap="1")} Left: [0 -1] [1 0] Right: [ 0 1] [-1 0] {CODE} Applying these matrices to our {MONO()}direction{MONO} vector gives us the transformations we're using. The next thing we will do is use this new {MONO()}movement{MONO} vector to set the mesh's animation and translate the main node of our camera system. {CODE(wrap="1", colors="c++")} if (movement.x == 0 && movement.z == 0) { GameManager::instance().player->setAnimation("idle"); } else { movement.normalise();; node->translate(dt * spd * movement); GameManager::instance().player->setAnimation("walk"); } {CODE} First, we check to see if there were no movement events at all. This would mean our movement vector has a length of zero. In this case, we set an idle animation for the mesh. If there is a non-zero movement vector, then we use it to translate the __Main Node__ and we set the mesh's walk animation. You may have noticed that our method for building the {MONO()}movement{MONO} vector would have resulted in a longer vector when the user tried to move forwards ''and'' to the side at the same time. This is a classic problem that occurs in many games where the character can increase their speed by running at an angle. To fix this, we normalise our {MONO()}movement{MONO} vector before applying it to the node. We also factor in the delta time since the last update and the {MONO()}spd{MONO} value we set earlier. We've taken care of the camera translation, now we need to set the camera's rotation. This code also relies on input events for the mouse that were captured in the same {MONO()}Input{MONO} object. The values represent the relative movements of the cursor since the last frame in the x-y plane of the screen. {CODE(wrap="1", colors="c++")} node->yaw(Ogre::Degree(dt * -input.rot_x * turn_spd)); pitch_node->pitch(Ogre::Degree(dt * -input.rot_y * pitch_spd)); {CODE} These two calls set the yaw and pitch of the camera. We factor in the delta time, the respective speeds, and the relative motion of the mouse. If you want the pitch to be "inverse" like it is in many first-person shooters, then you would remove the negative sign from the y rotation value in the second call. The same could be done for the yaw call if you wanted to allow your user to decide which axes will be inverse. The last thing we need to do is restart the animation. {CODE(wrap="1", colors="c++")} GameManager::instance().player->runAnimation(true); {CODE} That completes the update method and the entire Camera class. Now we just have to integrate it into our project. !Using the Camera Class Now we are going to add the Camera class to our project's startup code. Sometime before we start our rendering loop, we are going to create an instance of our Camera class and save it into our GameManager singleton. {CODE(wrap="1" colors="c++")} GameManager::instance().cam = new Camera(); {CODE} Now we need to create the __Player Node__. This is a little bit trickier than the camera nodes, because you will most likely build your Player instance separately from your camera, but we need to make the __Player Node__ a child of the __Main Node__. The approach that this example takes is adding a {MONO()}createSceneNode{MONO} method to our Entity class. This is a class that encapsulates our Ogre::SceneNode and Ogre::Entity. The method looks like this: {CODE(wrap="1" colors="c++")} bool Entity::createSceneNode(Ogre::SceneNode* parent_node) { if (!node) { node = parent_node->createChildSceneNode(); node->attachObject(entity); return true; } return false; } {CODE} Now we can use this method when setting up our scene to attach our Player to the camera system. We will retrieve the __Main Node__ of the Camera class and then pass it to the {MONO()}Entity::createSceneNode{MONO} method to hook our player's Entity into the system. The {MONO()}entity{MONO} that is attached is the class member that holds a pointer to the Ogre::Entity. We also have to rotate the character around by 180 degrees. By tradition, Ogre meshes are facing down the positive z-axis (looking out from the screen) and our camera is facing down the negative z-axis. But we want our mesh to have its back facing towards us for this setup. We simply rotate this new SceneNode we've created for the Player. Since it is only a child of the __Main Node__, its rotation will not screw up the rest of our camera system. {CODE(wrap="1" colors="c++")} GameManager::instance().player->getSceneNode()->rotate( Ogre::Vector3::UNIT_Y, Ogre::Degree(180)); {CODE} The last thing we need to do is call our Camera's update method during the game loop. {CODE(wrap="1" colors="c++")} GameManager::instance().cam->update(dt); {CODE} That's it! Barring any terrible catastrophes, you should have a working 3rd person camera setup. You may want to start playing around with some of the speed variables or things like the camera near clip distance to make things work better with your own project. Here's a picture of the camera in action: {img fileId="2299" rel="box[g]"} !Postscript The ((3rd person camera system tutorial)) provides another method for creating this kind of camera system. ~tc~ (alias(Simple_3rd_person_camera)) ~/tc~
Search by Tags
Search Wiki by Freetags
Latest Changes
Minimal Ogre Collision
Artifex Terra
OpenMB
Advanced Mogre Framework
MogreSocks
Critter AI
Mogre Add-ons
MOGRE
Mogre MyGUI wrapper
MOGRE Editable Terrain Manager
...more
Search
Find
Advanced
Search Help
Online Users
34 online users