Click or drag to resize
DigitalRuneStep 11: Physics

In this step we will add the physics simulation.

Add physics service

Let's add the physics service to the game:

Game1.cs
using DigitalRune.Physics;                                                              // NEWnamespace MyGame
{
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        …
        private Simulation _simulation;                                                 // NEW
        …

        protected override void Initialize()
        {
            …
            _animationManager = new AnimationManager();
            _services.Register(typeof(IAnimationService), null, _animationManager);

            _simulation = new Simulation();                                             // NEW
            _simulation.ForceEffects.Add(new Gravity());                                // NEW
            _simulation.ForceEffects.Add(new Damping());                                // NEW
            _services.Register(typeof(Simulation), null, _simulation);                  // NEW

            Components.Add(new MyGameComponent(this));

            base.Initialize();
        }

        protected override void Update(GameTime gameTime)
        {
            _inputManager.Update(gameTime.ElapsedGameTime);

            base.Update(gameTime);

            _gameObjectManager.Update(gameTime.ElapsedGameTime);

            _simulation.Update(gameTime.ElapsedGameTime);                               // NEW

            _animationManager.Update(gameTime.ElapsedGameTime);
            _animationManager.ApplyAnimations();
        }
        …

The physics service is represented by the class Simulation. (The physics library was one of our first products. If we redesign the physics library in the future we will probably rename Simulation to IPhysicsService and PhysicsManager to use the same naming convention as the other libraries.)

After we create the simulation, we immediately add two force effects: Gravity and Damping. Unless you make a game in space, you will need these force effects.

Add a rigid body to the GroundObject

So far the ground plane does not have a physical representation. Let's add a rigid body which represents the ground in GroundObject.cs:

GroundObject.cs
using DigitalRune.Geometry.Shapes;                                              // NEW
using DigitalRune.Physics;                                                      // NEWnamespace MyGame
{
    public class GroundObject : GameObject
    {
        private ModelNode _modelNode;
        private RigidBody _rigidBody;                                           // NEW

        protected override void OnLoad()
        {
            var game = ServiceLocator.Current.GetInstance<Game>();
            var scene = ServiceLocator.Current.GetInstance<IScene>();
            var simulation = ServiceLocator.Current.GetInstance<Simulation>();  // NEW

            _modelNode = game.Content.Load<ModelNode>("Ground/Ground").Clone();
            _modelNode.ScaleLocal = new Vector3F(0.5f);
            scene.Children.Add(_modelNode);

            _rigidBody = new RigidBody(new PlaneShape(Vector3F.UnitY, 0))       // NEW
            {                                                                   // NEW
                MotionType = MotionType.Static,                                 // NEW
            };                                                                  // NEW
          simulation.RigidBodies.Add(_rigidBody);
        }

        protected override void OnUnload()
        {
            _modelNode.Parent.Children.Remove(_modelNode);
            _modelNode.Dispose(false);
            _modelNode = null;

            _rigidBody.Simulation.RigidBodies.Remove(_rigidBody);               // NEW
            _rigidBody = null;                                                  // NEW
        }

        protected override void OnUpdate(TimeSpan deltaTime)
        {
        }
    }
}

Now the ground is represented in the physics simulation as an infinite plane. The MotionType of the ground is Static. This means, that it will never move.

Add a dynamic crate

Next, we add a physics object which moves using a new game object: CrateObject.cs

CrateObject.cs
using System;
using DigitalRune.Game;
using DigitalRune.Geometry;
using DigitalRune.Geometry.Shapes;
using DigitalRune.Graphics.SceneGraph;
using DigitalRune.Mathematics.Algebra;
using DigitalRune.Mathematics.Statistics;
using DigitalRune.Physics;
using Microsoft.Practices.ServiceLocation;
using Microsoft.Xna.Framework;

namespace MyGame
{
    public class CrateObject : GameObject
    {
        private ModelNode _modelNode;
        private RigidBody _rigidBody;

        protected override void OnLoad()
        {
            var game = ServiceLocator.Current.GetInstance<Game>();
            var scene = ServiceLocator.Current.GetInstance<IScene>();
            var simulation = ServiceLocator.Current.GetInstance<Simulation>();

            _modelNode = game.Content.Load<ModelNode>("MetalGrateBox/MetalGrateBox").Clone();
            scene.Children.Add(_modelNode);

            _rigidBody = new RigidBody(new BoxShape(1, 1, 1));
            _rigidBody.Pose = new Pose(new Vector3F(2, 2, -2), RandomHelper.Random.NextQuaternionF());
            simulation.RigidBodies.Add(_rigidBody);
        }

        protected override void OnUnload()
        {
            _modelNode.Parent.Children.Remove(_modelNode);
            _modelNode.Dispose(false);
            _modelNode = null;

            _rigidBody.Simulation.RigidBodies.Remove(_rigidBody);
            _rigidBody = null;
        }

        protected override void OnUpdate(TimeSpan deltaTime)
        {
            _modelNode.SetLastPose(true);
            _modelNode.PoseWorld = _rigidBody.Pose;
        }
    }
}

The crate object uses the MetalGrateBox.fbx model. It also creates a rigid body which has a box shape. The shape of the box must match size of the graphics model.

The ModelNode is handled by the graphics service. The RigidBody is handled by the physics service. In our case, the model should always have the same pose (position and orientation) as the physics body. To do this, we copy the pose of the rigid body to the pose of the model in OnUpdate.

But before we override the pose of the model, we call SetLastPose(recursive = true). This method stores the current SceneNode.PoseWorld in SceneNode.LastPoseWorld. This is done recursively for the ModelNode and all child nodes. LastPoseWorld may be required later by certain effects, such as motion blur.

In MyGameComponent we create an instance of CrateObject:

Game1.cs
namespace MyGame
{
    public class MyGameComponent : Microsoft.Xna.Framework.GameComponent
    {
        …

        public MyGameComponent(Game game)
            : base(game)
        {
            …

            gameObjectService.Objects.Add(new GroundObject());
            gameObjectService.Objects.Add(new LightsObject());
            gameObjectService.Objects.Add(new DudeObject());
            gameObjectService.Objects.Add(new CrateObject());                       // NEW

            _myGraphicsScreen.DebugRenderer.DrawText("MyGame");
            _myGraphicsScreen.DebugRenderer.DrawAxes(Pose.Identity, 1, false);
        }
        …

Current state of the game:

Tutorial-01-19
Interlude: Game engine design – Game logic and services

The CrateObject demonstrates an important aspect of our game engine design:

  • Game logic is encapsulated in reusable game objects (e.g. CreateObject).
  • The game object creates objects in the different services (e.g. graphics and physics).
  • Services can be discovered through the service provider.
  • Services are separated: Each service (e.g. graphics and physics) is designed and optimized independently. Both services could be executed in parallel (in case you want to create a multithreaded game).
  • The game object is used to exchange data between the different services. In this example, it copies the pose of the physics object to the pose of the graphics object. In other instances it might be the other way around. This kind of code is the glue logic that connects different services.