BEPU Plugin – Character Control

The BEPU Plugin, by CJ Bailey of Greedy Goblin Software, is a BEPU physics engine replacement for the inbuilt physics provide by SunBurn. If you haven’t got the BEPU Plugin Setup yet, read the proceeding post and come back.

In this post we are going to use the Character Control and build an FPS camera for it.

Say Hello to our Protagonist

The Character Control provides a physics implementation that takes care of most of the traditional player movements required for an FPS.

Lets start by setting up a new project, add in the BEPU Plugin and the load up the scene. Add a floor or ground of some description (you will need to use a model and you can not use the inbuilt terrain – if you want to use a terrain and build a mesh from that, there is a provided SunBurn plugin that was built for WP7 that’s worth checking out or alternatively have a look at blender.) – add a BEPU component of BEPUStaticMesh to your floor.

Then add a character to represent your player and add the component BEPUPlayerController

If you’ve done this right, you should have something like this:

image

 

BEPUPlayerController

The BEPUPlayerController is where all the magic happens, so lets take a closer look:

CloseUp

There are heaps of options for this component, more than most of the BEPU components:

BEPUPlayerControllerExtension

The first component, allows you to offset the character control from the parent object. It’s fine to leave these all as the default 0 position. You may need to modify it but if you don’t know that you need to, it’s unlikely you will.

Mass: Is the weight of the object, depending on what your trying to do for your Physics simulation, then you could just leave it as is.

 

BEPUPlayerControllerExtension1

AffectedByGravity: This option replaces the SunBurn built in option – the Physics options built in for the Scene Objects don’t work for the BEPU Plugin.

IsDynamic: You’ll need to leave this ticked. It allows your object to move.

Bounciness, KineticFriction & StaticFriction: Are the friction options that are common across all of the BEPU Components.

Lock X axis, Lock Y axis, Lock Z axis: These prevent your object from being rotated automatically through a physics calculation. For a normal FPS you want these left ticked. Leaving these ticked doesn’t mean that you can’t rotate the player but it does mean that you will need to rotate the player yourself rather than letting the Physics Simulation do it automatically. (That’s a good thing).

If this was a game where you wanted your player to roll around if they were hit by another object then untick these.

Radius: This should be more than 0, it’s needed to prevent the character from going through walls; for a normal human height you probably want 0.5 or 1.

Height: The height of your player, 1.7 works for me.

 

BEPUPlayerControllerExtension2

Jump Speed, Sliding Jump Speed: The Maximum speed that the player can move while jumping.

Jump Force Factor: The amount of force applied to the object when the player presses the Jump key.

Crouching Height: When the player crouches, their representative height is reduced.

Max. crouch speed: The maximum speed the player can move at while crouched.

Max Speed: Regardless of everything else, the maximum speed the player can move at.

 

Beyond the Component

Adding the BEPUPlayerController doesn’t actually move the object in an of itself. Test for yourself, the game pad wont do anything.. yet; so let’s fix that.

Before the player can be moved by code, we need a reference to the player. To get a reference that we can use in the code we need to find the object that the BEPUPlayerComponent has been attached to, then find the BEPUPlayerComponent from that scene object list. That mouthful is actually rather easy. Let’s define our using statements.

using BEPUPlugin;
using BEPUPlugin.CharacterController;

The we will need a reference to the players representative SceneObject and

BEPUCharacterController chasecontroller;
SceneObject Player = null;

Then in the update loop, find the player and get a reference to the BEPUCharacterController component:

if (Player == null)
{
    SceneInterface.ActiveSceneInterface.ObjectManager.Find(“Player”, true, out Player);
    var components = Player.Components;
    var component = components.GetComponent<BEPUCharacterController>(true);
    chasecontroller = component;
}

 

Show me your moves

Synapse Gaming has posted a demo project for their implementation of the Character Controller & has built an Input Helper class there’s some parallels between what we are trying to do so it’s a good place for us to start.

As an aside, I really appreciate that they made this class Public Static, this sort of design pattern is recognised as generally bad for commercial business software development because of all of the fun that you can get in to trouble shooting stuff but this is game development, you probably don’t have to worry about the next 100 programmers who are going to maintain this code for the next 10 years & even if your worried about this, limit it to the really important stuff that your game always needs access to, where the player is, how much health they have etc. I really like this but I digress.

