EIE 360 Lab Group B Lab 2

Document Sample
EIE 360 Lab Group B Lab 2 Powered By Docstoc

                 Department of Electronic and Information Engineering

                               EIE360 Integrated Project
                   Group B Lab 2: Computer Game Development Part 2

1. Objective
In this experiment, we shall further enhance the computer game developed in Lab 1 by introducing
the following features: collision detection and explosion effect.

2. Preparation
      A PC with Windows Vista SP2 32-bit / 64-bit Version
      Microsoft Visual Studio 2008 SP1
      OGRE SDK v1.7.2 or later
      Ogre AppWizard_v1.7.2
      DirectX End-User Runtimes (June 2010)
      Basic knowledge in C++ programming, object-oriented programming and 3D graphics
      OgreMax WinViewer v2.3.6 or later
      Newton Game Dynamics SDK 2.24 and OgreNewt

3. Computer Game Development
Copy the project EIE360Project that you created for Lab 1 to a new directory of your choice.
Make sure it is still working in the new directory you are using.

3.1 Newton Game Dynamics
Newton Game Dynamics is a physics engine that simulates the physics environment to enhance the
realism of a computer game. It encapsulates most of the physics logic from the user, and therefore
one only needs to know the basic principles of physics to use it. There is an interface named
“Ogrenewt” that allows simple integration of Newton physics engine into an Ogre application. Most
of the content of this lab is directly extracted from the following and related web sites:

Newton has a few basic elements used to describe the physics world. They are:

      WORLD (OgreNewt::World)
      RIGID BODY (OgreNewt::Body)
      COLLISION (OgreNewt::Collision)
      JOINT (OgreNewt::Joint)
      MATERIAL (OgreNewt::MaterialID && OgreNewt::MaterialPair)

                                                                                           Page 1

To simplify the task, two libraries called OgreNewt.lib and OgreNewt_d.lib have been pre-built to
hide the complexity of using Newton.

To use Newton, we need to link a few libraries. In Visual Studio C++ IDE, modify the link and include
settings as follows:

       Click Project  Properties  Linker  General  Additional Library Directories. Add “lib”
        and “C:\NewtonWin-2.24\sdk\x32\dll_vs9” to both Debug and Release Configuration.
       Click Project  Properties  Linker  Input  Additional Dependencies. Add newton.lib to
        both Debug and Release Configuration. Also add OgreNewt_d.lib to Debug and OgreNewt.lib
        to Release Configuration.
       Click Project  Properties  C++  General  Additional Include Directories. Add
        “C:\ogrenewt\newton20\inc” and “C:\NewtonWin-2.24\sdk” to both Debug and Release
       Include both “OgreNewt.h” and “OgreNewt_BasicFrameListener.h” in EIE360Project.h. You
        may assume they are in the current directory.

Look for Newton.dll in the file, which can be downloaded from the subject Web site.
Make a copy of it and save to Bin/debug and Bin/release of your project. Copy also both
“OgreNewt_d.dll” and “dJointLibrary_d.dll” to Bin/debug, and both “OgreNewt.dll” and
“dJointLibrary.dll” to Bin/release. Finally copy the directories lib and media in to your
working directory/EIE360Project/EIE360Project.

Declare two member variables in EIE360Project.h:

        OgreNewt::World* mWorld;
        Ogre::FrameListener* mOgreNewtListener;

In the constructor of EIE360Project, add
        mWorld = new OgreNewt::World();

In the destructor of EIE360Project, add
        delete mWorld;
        mWorld = 0;

The Newton world has its own frame listener to update all the objects in each time step. Overriding
the createFrameListener(...) of BaseApplication by adding another one in
EIE360Project. Add the following codes to EIE360Project.cpp.

void EIE360Project::createFrameListener(void)
       // It is important that the Physics update is updated before anything
       mOgreNewtListener = new OgreNewt::BasicFrameListener( mWindow, mWorld );
       mRoot->addFrameListener( mOgreNewtListener );


