Ogre Beginner for Beginner Tutorial: Opponent & simple collision detection

5th March 2011 – Ogre 1.7.1 & Blender 2.47

A previous tutorial showed how to create a simple program allowing control of a mech model using Ogre3D (the mech model was created separately in this tutorial). Now it is time to add a little more functionality to that program and create a simple shooting game. In this tutorial we will place some other objects on the screen that our mech will need to manoeuvre around. Thus some collision detection is also required. To provide some competition, a computer controlled opposing mech is added. Lastly, the mechs are given the ability to fire lasers at each other – the first to score a hit wins. Below is a video of the resulting program (unfortunately the screen capture program I used has affected the framerate; it is a great deal less choppy on my laptop than seen here). The final code (in a Visual Studio 2008 project) can be downloaded here.


  1. The steps below follow on from the steps in the first tutorial and it is assumed the reader is familiar with them. I have not defined any namespaces, so it is clear when an Ogre class is being used. I have also omitted the header definitions and the assigning of values to member variables in constructors from the code examples. They are in the downloadable code.
  2. First another object will be placed on the centre of the ground. The object is a simplified version of the Tozeur sculpture built in an older Blender tutorial. I have only used the top part of that sculpture and removed all but the concrete material. After exporting from Blender using the technique in the previous tutorial, the resulting scuplture.mesh file is placed in the resources folder of the downloadable project (the modified .blend file is also there). The Blender export also creates a .material file, but we don’t want another of those, so just copy the materials from it into the project’s existing .material file.
  3. To place the sculpture on the centre of the plane just add the code below to MechWalkTest::createScene after the mech is placed. It can be seen that the code for placing the mech and sculpture is very similar.

    Ogre::Entity* sculptureEntity = mSceneMgr→createEntity(“Sculpture”, “Sculpture.mesh”);
    Ogre::AxisAlignedBox sculptureBox = sculptureEntity→getBoundingBox();
    mSculptureNode = mSceneMgr→getRootSceneNode()→createChildSceneNode();
    mSculptureNode→attachObject(sculptureEntity);
    mSculptureNode→setScale(4.0f, 17.0f, 4.0f);
    mSculptureNode→setPosition(0, -sculptureBox.getCorner(Ogre::AxisAlignedBox::FAR_LEFT_BOTTOM).y*17.0f, 0);
    sculptureEntity→setCastShadows(true);

    In fact this is so close to how the mech is placed that if you ran the program now the sculpture would be on top of the mech. Move the mech to one of the corners by changing the mech’s setPosition call to the below.


    mMechNode→setPosition(mPlaneSize/4, -box.getCorner(Ogre::AxisAlignedBox::FAR_LEFT_BOTTOM).y, mPlaneSize/4);


  4. Now it is time to place the opposition mech. Again, it done in a very similar manner to the player mech. Add the following code to MechWalkTest::createScene. The yaw() call makes the opposing mech face in the opposite direction to the player mech by spinning it 180 degrees around the Z axis (pitch() & roll() spin around the other axes).

    Ogre::Entity* oppEntity = mSceneMgr→createEntity(“Opponent”, “Mech.mesh”);
    Ogre::AxisAlignedBox oppBox = oppEntity→getBoundingBox();
    mOppNode = mSceneMgr→getRootSceneNode()→createChildSceneNode();
    mOppNode→attachObject(oppEntity);
    mOppNode→setScale(0.8f, 0.8f, 0.8f);
    mOppNode→setPosition(-mPlaneSize/4, -oppBox.getCorner(Ogre::AxisAlignedBox::FAR_LEFT_BOTTOM).y, -mPlaneSize/4);
    oppEntity→setCastShadows(true);
    mOppState = oppEntity→getAnimationState(“Walkcycle”);
    mOppState→setLoop(true);
    mOppNode→yaw(Ogre::Radian(Ogre::Math::PI));

    If this is run now there are two identical mechs on the screen. There needs to be a way of differentiating between them. One way to do this is to make the opposing mech a different colour. The colour of an Ogre entity is determined by its material. The material on the mechs is set to the default defined in the mesh (which will be the material originally exported from Blender with the mesh). The materials are defined in the Scene.material file in the resources folder. Add a new material by adding the text below at the bottom of this file. Now the opposing mech can be made to use the new material with the method call oppEntity→setMaterialName(“OpponentMaterial”); (put this line just after the yaw). This new material is just a copy of the original mech material with the green & blue settings for diffuse and specular lighting reversed. The numbers across the line are normalised red, green, blue and alpha values – see this web page for the definition of the different lines.

    material OpponentMaterial
    {
    receive_shadows on
    technique
    {
    pass
    {
    ambient 0.500000 0.500000 0.500000 1.000000
    diffuse 0.189712 0.159939 0.368351 1.000000
    specular 0.154000 0.144000 0.196000 1.000000 0.250000
    emissive 0.000000 0.000000 0.000000 1.000000
    }
    }
    }


  5. At the moment the player’s mech can happily run straight through the sculpture and opposing mech. This just won’t do – it’s time for some collision detection. If accurate collision detection is required a specialist library like MOC or a physics library like Bullet could be used. However, for this project only a simple collision detection algorithm is needed. So the ray query code already in Ogre will be used.
    Executing a ray query returns any intersecting meshes along the ray, together with the distance of the intersection from the ray origin. Our usage of it here introduces errors as the ray is not projected from the nose of the mech (just close to the nose), and even then rays from the other mech extremities would also be required to detect glancing hits. However, I think this is enough to show how to use this method and decide if a more detailed system is required in other programs. The query needs to be created at the bottom of MechWalkTest::createFrameListener

    mRaySceneQuery = mSceneMgr→createRayQuery(Ogre::Ray());

    Also add this line to the destructor to clean up after the program.

    mSceneMgr→destroyQuery(mRaySceneQuery);

    Now the method to detect collisions is below. It creates a ray starting at position and pointing in direction checking for any meshes that intersect it that aren’t the player’s mech itself and are very close (within half the length of the mech). Thus it will return true if the player mech roughly runs into something. It is not exact and much work could be spent on improving the algorithm, but this should give an idea of how it is done.

    bool MechWalkTest::isCollision(const Ogre::Vector3& position, const Ogre::Vector3& direction)
    {
    Ogre::Ray ray(position, direction);
    mRaySceneQuery→setRay(ray);
    Ogre::RaySceneQueryResult &result = mRaySceneQuery→execute();
    Ogre::RaySceneQueryResult::iterator itr;
    for (itr = result.begin(); itr != result.end(); itr++) {
    if (itr→movable→getName().compare(“Mech”)!=0 && itr→distance<mMechLength/2) {
    return true;
    }
    }
    return false;
    }

    To add collision detection, just before the mech is about to move check it has not collided with something and if it has prevent the forward movement. This means guarding the translate method used to move the player’s mech with a call to isCollision.

    Ogre::Vector3 direction = Ogre::Vector3(Ogre::Math::Cos(mMechDirection),0,Ogre::Math::Sin(mMechDirection));
    Ogre::Vector3 position = mMechNode→getPosition();
    if (!isCollision(position, direction)) {
    mMechNode→translate(direction * mSpeed * evt.timeSinceLastFrame * 2.5);
    checkBounds(position);
    }

  6. Soon both mechs will move and interact with each other. The code is getting more complex and putting it all in the MechWalkTest class is becoming unwieldy. It is time to refactor. A class, Mech, is created to hold all the code related to manoeuvring the player mech. Another OpponentMech class, subclasses Mech and handles any logic specific to the opponent mech (for example making it another colour). Then instances of these classes are created in MechWalkTest. A large amount of the code changed in this step, so it is not all presented here. Instead look at the final program code to see what has changed.
  7. Next the opponent mech should move, or it will be a sitting duck for the player. After the refactor, the OpponentMech has an empty move method. The logic to determine how the opponent mech moves is added to this move method, using the existing manoeuvre methods on Mech (accelerate, decelerate, turnLeft, turnRight). The algorithm for movement will be very simple – it will continuously turn right at a constant speed. The speed and rate of turn (mTurnTimeSeconds) is set in the constructor and the new move method is shown below.

    void OpponentMech::move(Ogre::Real time, Mech* player)
    {
    mTimeToTurnSeconds+=time;
    if (mTimeToTurnSeconds>mTurnTimeSeconds) {
    mTimeToTurnSeconds=0.0f;
    turnRight();
    }
    Mech::move(time);
    }
  8. To add a little competition the mechs will be able to shoot at each other. Firing a laser will display a manual object (an object constructed with API calls rather than through a mesh) on the screen. The laser will stay for a fraction of a second and then disappear. When the laser is drawn on the screen it will need a material so it can be seen. Add a material like the one below to the Scene.material file. Applying this material to the laser will make it bright red as the red values are set to 1 and the green and blue values are 0. Note the emissive value – this makes the laser self-illuminate (with a red light).

    material LaserMaterial
    {
    receive_shadows off
    technique
    {
    pass
    {
    ambient 1.000000 0.000000 0.000000 1.000000
    diffuse 1.000000 0.000000 0.000000 1.000000
    specular 1.000000 0.000000 0.000000 1.000000 1.000000
    emissive 1.000000 0.000000 0.000000 1.000000
    }
    }
    }

    As the mech can only shoot one laser at a time, the laser object can be created in advance and just positioned and displayed when required. In the constructor add the lines below to create the laser. Note that the object and node names are postfixed with the mech’s name. This is so the name does not clash with the opponent mech’s laser.


    mLaser = mSceneMgr→createManualObject("laser"+name);
    mLaserNode = mSceneMgr→getRootSceneNode()→createChildSceneNode("laser_node"+name);

    The methods to display and remove the laser are below. To make sure there are not two laser shots at once, the time remaining on the current laser shot is checked. Then the laser is drawn as a line with two points (set by the position method), both projected different distances from the centre of the mech mesh in the direction the mech is heading. The laser is added to the scene by just attaching it to the laser node (which is part of the scene). Similarly, removing it is a matter of detaching it from the node and clearing the points of the line so they are not reused the next time the laser is fired.

    void Mech::removeLaser()
    {
    mLaser→clear();
    mLaserNode→detachObject(mLaser);
    }

    void Mech::fireLaser()
    {
    if (mCurrentLaserSeconds<=0) {
    Ogre::Vector3 mechHeight = Ogre::Vector3(0, mEntity→getBoundingBox().getSize().y*0.5, 0);
    mLaser→begin(“LaserMaterial”, Ogre::RenderOperation::OT_LINE_LIST);
    mLaser→position(mechHeight+getPosition()getDirection()*5);
    mLaser→position(mechHeight
    getPosition()getDirection()50);
    mLaser→end();
    mLaserNode→attachObject(mLaser);
    mCurrentLaserSeconds = mLaserSeconds;
    }
    }


    To determine the appropriate time to remove the laser add the code below to the top of Mech::move, as this method keeps track of time passing.

    if (mCurrentLaserSeconds>0) {
    mCurrentLaserSeconds-=time;
    if (mCurrentLaserSeconds<=0) {
    removeLaser();
    }
    }

    Now the mechs can fire lasers, a way is needed to allow the player to trigger such an event. The code below added to MechWalkTest::processUnbufferedInput will fire the player’s mech laser when the space key is pressed.

    if (mKeyboard→isKeyDown(OIS::KC_SPACE)) {
    mMech→fireLaser();
    }


  9. Right now firing a laser just draws it on the screen; the opposing mech could be hit repeatedly with no effect. Laser hits need to be detected and processed. This is done in a similar manner to collision detection. A ray is projected out along the laser line and if it intersects the other mech’s mesh within the length of the laser then a hit is scored.

    bool Mech::laserHits(Mech
    opponent)
    {
    Ogre::Ray ray(getPosition(), getDirection());
    mRaySceneQuery→setRay(ray);
    Ogre::RaySceneQueryResult &result = mRaySceneQuery→execute();
    Ogre::RaySceneQueryResult::iterator itr;
    for (itr = result.begin(); itr != result.end(); itr) {
    if (itr→movable→getName().compare(opponent→getName())==0) {
    return itr→distance<(mLaserLength
    mMechLength/2);
    }
    }
    return false;
    }

    Checking whether the laser hits the target is done when it is fired in Mech::fireLaser. For this simple game, one laser hit is fatal for a mech.

    if (laserHits(opponent)) {
    opponent→mechDead();
    }

    Death is marked by setting the mech as inactive with a speed of 0 and by flipping it upside down.

    void Mech::mechDead()
    {
    mActive=false;
    mMechNode→translate(0.0f, mEntity→getBoundingBox().getHalfSize().y*mScale, 0.0f);
    mMechNode→pitch(Ogre::Radian(Ogre::Math::PI));
    mSpeed=0;
    }

    If a mech is dead then it should no longer be able to move. Thus all the manoeuvre methods need to check that the mech is active before doing anything – below is an example.

    void Mech::decelerate(void)
    {
    if (mSpeed>0 && mActive) mSpeed-=mSpeedChange;
    }

    The opponent mech can be made to fire by modifying its move method as below. It now checks to see if it can successfully hit the player if it shoots and if it can, it fires – a deadly aim!

    void OpponentMech::move(Ogre::Real time, Mech* player)
    {
    if (mActive) {
    mTimeToTurnSeconds+=time;
    if (mTimeToTurnSeconds>mTurnTimeSeconds) {
    mTimeToTurnSeconds=0.0f;
    turnRight();
    }
    if (laserHits(player)) {
    fireLaser(player);
    }
    }
    Mech::move(time);
    }

  10. If one of the mechs is shot dead now it just lays there while the program continues – not a very satisfying end. Instead a text box displaying the result will be displayed once one of the mechs is inactive. The text panel is displayed as a widget like the speed and framerate boxes. The guarding if ensures that the text panel is only added to the display once.


    void MechWalkTest::showResult(Ogre::String result)
    {
    if (mTrayMgr→getWidget(“result”)==0) {
    Ogre::StringVector items2;
    items2.push_back(“Result”);
    mResultPanel = mTrayMgr→createParamsPanel(OgreBites::TL_NONE, “result”, 200, items2);
    mTrayMgr→moveWidgetToTray(mResultPanel, OgreBites::TL_CENTER, 0);
    mResultPanel→setParamValue(0, result);
    mResultPanel→show();
    }
    }

    To detect if the end condition has been met, just add the code below to the top of MechWalkTest::frameRenderingQueued.

    if (!mMech→isActive()) {
    showResult(“You Lose!”);
    } else if (!mOpponent→isActive()) {
    showResult(“You Win!”);
    }

    </



That is it – a simple game has been created. Much more could be done, but this is enough to get you started. There are some great tutorials and examples on the Ogre wiki website.