For this post, we are going to assume you have an XBOX controller for player input. If you don’t, buy one (with a USB cable or get the little dongle).

In the update loop, after you have got a reference to the BEPUPlayerController component, call the input helper:

if (chasecontroller != null)
{
               Input.HandleCharacterControllerInput(gameTime, chasecontroller, SceneInterface.ActiveSceneInterface);
}

The HandleCharacterControllerInput does as the name implies; thanks to the Public Static nature of this class, we can just call it and do everything without a whole heap of additional setup.


private static float
_CharacterLookUpDownAngle = 0.0f;

public static void
HandleCharacterControllerInput(GameTime gametime, 
                     BEPUCharacterController
controller,
                     IManagerServiceProvider sceneinterface)
{
    // Get the gamepad associated with the controller.
    GamePadState gamepad = GamePad.GetState(GlobalVariables.GamePadPlayerIndex);
    if (gamepad.IsConnected)
    {
        // GamePad connected, apply the player input to the BEPUCharacterController.

        // Turn
        controller.Turn(gamepad.ThumbSticks.Right.X * 0.05f);

        // Move the player based (forwards, backwards and strafe as required)
        controller.Move(new Vector2(gamepad.ThumbSticks.Left.X, gamepad.ThumbSticks.Left.Y));

        // Adjust the players speed based on the amount from the left thumbstick
        if (gamepad.ThumbSticks.Left.Length() > 0.1)
        {
            if (!controller.IsCrouching)
            {
                controller.MaxSpeed = gamepad.ThumbSticks.Left.Length() * 5f;
            }
            else
            {
                controller.MaxSpeed = gamepad.ThumbSticks.Left.Length() * 2f;
            }
        }
        else
        {
            if (gamepad.ThumbSticks.Left.Length() < -0.1)
            {
                if (!controller.IsCrouching)
                {
                    controller.MaxSpeed = -gamepad.ThumbSticks.Left.Length() * 5f;
                }
                else
                {
                    controller.MaxSpeed = -gamepad.ThumbSticks.Left.Length() * 2f;
                }
            }
        }

        if (gamepad.Buttons.A == ButtonState.Pressed)
            controller.Jump();

        controller.IsCrouching = (gamepad.Buttons.B == ButtonState.Pressed);

        // Apply up / down view.
        _CharacterLookUpDownAngle += (float)(gamepad.ThumbSticks.Right.Y * 1.5f * gametime.ElapsedGameTime.TotalSeconds);
    }

    // Clamp the look up / down range.
    _CharacterLookUpDownAngle = MathHelper.Clamp(_CharacterLookUpDownAngle, -0.50f, 0.50f);
}

I was going to explain all of that but considering the amount of comments that exist and the obviousness of the rest of the variable names, I might just let you ask questions in the comments if any of it is a great concern.

That’s just enough to get the character walking around, crouching, jumping, turning, strafing and generally looking around a bit.

A New View

In a FPS, we don’t watch the player aimlessly run around from a far off vantage point, instead we see what they would see, from the eyes of our characters perspective. To do this, we need to move the camera to be positioned where the players eyes would be and follow the movement of the player.

To update the view, add the following to your games update loop:

// Set the view matrix.
if (chasecontroller != null)
{
    view = Input.GetCharacterControllerView(chasecontroller,
                            SceneInterface.ActiveSceneInterface);
}

 

In the Input class, add the following GetCharacterControllerView method:

public static Entity PlayerEntity;

public static Matrix GetCharacterControllerView(BEPUCharacterController controller,
IManagerServiceProvider sceneinterface)
{
    Vector3 forward = new Vector3();
    Vector3 eyelocation = Vector3.Zero;
           
    // Update our PlayerEntity reference from the BEPUPlayerController
    PlayerEntity = (controller.PhysicsEntity as Entity);
    // Get the forward direction, from the character
    forward = Vector3.Normalize(PlayerEntity.WorldTransform.Forward);
    // Set the eye location of the character
    eyelocation = PlayerEntity.Position;
    eyelocation.Y = eyelocation.Y + 0.8f;

    // Convert it to a view matrix.
    return Matrix.Invert(Matrix.CreateRotationX(_CharacterLookUpDownAngle) *
                           Matrix.CreateWorld(eyelocation, forward, Vector3.Up));
}

Wrapping It Up

Now we have a player, who can see and do player-ish type things.

Leave a Reply

Your email address will not be published. Required fields are marked *