Monday, August 3, 2015

23. Character Physics Controller Part 1

       My apologies for not posting stuff in a while. I have been working at a new job and I am still getting adjusted. An important feature of modern physics libraries is the character controller. This feature may seem obvious to implement, but it is not so straight forward for every game. This is simply due to the fact that a controller usually has to "break" the laws of physics in order to follow the user commands. In this update, I am utlizing the JigLibX physics library. This video shows the very first successful build of my basic character interacting with the physics world. The character for now is essentially a capsule which gets moved by player input. The animated character model for now is encapsulated inside the capsule. I am adding a crouch function soon for the player. Two other features I am currently adding are changing the jumping distance proportional to the character's speed, while preserving its momentum.

       I am fixing the first-person camera's clip planes so that you will not see the player's head and other triangles from the character's model mesh. In this video, I also try to show the collision skins from the models. I am working on an 'Editor' mode where you leave first-person perspective into a Free-Cam mode. From there, you will be able to access the Level Editor. I am still programming the Level Editor and adding more functionality to it so you can add, remove, rotate, scale and translate models in your levels. This following video is a blank sandbox for demonstration purposes. The building models in the scene are also for demonstration.

       Visualizing the collision skins decreases the frame rate as of now, and I am fixing this. I now got it working to where you are fully attached to the character at eye level. I am also able to move the bones of the character model so that the head and arms move and rotate according to the player's input when looking up and down. This was possible with the help of the SetBoneController function inside the AnimationController class which allows for per-bone rotation via quaternions. The SetBoneController function allows a manual bone rotation to be applied that will be blened with the current playing animation if it exists. For example, when you look down, the character's head will look down as well and you can also see your feet while the character is idling. Based on the 3.0 version of the XNAnimation library, I have added the following to my XNA 4.0 version of the library:

  • AnimationController: added per-bone rotation controllers via quaternions (SetBoneController function).
  • AnimationController: added GetBoneAbsoluteTransform to retrieve the absolute world transformation of a bone.
  • SkinnedModelBoneCollection: added a indexer for accessing bones by name (skeleton["HeadBone"] instead of skeleton[8]).

       Allowing the players to look down and see their feet and movements of the characters they control makes them feel more immersed and as if they are actually in the character's shoes and looking through the character's eyes. That way, players don't feel detached from their characters they control. Although players can see their character's hands, arms and weapons that they're holding; its good to feel as if they are that character. Now that bones can be directly accessed, I am working on scaling the bones such as making the head bigger. I am also working on an animation feature called Additive Blending which allows for two animation clips to be played from the animated model simultaneously.

Below is the source code to the character object: CharacterObject.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using JiggleGame.PhysicObjects;
using JigLibX.Physics;
using Microsoft.Xna.Framework;
using JigLibX.Collision;
using Microsoft.Xna.Framework.Graphics;
using JigLibX.Geometry;
using JigLibX.Math;
using Microsoft.Xna.Framework.Input;

namespace JiggleGame.PhysicObjects
    class CharacterObject : PhysicObject

        public Character CharacterBody {get; set;}

        public CharacterObject(Game game,Vector3 position)
            : base(game,null)
            body = new Character();
            collision = new CollisionSkin(body);

            Capsule capsule = new Capsule(Vector3.Zero, Matrix.CreateRotationX(MathHelper.PiOver2), 1.0f, 1.0f);
            collision.AddPrimitive(capsule, (int)MaterialTable.MaterialID.NotBouncyNormal);
            body.CollisionSkin = this.collision;
            Vector3 com = SetMass(1.0f);

            body.MoveTo(position + com, Matrix.Identity);
            collision.ApplyLocalTransform(new Transform(-com, Matrix.Identity));

            body.SetBodyInvInertia(0.0f, 0.0f, 0.0f);

            CharacterBody = body as Character;

            body.AllowFreezing = false;

        public override void ApplyEffects(BasicEffect effect)
            //throw new NotImplementedException();

    class ASkinPredicate : CollisionSkinPredicate1
        public override bool ConsiderSkin(CollisionSkin skin0)
            if (!(skin0.Owner is Character))
                return true;
                return false;

    class Character : Body

        public Character() : base()

        public Vector3 DesiredVelocity { get; set; }

        private bool doJump = false;

        public void DoJump()
            doJump = true;

        public override void AddExternalForces(float dt)

            if (doJump)
                foreach (CollisionInfo info in CollisionSkin.Collisions)
                    Vector3 N = info.DirToBody0;
                    if (this == info.SkinInfo.Skin1.Owner)
                        Vector3.Negate(ref N, out N);

                    if (Vector3.Dot(N, Orientation.Up) > 0.7f)
                        Vector3 vel = Velocity; vel.Y = 5.0f;
                        Velocity = vel;

            Vector3 deltaVel = DesiredVelocity - Velocity;

            bool running = true;

            if (DesiredVelocity.LengthSquared() < JiggleMath.Epsilon) running = false;
            else deltaVel.Normalize();

            deltaVel.Y = 0.0f;

            // start fast, slow down slower
            if (running) deltaVel *= 10.0f;
            else deltaVel *= 2.0f;

            float forceFactor = 1000.0f;

            AddBodyForce(deltaVel * Mass * dt * forceFactor);

            doJump = false;



No comments:

Post a Comment