Remember to modify the header file to add its declaration.

Build the project. Make sure there is no compilation error up to this point.

                                                                                                 Page 2

3.2 Create Rigid Body
Then, we need to create a body in the Newton world. To handle physics of an object, one must
create a body with collision primitives and attach it to a SceneNode. It sets up a standard
transformation (i.e., position and orientation) callback to the scene node automatically.

A ground and a wooden barrel will be created. The ground entity will be scaled to fit the size of the
terrain we used. Then we create a rigid body of the ground entity by loading the shape for collision
detection got from TreeCollision() and attach it to the groundNode. As the ground mesh
object overlaps with the terrain, so we make it to be invisible.

Firstly, in createScene(), set the Newton world size.

       mWorld->setWorldSize(Ogre::Vector3(-100, -100, -100), Ogre::Vector3(3300,
500, 3300));

Then, add the following codes:

       // Island
       Ogre::Entity* island = mSceneMgr->createEntity("IslandMesh",
"Island.mesh" );
       Ogre::SceneNode *groundNode = mSceneMgr->getRootSceneNode()-
>createChildSceneNode( "IslandNode" );
       groundNode->attachObject( island );
       groundNode->setScale(50, 100, 50);

       // Rigid body of Island
       OgreNewt::CollisionPtr col(new
OgreNewt::CollisionPrimitives::TreeCollision(mWorld, island, false, 1));
       OgreNewt::Body *ground_body = new OgreNewt::Body(mWorld, col);
       ground_body->attachNode( groundNode );
       ground_body->setPositionOrientation( Ogre::Vector3(1600, 0, 1600),
Ogre::Quaternion(sqrt(0.5), 0, -sqrt(0.5), 0) );

       // Wooden Barrel
       Ogre::Entity *barrelEnt = mSceneMgr->createEntity("Barrel",
       Ogre::SceneNode *barrelNode = mSceneMgr->getRootSceneNode()-
       barrelNode->setPosition( 1600, 195, 1300);

Now we create the rigid body of the barrel by loading the shape of wooden barrel for collision
detection got from ConvexHull(). It “wraps” around a set cloud of vertices of the barrel with a
convex hull, which is the smallest possible convex shape that fully encloses all points supplied. Then
we should attach the rigid body to a SceneNode namely barrelNode.

To achieve these, add the following codes to createScene():

       // Rigid body of barrel
       OgreNewt::ConvexCollisionPtr col_convex(new
OgreNewt::CollisionPrimitives::ConvexHull(mWorld, barrelEnt, 2));
       OgreNewt::Body *barrel_body = new OgreNewt::Body( mWorld, col_convex );
       barrel_body->attachNode( barrelNode );

                                                                                               Page 3

       barrel_body->setPositionOrientation( Ogre::Vector3(1600, 195, 1300),

Note that if you want to move the barrel, you need to apply translation and rotation on the rigid
body instead of its attached scene node. Now we set the mass, inertia, offset and gravity of the
barrel. The inertial matrix is calculated by calculateInertialMatrix(). Add the following
codes to createScene().

        Ogre::Real mass = 10;
        Ogre::Vector3 inertia, centerOfMass;
        col_convex->calculateInertialMatrix(inertia, centerOfMass);

        barrel_body->setMassMatrix( mass, inertia );

Now you can build and execute your project. Did you see both the dragon and the barrel on the
screen? If not, adjust the position of your camera in order to see both. Grab the screen output and
paste it below.

Do you see the barrel falling and hitting the ground? If not, set the initial position of the barrel 100
units upper. You only need to change the initial position of the barrel body.

3.3 Player Controller
In Lab 1, we move the dragon by translating and rotating the sceneNode. In fact, if an object is
attached with a Newton body, we can move it by using Player Controller. In this session, we make
use of the player controller to control the dragon. Now let’s create the rigid body of the dragon with
an ellipsoid. It can be done like similar to that for the wooden barrel. First, declare a member
variable mPlayer_body of type pointer to OgreNewt::Body. Then in createScene(), add
the following codes:

                                                                                                   Page 4

        // Rigid body for dragon
        Ogre::Vector3 size = mCharacterEntity->getBoundingBox().getSize();
        size.y = size.y/2;

       col_convex = OgreNewt::ConvexCollisionPtr(new
OgreNewt::CollisionPrimitives::Ellipsoid( mWorld, size * sizeFactor/4, 3,
              Ogre::Quaternion(Ogre::Degree(180), Ogre::Vector3::UNIT_Y),
Ogre::Vector3(0, -size.y * sizeFactor / 2 + 5, 0) ));

       mPlayer_body = new OgreNewt::Body( mWorld, col_convex );
       mPlayer_body->setPositionOrientation(Ogre::Vector3(1450, 195+80, 1300),

        mass = 50;
        col_convex->calculateInertialMatrix(inertia, centerOfMass);
        mPlayer_body->setMassMatrix( mass, inertia );

Then, declare a member variable mPlayer of type pointer to OgreNewt::PlayerController.
Create a player controller for the dragon and initialize its velocity by adding the following codes to
the end of createScene():

        mPlayer = new OgreNewt::PlayerController(mPlayer_body, 0.5);
        mPlayer->setVelocity(0, 0, Ogre::Degree(0));

Now remove the statements in processCalculation() for translating and rotating the
character node of the dragon. Remove also the statements for ray scene query. You do not need
them since the player controller will help you move the dragon on the ground.

Following the instructions in the course notes, modify your program such that you will make use of
Player Controller to control the dragon to walk on the terrain. The program should be able to run the
same as before.

Give your codes below. Clearly indicate in which functions the codes are modified.

                                                                                              Page 5

3.4 Creating a Custom Force Callback to Fly
Next we modify the program to allow the dragon to fly in the sky. To do so, we need to enable the
dragon to take off. It can be done by applying an upward impulse to the dragon body. However, it
requires precise control of the magnitude and timing of the impulse or the dragon may jump high up
in the sky. It does not look like taking off in this case. Such control procedure can be complicated. To
simplify the task, we use an alternative approach that artificially sets the dragon at a height of about
100 units above the ground. The effect is acceptable as seen to be taking off.

Declare a member variable mKeyBuffer of type Ogre::Real and initialize it to 0. Declare
another member variable mFlying of type bool and initialize it to false Then, add the following
codes in processCalculation().

       OgreNewt::Body *playerBody = mPlayer->getBody0();
       if(mKeyboard->isKeyDown(OIS::KC_SPACE) && mKeyBuffer < 0)
              playerBody->setPositionOrientation(playerBody->getPosition() +
Ogre::Vector3(0, 30, 0), playerBody->getOrientation());
              mKeyBuffer = 0.5;
              mFlying = true;
       mKeyBuffer -= evt.timeSinceLastEvent;

The above codes allow the dragon to raise its height by approximately 30 units from the ground
when the player hits the space bar. To see it more clearly, you may change the position of your
camera. But remember to change it back in order to finish the rest of this lab work.

To control the dragon to fly, we need to create a custom force callback to add forces on its body.
First, declare the following member variables

        Ogre::Real mFlyForwardSpeed;
        Ogre::Real mFlySideSpeed;
        Ogre::Real mFlyUpSpeed;

and initialize them all to 0. Declare another member variable
        DWORD mTickCount;

and initialize it by adding the following statement in the constructor of EIE360Project:

        mTickCount = GetTickCount();

In createScene(), remove the statement:


Then declare a new custom force callback function called customFlyCallback(). Register the
callback function by adding the following statement

        (&EIE360Project::customFlyCallback, this);

in createScene().

                                                                                                 Page 6

Add the following customFlyCallback() function to EIE360Project.cpp. Remember to declare
it in EIE360Project.h.

void EIE360Project::customFlyCallback(OgreNewt::Body *body, float timeStep, int
       Ogre::Real mass;
       Ogre::Vector3 inertia;
       body->getMassMatrix(mass, inertia);

       Ogre::Vector3 force = Ogre::Vector3::ZERO;
       if (!mFlying)
              force = mass * Ogre::Vector3(0,-500,0);
       else force = mass * Ogre::Vector3(0,-0.01,0);

       DWORD now = GetTickCount();
       Ogre::Real time = (now - mTickCount) / 1000.0;
       mTickCount = now;

       Ogre::Vector3 accel = Ogre::Vector3::ZERO;
       Ogre::Vector3 velocity = body->getVelocity();

       Ogre::Quaternion bq2 = body->getOrientation() *
Ogre::Quaternion(Ogre::Degree(90), Ogre::Vector3::UNIT_Y);
       Ogre::Vector3 forwardSpeed = bq2 * Ogre::Vector3(mFlyForwardSpeed, 0,

       if (mFlying)
              if (time > 0)
                     accel.x   = (forwardSpeed.x - velocity.x) / time;
                     accel.z   = (forwardSpeed.z - velocity.z) / time;
                     accel.y   = (mFlyUpSpeed - velocity.y) / time;
                     accel.x   = 0;
                     accel.z   = 0;
                     accel.y   = 0;

              if (accel.x > 500)
                     accel.x = 500;
              else if (accel.x < -500)
                     accel.x = -500;

              if (accel.z > 500)
                     accel.z = 500;
              else if (accel.z < -500)
                     accel.z = -500;

              if (accel.y > 500)
                     accel.y = 500;
              else if (accel.y < -500)
                     accel.y = -500;

              Ogre::Vector3 dir;
              force = accel * mass;

                                                                                  Page 7

Study the requirement of the above callback function and modify processCalculation() such
that if the dragon has taken off, you can control the dragon to fly up, fly down, turn left and turn
right by pressing key I, K, U and O, respectively. Remember to show the fly animation of the dragon
when it is flying. Give your codes below. Clearly indicate how the function
processCalculation() is modified.

Hint: the Player Controller of the dragon can help in changing its heading but not its forward speed.
For moving forward, climbing up and flying down, you need to control the force applied to the

                                                                                              Page 8

Grab the screen output when the dragon is flying and paste it below.

3.5 Emitting a Fire Ball
In this section, we are going to apply Newton to the fire ball that you shoot to the barrel.

First, we have to remove all codes in processCalculation() that are related to the position
and animation of the fire ball. We will apply a physics system to the fire ball by creating it in

In EIE360Project.h, declare a variable mFireBall_body, which is a pointer of type

Then, create a rigid body, set the mass, inertia and gravity of the fire ball by adding the following
codes at the end of createScene(). In the codes, a primitive ellipsoid with a radius of 10 is
created. It defines the size of the fire ball.

       // Rigid body of fire ball with a radius of 10
       size = Ogre::Vector3( 10, 10, 10);
       OgreNewt::CollisionPrimitives::Ellipsoid *sphereCol = new
OgreNewt::CollisionPrimitives::Ellipsoid(mWorld, size, 4);
       OgreNewt::CollisionPtr col_sphere = OgreNewt::CollisionPtr(sphereCol);
       mFireBall_body = new OgreNewt::Body( mWorld, col_sphere );
       mFireBall_body->attachNode( mFireBallNode );

       mass = 1.0;
       sphereCol->calculateInertialMatrix(inertia, centerOfMass);
       inertia *= mass;
       mFireBall_body->setMassMatrix( mass, inertia );
       mFireBall_body->setPositionOrientation(Ogre::Vector3(1450, 450, 1300),

                                                                                               Page 9

To make the fire ball move, normally we should apply a force to it. To simplify the task, we are going
to set the velocity instead of applying force in this project. And to make the codes safe, we will
freeze the rigid body before modifying its parameter.

Now, we are going to emit the fire ball. When the “Start” button is pressed, a fire ball will be emitted
from the dragon and both velocity and omega are reset.

Modify EIE360Project::startGame() by adding the following codes at the end:


       mFireBall_body->setVelocity(mFireBallDirection * 200);


Grab the screen output when the dragon is flying and emitting a fireball and paste it below.

3.6 Material Pair
Materials are how Newton lets you adjust the interaction between bodies when they collide. This
can be as simple as adjusting the friction, or much more complex. The material system is pretty
simple. First you create “MaterialID” objects to represent each material you might need in your
system. Then you build what is called a “MaterialPair”, which is a description of what happens when
2 materials collide with each other. For example, you might create a material pair for collisions
between iron and steel. Then you will know any time an “iron” and “steel” object come in contact.
You can then create sparks, or sound effects, etc.

                                                                                               Page 10

We are now going to set a material for the fire ball and the barrel. A MaterialPair between them will
also be created.

In EIE360Project::createScene(), add the following codes at the end:

       OgreNewt::MaterialID* fireBall_material_id = new
       OgreNewt::MaterialID* barrel_material_id = new OgreNewt::MaterialID(mWorld);
       OgreNewt::MaterialPair* pair = new OgreNewt::MaterialPair(mWorld,
fireBall_material_id, barrel_material_id );

3.7 Collision Detection with Newton
Newton (and OgreNewt) has a very powerful collision response system. As mentioned above, it is
achieved through setting Materials on objects, and then creating callbacks for when objects of a
specific pair of materials collide.

In the case of OgreNewt, you have to inherit from the ContactCallback class, and create 2
functions: onAABBOverlap and contactsProcess. The detailed mechanism is as follows:

When OgreNewt::World::update() is called, all bodies will be updated, and then the
possibility for collision is checked. When it does, the system will call your callbacks for you, like so:

onAABBOverlap This is called if the AABB of the 2 bodies in question overlap during this frame.

        This does NOT mean that they will necessarily collide, but there is a good chance. At this point,
        if you return 1 from the function, it tells Newton you want to keep processing this possible
        collision. If you return 0, it will not process the actual collision, and skip onto the next bodies to

contactsProcess This is called for every discreet contact between 2 bodies.

        In most cases there will only be once, but in some cases you may get more than one contact
        per update(). Inside this function you can get lots of information about the contact (contact
        normal, speed of collision along the normal, tangent vectors, etc). You can also alter the
        collision here (set a custom friction setting for this collision, apply acceleration along the
        contact normal, etc).

Note: Don’t dispose Bodies inside of these callback functions (also not indirectly by call of other
functions). You can get exceptions. Instead, you may save the delete information somewhere and
dispose the Body later (e.g. in the render loop).

In your project, a C++ class called FireballBoxCallback will be developed to handle a callback
when there is collision between the fire ball and the box. We pass the pointer of EIE360Project
to the class to achieve the callback action immediately. The procedure is listed as follows:

         Click Project  Add Class…, and select C++ Class, then click Add.

                                                                                                     Page 11

       Enter “FireballBoxCallback” as the class name. In the boxes .h file: and .cpp file:, enter
        “include\FireballBoxCallback.h” and “src\FireballBoxCallback.cpp”. In the box Base class:,
        enter “OgreNewt::ContactCallback”. Then click Finish.

In the file FireballBoxCallback.h, enter the following codes.

#pragma once
#include "ogrenewt_contactcallback.h"
#include "Ogre.h"

class EIE360Project;

class FireballBoxCallback :
       public OgreNewt::ContactCallback
       FireballBoxCallback(EIE360Project *pProject);

       void contactsProcess( OgreNewt::ContactJoint &contactJoint, Ogre::Real
timeStep, int threadIndex );

       EIE360Project *mProject;

In the file FireballBoxCallback.cpp, enter the following codes.

#include "..\include\FireballBoxCallback.h"

#include "EIE360Project.h"

FireballBoxCallback:: FireballBoxCallback (EIE360Project *project)
       mProject = project;

FireballBoxCallback::~ FireballBoxCallback (void)

void FireballBoxCallback::contactsProcess( OgreNewt::ContactJoint &contactJoint,
Ogre::Real timeStep, int threadIndex )
       //Add your codes here

Now, we modify our EIE360Project class to add a FireballBoxCallback class. Include
FireballBoxCallback.h in the file EIE360Project.cpp. You may assume the file is in current directory.

Then create an instance of FireballBoxCallback and pass it to the material pair we created in
the previous session. To do this, add the following codes to the end of

        FireballBoxCallback* callback = new FireballBoxCallback(this);

                                                                                             Page 12

We are going to add an explosion effect to the game when the fire ball hits the box. First, we declare
three      variables,      namely      mExplosionNode,            mExplosionParticle              and
mExplosionCountdown to the class EIE360Project. The particle system
mExplosionParticle will be attached to mExplosionNode and mExplosionCountdown
will be used to control the time period for showing the explosion effect.

In EIE360Project.h, add the following codes:

        Ogre::SceneNode *mExplosionNode;
        Ogre::ParticleSystem* mExplosionParticle;
        Ogre::Real mExplosionCountdown;

Initialize mExplosionCountdown to -1 and mExplosionParticle to NULL in the
constructor of EIE360Project.

In EIE360Project::createScene(), create a SceneNode by adding:

       mExplosionNode = mSceneMgr->getRootSceneNode()->

Add a public member function startExplosion() to the EIE360Project class (note: need
to be public member, not protected member). Modify the class FireballBoxCallback to call
startExplosion() in FireballBoxCallback::contactsProcess(). Add also the
following function startExplosion() to EIE360Project class.

void EIE360Project::startExplosion(void)
       if (mExplosionParticle == NULL)
              mExplosionParticle = mSceneMgr->createParticleSystem("Explosion",
              mExplosionCountdown = 3;

Hence every time when we call startExplosion(), we will create a particle system Explosion
and attach it to a SceneNode at an appropriate position. We also enable the countdown to control
the time period for which the explosion effect is shown.

When mExplosionCountdown becomes zero, we remove the explosion by detaching it from the
SceneNode such that we can restart the explosion for the next time. To do so, add the following
codes to EIE360Project::processCalculation():

        mExplosionCountdown -= evt.timeSinceLastFrame;
        if (mExplosionCountdown < 0 && mExplosionParticle != NULL)
               mExplosionParticle = NULL;

                                                                                             Page 13

Build and run the project. Grab the screen when the fireball hits the barrel such that an explosion
effect is shown. Paste the screen output below.

4. Exercise
Modify your program to do the following:

   1. At the moment, the dragon will stop moving when it touches down the ground. Modify your
      program such that when the dragon touches down, it will change back to walking mode.
      (Hint: you may want to use the ray scene query technique again to check if the height of the
      dragon is about on the ground.)
   2. Add four ogre heads randomly at four different locations in the sky. Add a rigid body to each
      of these ogre heads such that if the fireball hits the ogre head or the dragon hits the ogre
      head, an explosion effect will also be shown at the ogre head and the fireball disappears.

Show the results to your tutor.

Write down your codes below. Clearly indicate the functions that you have modified or added.

                                                                                           Page 14

5. References
[1] OGRE 3D,

[2] OGRE Tutorials – OGRE Wiki

[3] Ogre AppWizards

[4] SD3983 Computer Game Development II Lab sheet

[5] MSDN

[6] Newton Game Dynamics

[7] OgreNewt 2 : Ogre Wiki


[9] FMOD



                                                                                     Page 15

Shared By: