ogre RPG Character Camera Controller code to Mogre

andyhebear1

04-02-2013 10:22:39

look ogre Sample_Character demo:
this camera Controller code in c# :

class SinbadCharacterController {
//write by: rains
//blog : http://hi.baidu.com/rainssoft
//web : http://www.masswig.com
//mail : andyhebear#gmail.com , andyhebear#hotmail.com
enum AnimID : byte {
ANIM_IDLE_BASE,
ANIM_IDLE_TOP,
ANIM_RUN_BASE,
ANIM_RUN_TOP,
ANIM_HANDS_CLOSED,
ANIM_HANDS_RELAXED,
ANIM_DRAW_SWORDS,
ANIM_SLICE_VERTICAL,
ANIM_SLICE_HORIZONTAL,
ANIM_DANCE,
ANIM_JUMP_START,
ANIM_JUMP_LOOP,
ANIM_JUMP_END,
ANIM_NONE
}

const int NUM_ANIMS = 13; // number of animations the character has
const int CHAR_HEIGHT = 5; // height of character's center of mass above ground
const int CAM_HEIGHT = 2; // height of camera above character's center of mass
const int RUN_SPEED = 17; // character running speed in units per second
const float TURN_SPEED = 500.0f; // character turning in degrees per second
const float ANIM_FADE_SPEED = 7.5f; // animation crossfade speed in % of full weight per second
const float JUMP_ACCEL = 30.0f; // character jump acceleration in upward units per squared second
const float GRAVITY = 90.0f; // gravity in downward units per squared second
//
public SinbadCharacterController(Camera cam) {
setupBody(cam.SceneManager);
setupCamera(cam);
setupAnimations();
}
public void addTime(float deltaTime) {
updateBody(deltaTime);
updateAnimations(deltaTime);
updateCamera(deltaTime);
}
public void injectKeyDown(MOIS.KeyEvent evt) {
if (evt.key == MOIS.KeyCode.KC_Q && (mTopAnimID == AnimID.ANIM_IDLE_TOP || mTopAnimID == AnimID.ANIM_RUN_TOP)) {
// take swords out (or put them back, since it's the same animation but reversed)
setTopAnimation(AnimID.ANIM_DRAW_SWORDS, true);
mTimer = 0;
}
else if (evt.key == MOIS.KeyCode.KC_E && !mSwordsDrawn) {
if (mTopAnimID == AnimID.ANIM_IDLE_TOP || mTopAnimID == AnimID.ANIM_RUN_TOP) {
// start dancing
setBaseAnimation(AnimID.ANIM_DANCE, true);
setTopAnimation(AnimID.ANIM_NONE);
// disable hand animation because the dance controls hands
mAnims[(int)AnimID.ANIM_HANDS_RELAXED].Enabled = (false);
}
else if (mBaseAnimID == AnimID.ANIM_DANCE) {
// stop dancing
setBaseAnimation(AnimID.ANIM_IDLE_BASE);
setTopAnimation(AnimID.ANIM_IDLE_TOP);
// re-enable hand animation
mAnims[(int)AnimID.ANIM_HANDS_RELAXED].Enabled=(true);
}
}

// keep track of the player's intended direction
else if (evt.key == MOIS.KeyCode.KC_W) mKeyDirection.z = -1;
else if (evt.key == MOIS.KeyCode.KC_A) mKeyDirection.x = -1;
else if (evt.key == MOIS.KeyCode.KC_S) mKeyDirection.z = 1;
else if (evt.key == MOIS.KeyCode.KC_D) mKeyDirection.x = 1;

else if (evt.key == MOIS.KeyCode.KC_SPACE && (mTopAnimID == AnimID.ANIM_IDLE_TOP || mTopAnimID == AnimID.ANIM_RUN_TOP)) {
// jump if on ground
setBaseAnimation(AnimID.ANIM_JUMP_START, true);
setTopAnimation(AnimID.ANIM_NONE);
mTimer = 0;
}

if (!mKeyDirection.IsZeroLength && mBaseAnimID == AnimID.ANIM_IDLE_BASE) {
// start running if not already moving and the player wants to move
setBaseAnimation(AnimID.ANIM_RUN_BASE, true);
if (mTopAnimID == AnimID.ANIM_IDLE_TOP) setTopAnimation(AnimID.ANIM_RUN_TOP, true);
}
}

public void injectKeyUp(MOIS.KeyEvent evt) {
// keep track of the player's intended direction
if (evt.key == MOIS.KeyCode.KC_W && mKeyDirection.z == -1) mKeyDirection.z = 0;
else if (evt.key == MOIS.KeyCode.KC_A && mKeyDirection.x == -1) mKeyDirection.x = 0;
else if (evt.key == MOIS.KeyCode.KC_S && mKeyDirection.z == 1) mKeyDirection.z = 0;
else if (evt.key == MOIS.KeyCode.KC_D && mKeyDirection.x == 1) mKeyDirection.x = 0;

if (mKeyDirection.IsZeroLength && mBaseAnimID == AnimID.ANIM_RUN_BASE) {
// stop running if already moving and the player doesn't want to move
setBaseAnimation(AnimID.ANIM_IDLE_BASE);
if (mTopAnimID == AnimID.ANIM_RUN_TOP) setTopAnimation(AnimID.ANIM_IDLE_TOP);
}
}

#if OGRE_PLATFORM_IPHONE
void injectMouseMove( MOIS.MultiTouchEvent evt)
{
// update camera goal based on mouse movement
updateCameraGoal(-0.05f * evt.state.X.rel, -0.05f * evt.state.Y.rel, -0.0005f * evt.state.Z.rel);
}

void injectMouseDown( MOIS.MultiTouchEvent evt)
{
if (mSwordsDrawn && (mTopAnimID == ANIM_IDLE_TOP || mTopAnimID == ANIM_RUN_TOP))
{
// if swords are out, and character's not doing something weird, then SLICE!
setTopAnimation(ANIM_SLICE_VERTICAL, true);
mTimer = 0;
}
}
#else
public void injectMouseMove(MOIS.MouseEvent evt) {
// update camera goal based on mouse movement
updateCameraGoal(-0.05f * evt.state.X.rel, -0.05f * evt.state.Y.rel, -0.0005f * evt.state.Z.rel);
}

public void injectMouseDown(MOIS.MouseEvent evt, MOIS.MouseButtonID id) {
if (mSwordsDrawn && (mTopAnimID == AnimID.ANIM_IDLE_TOP || mTopAnimID == AnimID.ANIM_RUN_TOP)) {
// if swords are out, and character's not doing something weird, then SLICE!
if (id == MOIS.MouseButtonID.MB_Left) setTopAnimation(AnimID.ANIM_SLICE_VERTICAL, true);
else if (id == MOIS.MouseButtonID.MB_Right) setTopAnimation(AnimID.ANIM_SLICE_HORIZONTAL, true);
mTimer = 0;
}
}
#endif
//
Camera mCamera;
SceneNode mBodyNode;
SceneNode mCameraPivot;
SceneNode mCameraGoal;
SceneNode mCameraNode;
float mPivotPitch;
Entity mBodyEnt;
Entity mSword1;
Entity mSword2;
RibbonTrail mSwordTrail;
AnimationState[] mAnims = new AnimationState[NUM_ANIMS]; // master animation list
AnimID mBaseAnimID; // current base (full- or lower-body) animation
AnimID mTopAnimID; // current top (upper-body) animation
bool[] mFadingIn = new bool[NUM_ANIMS]; // which animations are fading in
bool[] mFadingOut = new bool[NUM_ANIMS]; // which animations are fading out
bool mSwordsDrawn;
Vector3 mKeyDirection; // player's local intended direction based on WASD keys
Vector3 mGoalDirection; // actual intended direction in world-space
float mVerticalVelocity; // for jumping
float mTimer; // general timer to see how long animations have been playing
//
void setupBody(SceneManager sceneMgr) {
// create main model
mBodyNode = sceneMgr.RootSceneNode.CreateChildSceneNode(Vector3.UNIT_Y * CHAR_HEIGHT);
mBodyEnt = sceneMgr.CreateEntity("SinbadBody", "Sinbad.mesh");
mBodyNode.AttachObject(mBodyEnt);

// create swords and attach to sheath
mSword1 = sceneMgr.CreateEntity("SinbadSword1", "Sword.mesh");
mSword2 = sceneMgr.CreateEntity("SinbadSword2", "Sword.mesh");
mBodyEnt.AttachObjectToBone("Sheath.L", mSword1);
mBodyEnt.AttachObjectToBone("Sheath.R", mSword2);
// create a couple of ribbon trails for the swords, just for fun
NameValuePairList params_ = new NameValuePairList();
params_.Insert("numberOfChains", "2");
params_.Insert("maxElements", "80");
mSwordTrail = new RibbonTrail("RibbonTrail");// (RibbonTrail)sceneMgr.CreateMovableObject("RibbonTrail", params_);
mSwordTrail.MaterialName = ("Examples/LightRibbonTrail");
mSwordTrail.TrailLength = (20);
mSwordTrail.Visible = (false);
sceneMgr.RootSceneNode.AttachObject(mSwordTrail);
//
for (uint i = 0; i < 2; i++) {
mSwordTrail.SetInitialColour(i, 1f, 0.8f, 0f);
mSwordTrail.SetColourChange(i, 0.75f, 1.25f, 1.25f, 1.25f);
mSwordTrail.SetWidthChange(i, 1f);
mSwordTrail.SetInitialWidth(i, 0.5f);
}

mKeyDirection = Vector3.ZERO;
mVerticalVelocity = 0;
}

void setupAnimations() {
// this is very important due to the nature of the exported animations
mBodyEnt.Skeleton.BlendMode = SkeletonAnimationBlendMode.ANIMBLEND_CUMULATIVE;

string[] animNames = new string[]
{"IdleBase", "IdleTop", "RunBase", "RunTop", "HandsClosed", "HandsRelaxed", "DrawSwords",
"SliceVertical", "SliceHorizontal", "Dance", "JumpStart", "JumpLoop", "JumpEnd"};

// populate our animation list
for (int i = 0; i < NUM_ANIMS; i++) {
mAnims = mBodyEnt.GetAnimationState(animNames);
mAnims.Loop = (true);
mFadingIn = false;
mFadingOut = false;
}

// start off in the idle state (top and bottom together)
setBaseAnimation(AnimID.ANIM_IDLE_BASE);
setTopAnimation(AnimID.ANIM_IDLE_TOP);

// relax the hands since we're not holding anything
mAnims[(int)AnimID.ANIM_HANDS_RELAXED].Enabled = (true);

mSwordsDrawn = false;
}

void setupCamera(Camera cam) {
// create a pivot at roughly the character's shoulder
mCameraPivot = cam.SceneManager.RootSceneNode.CreateChildSceneNode();
// this is where the camera should be soon, and it spins around the pivot
mCameraGoal = mCameraPivot.CreateChildSceneNode(new Vector3(0f, 0f, 15f));
// this is where the camera actually is
mCameraNode = cam.SceneManager.RootSceneNode.CreateChildSceneNode();
Vector3 cpos = mCameraPivot.Position + mCameraGoal.Position;
mCameraNode.SetPosition(cpos.x,cpos.y,cpos.z);

mCameraPivot.SetFixedYawAxis(true);
mCameraGoal.SetFixedYawAxis(true);
mCameraNode.SetFixedYawAxis(true);

// our model is quite small, so reduce the clipping planes
cam.NearClipDistance = (0.1f);
cam.FarClipDistance = (100f);
mCameraNode.AttachObject(cam);

mPivotPitch = 0;
}

void updateBody(float deltaTime) {
mGoalDirection = Vector3.ZERO; // we will calculate this

if (mKeyDirection != Vector3.ZERO && mBaseAnimID != AnimID.ANIM_DANCE) {
// calculate actually goal direction in world based on player's key directions
mGoalDirection += mKeyDirection.z * mCameraNode.Orientation.ZAxis;
mGoalDirection += mKeyDirection.x * mCameraNode.Orientation.XAxis;
mGoalDirection.y = 0;
mGoalDirection.Normalise();
mGoalDirection = mGoalDirection.NormalisedCopy;

Quaternion toGoal = mBodyNode.Orientation.ZAxis.GetRotationTo(mGoalDirection);

// calculate how much the character has to turn to face goal direction
float yawToGoal = toGoal.Yaw.ValueDegrees;
// this is how much the character CAN turn this frame
float yawAtSpeed = yawToGoal / Mogre.Math.Abs(yawToGoal) * deltaTime * TURN_SPEED;
// reduce "turnability" if we're in midair
if (mBaseAnimID == AnimID.ANIM_JUMP_LOOP) yawAtSpeed *= 0.2f;

// turn as much as we can, but not more than we need to
if (yawToGoal < 0) yawToGoal = (float)System.Math.Min(0f, (float)System.Math.Max(yawToGoal, yawAtSpeed));//yawToGoal = std::min<Real>(0, std::max<Real>(yawToGoal, yawAtSpeed)); //yawToGoal = Math::Clamp<Real>(yawToGoal, yawAtSpeed, 0);
else if (yawToGoal > 0) yawToGoal = (float)System.Math.Max(0, (float)System.Math.Min(yawToGoal, yawAtSpeed));//yawToGoal = std::max<Real>(0, std::min<Real>(yawToGoal, yawAtSpeed)); //yawToGoal = Math::Clamp<Real>(yawToGoal, 0, yawAtSpeed);

mBodyNode.Yaw(new Degree(yawToGoal));

// move in current body direction (not the goal direction)
mBodyNode.Translate(0f, 0f, deltaTime * RUN_SPEED * mAnims[(int)mBaseAnimID].Weight, Node.TransformSpace.TS_LOCAL);
}

if (mBaseAnimID == AnimID.ANIM_JUMP_LOOP) {
// if we're jumping, add a vertical offset too, and apply gravity
mBodyNode.Translate(0f, mVerticalVelocity * deltaTime, 0f, Node.TransformSpace.TS_LOCAL);
mVerticalVelocity -= GRAVITY * deltaTime;

Vector3 pos = mBodyNode.Position;
if (pos.y <= CHAR_HEIGHT) {
// if we've hit the ground, change to landing state
pos.y = CHAR_HEIGHT;
mBodyNode.SetPosition(pos.x, pos.y, pos.z);
setBaseAnimation(AnimID.ANIM_JUMP_END, true);
mTimer = 0;
}
}
}

void updateAnimations(float deltaTime) {
float baseAnimSpeed = 1;
float topAnimSpeed = 1;

mTimer += deltaTime;

if (mTopAnimID == AnimID.ANIM_DRAW_SWORDS) {
// flip the draw swords animation if we need to put it back
topAnimSpeed = mSwordsDrawn ? -1 : 1;

// half-way through the animation is when the hand grasps the handles...
if (mTimer >= mAnims[(int)mTopAnimID].Length / 2 &&
mTimer - deltaTime < mAnims[(int)mTopAnimID].Length / 2) {
// so transfer the swords from the sheaths to the hands
mBodyEnt.DetachAllObjectsFromBone();
mBodyEnt.AttachObjectToBone(mSwordsDrawn ? "Sheath.L" : "Handle.L", mSword1);
mBodyEnt.AttachObjectToBone(mSwordsDrawn ? "Sheath.R" : "Handle.R", mSword2);
// change the hand state to grab or let go
mAnims[(int)AnimID.ANIM_HANDS_CLOSED].Enabled = (!mSwordsDrawn);
mAnims[(int)AnimID.ANIM_HANDS_RELAXED].Enabled = (mSwordsDrawn);

// toggle sword trails
if (mSwordsDrawn) {
mSwordTrail.Visible = (false);
mSwordTrail.RemoveNode(mSword1.ParentNode);
mSwordTrail.RemoveNode(mSword2.ParentNode);
}
else {
mSwordTrail.Visible = (true);
mSwordTrail.AddNode(mSword1.ParentNode);
mSwordTrail.AddNode(mSword2.ParentNode);
}
}

if (mTimer >= mAnims[(int)mTopAnimID].Length) {
// animation is finished, so return to what we were doing before
if (mBaseAnimID == AnimID.ANIM_IDLE_BASE) setTopAnimation(AnimID.ANIM_IDLE_TOP);
else {
setTopAnimation(AnimID.ANIM_RUN_TOP);
mAnims[(int)AnimID.ANIM_RUN_TOP].TimePosition = (mAnims[(int)AnimID.ANIM_RUN_BASE].TimePosition);
}
mSwordsDrawn = !mSwordsDrawn;
}
}
else if (mTopAnimID == AnimID.ANIM_SLICE_VERTICAL || mTopAnimID == AnimID.ANIM_SLICE_HORIZONTAL) {
if (mTimer >= mAnims[(int)mTopAnimID].Length) {
// animation is finished, so return to what we were doing before
if (mBaseAnimID == AnimID.ANIM_IDLE_BASE) setTopAnimation(AnimID.ANIM_IDLE_TOP);
else {
setTopAnimation(AnimID.ANIM_RUN_TOP);
mAnims[(int)AnimID.ANIM_RUN_TOP].TimePosition = (mAnims[(int)AnimID.ANIM_RUN_BASE].TimePosition);
}
}

// don't sway hips from side to side when slicing. that's just embarrasing.
if (mBaseAnimID == AnimID.ANIM_IDLE_BASE) baseAnimSpeed = 0;
}
else if (mBaseAnimID == AnimID.ANIM_JUMP_START) {
if (mTimer >= mAnims[(int)mBaseAnimID].Length) {
// takeoff animation finished, so time to leave the ground!
setBaseAnimation(AnimID.ANIM_JUMP_LOOP, true);
// apply a jump acceleration to the character
mVerticalVelocity = JUMP_ACCEL;
}
}
else if (mBaseAnimID == AnimID.ANIM_JUMP_END) {
if (mTimer >= mAnims[(int)mBaseAnimID].Length) {
// safely landed, so go back to running or idling
if (mKeyDirection == Vector3.ZERO) {
setBaseAnimation(AnimID.ANIM_IDLE_BASE);
setTopAnimation(AnimID.ANIM_IDLE_TOP);
}
else {
setBaseAnimation(AnimID.ANIM_RUN_BASE, true);
setTopAnimation(AnimID.ANIM_RUN_TOP, true);
}
}
}

// increment the current base and top animation times
if (mBaseAnimID != AnimID.ANIM_NONE) mAnims[(int)mBaseAnimID].AddTime(deltaTime * baseAnimSpeed);
if (mTopAnimID != AnimID.ANIM_NONE) mAnims[(int)mTopAnimID].AddTime(deltaTime * topAnimSpeed);

// apply smooth transitioning between our animations
fadeAnimations(deltaTime);
}

void fadeAnimations(float deltaTime) {
for (int i = 0; i < NUM_ANIMS; i++) {
if (mFadingIn) {
// slowly fade this animation in until it has full weight
float newWeight = mAnims.Weight + deltaTime * ANIM_FADE_SPEED;
mAnims.Weight = (Clamp<float>(newWeight, 0f, 1f));
if (newWeight >= 1) mFadingIn = false;
}
else if (mFadingOut) {
// slowly fade this animation out until it has no weight, and then disable it
float newWeight = mAnims.Weight - deltaTime * ANIM_FADE_SPEED;
mAnims.Weight = (Clamp<float>(newWeight, 0f, 1f));
if (newWeight <= 0) {
mAnims.Enabled = (false);
mFadingOut = false;
}
}
}
}

private T Clamp<T>(T value, T max, T min) where T : System.IComparable<T> {
var result = value;
if (value.CompareTo(max) > 0) {
result = max;
}
if (value.CompareTo(min) < 0) {
result = min;
}
return result;
}
private float _getDistance(Vector3 p1, Vector3 p2) {
//TODO:
return (p1-p2).Length;
}
void updateCamera(float deltaTime) {
// place the camera pivot roughly at the character's shoulder
Vector3 mpos = mBodyNode.Position + Vector3.UNIT_Y * CAM_HEIGHT;
mCameraPivot.SetPosition(mpos.x, mpos.y, mpos.z);
// move the camera smoothly to the goal
Vector3 goalOffset = mCameraGoal._getDerivedPosition() - mCameraNode.Position;
mCameraNode.Translate(goalOffset * deltaTime * 9.0f);
// always look at the pivot
mCameraNode.LookAt(mCameraPivot._getDerivedPosition(), Node.TransformSpace.TS_WORLD);
}

void updateCameraGoal(float deltaYaw, float deltaPitch, float deltaZoom) {
mCameraPivot.Yaw(new Degree(deltaYaw), Node.TransformSpace.TS_WORLD);

// bound the pitch
if (!(mPivotPitch + deltaPitch > 25f && deltaPitch > 0f) &&
!(mPivotPitch + deltaPitch < -60f && deltaPitch < 0f)) {
mCameraPivot.Pitch(new Degree(deltaPitch), Node.TransformSpace.TS_LOCAL);
mPivotPitch += deltaPitch;
}

//float dist = mCameraGoal._getDerivedPosition().DotProduct/*distance*/(mCameraPivot._getDerivedPosition());
float dist = _getDistance(mCameraGoal._getDerivedPosition(),mCameraPivot._getDerivedPosition());
//
float distChange = deltaZoom * dist;

// bound the zoom
if (!(dist + distChange < 8 && distChange < 0) &&
!(dist + distChange > 25 && distChange > 0)) {
mCameraGoal.Translate(0f, 0f, distChange, Node.TransformSpace.TS_LOCAL);
}
}
void setBaseAnimation(AnimID id) {
setBaseAnimation(id, false);
}
void setBaseAnimation(AnimID id, bool reset) {
if (mBaseAnimID >= 0 && (int)mBaseAnimID < NUM_ANIMS) {
// if we have an old animation, fade it out
mFadingIn[(int)mBaseAnimID] = false;
mFadingOut[(int)mBaseAnimID] = true;
}

mBaseAnimID = id;

if (id != AnimID.ANIM_NONE) {
// if we have a new animation, enable it and fade it in
mAnims[(int)id].Enabled = (true);
mAnims[(int)id].Weight = (0);
mFadingOut[(int)id] = false;
mFadingIn[(int)id] = true;
if (reset) mAnims[(int)id].TimePosition = (0);
}
}
void setTopAnimation(AnimID id) {
setTopAnimation(id, false);
}
void setTopAnimation(AnimID id, bool reset) {
if (mTopAnimID >= 0 && (int)mTopAnimID < NUM_ANIMS) {
// if we have an old animation, fade it out
mFadingIn[(int)mTopAnimID] = false;
mFadingOut[(int)mTopAnimID] = true;
}

mTopAnimID = id;

if (id != AnimID.ANIM_NONE) {
// if we have a new animation, enable it and fade it in
mAnims[(int)id].Enabled = (true);
mAnims[(int)id].Weight = (0);
mFadingOut[(int)id] = false;
mFadingIn[(int)id] = true;
if (reset) mAnims[(int)id].TimePosition = (0);
}
}

}



c++ SinbadCharacterController.h


#ifndef __Sinbad_H__
#define __Sinbad_H__

#include "Ogre.h"
#include "OIS.h"

using namespace Ogre;

#define NUM_ANIMS 13 // number of animations the character has
#define CHAR_HEIGHT 5 // height of character's center of mass above ground
#define CAM_HEIGHT 2 // height of camera above character's center of mass
#define RUN_SPEED 17 // character running speed in units per second
#define TURN_SPEED 500.0f // character turning in degrees per second
#define ANIM_FADE_SPEED 7.5f // animation crossfade speed in % of full weight per second
#define JUMP_ACCEL 30.0f // character jump acceleration in upward units per squared second
#define GRAVITY 90.0f // gravity in downward units per squared second

class SinbadCharacterController
{
private:

// all the animations our character has, and a null ID
// some of these affect separate body parts and will be blended together
enum AnimID
{
ANIM_IDLE_BASE,
ANIM_IDLE_TOP,
ANIM_RUN_BASE,
ANIM_RUN_TOP,
ANIM_HANDS_CLOSED,
ANIM_HANDS_RELAXED,
ANIM_DRAW_SWORDS,
ANIM_SLICE_VERTICAL,
ANIM_SLICE_HORIZONTAL,
ANIM_DANCE,
ANIM_JUMP_START,
ANIM_JUMP_LOOP,
ANIM_JUMP_END,
ANIM_NONE
};

public:

SinbadCharacterController(Camera* cam)
{
setupBody(cam->getSceneManager());
setupCamera(cam);
setupAnimations();
}

void addTime(Real deltaTime)
{
updateBody(deltaTime);
updateAnimations(deltaTime);
updateCamera(deltaTime);
}

void injectKeyDown(const OIS::KeyEvent& evt)
{
if (evt.key == OIS::KC_Q && (mTopAnimID == ANIM_IDLE_TOP || mTopAnimID == ANIM_RUN_TOP))
{
// take swords out (or put them back, since it's the same animation but reversed)
setTopAnimation(ANIM_DRAW_SWORDS, true);
mTimer = 0;
}
else if (evt.key == OIS::KC_E && !mSwordsDrawn)
{
if (mTopAnimID == ANIM_IDLE_TOP || mTopAnimID == ANIM_RUN_TOP)
{
// start dancing
setBaseAnimation(ANIM_DANCE, true);
setTopAnimation(ANIM_NONE);
// disable hand animation because the dance controls hands
mAnims[ANIM_HANDS_RELAXED]->setEnabled(false);
}
else if (mBaseAnimID == ANIM_DANCE)
{
// stop dancing
setBaseAnimation(ANIM_IDLE_BASE);
setTopAnimation(ANIM_IDLE_TOP);
// re-enable hand animation
mAnims[ANIM_HANDS_RELAXED]->setEnabled(true);
}
}

// keep track of the player's intended direction
else if (evt.key == OIS::KC_W) mKeyDirection.z = -1;
else if (evt.key == OIS::KC_A) mKeyDirection.x = -1;
else if (evt.key == OIS::KC_S) mKeyDirection.z = 1;
else if (evt.key == OIS::KC_D) mKeyDirection.x = 1;

else if (evt.key == OIS::KC_SPACE && (mTopAnimID == ANIM_IDLE_TOP || mTopAnimID == ANIM_RUN_TOP))
{
// jump if on ground
setBaseAnimation(ANIM_JUMP_START, true);
setTopAnimation(ANIM_NONE);
mTimer = 0;
}

if (!mKeyDirection.isZeroLength() && mBaseAnimID == ANIM_IDLE_BASE)
{
// start running if not already moving and the player wants to move
setBaseAnimation(ANIM_RUN_BASE, true);
if (mTopAnimID == ANIM_IDLE_TOP) setTopAnimation(ANIM_RUN_TOP, true);
}
}

void injectKeyUp(const OIS::KeyEvent& evt)
{
// keep track of the player's intended direction
if (evt.key == OIS::KC_W && mKeyDirection.z == -1) mKeyDirection.z = 0;
else if (evt.key == OIS::KC_A && mKeyDirection.x == -1) mKeyDirection.x = 0;
else if (evt.key == OIS::KC_S && mKeyDirection.z == 1) mKeyDirection.z = 0;
else if (evt.key == OIS::KC_D && mKeyDirection.x == 1) mKeyDirection.x = 0;

if (mKeyDirection.isZeroLength() && mBaseAnimID == ANIM_RUN_BASE)
{
// stop running if already moving and the player doesn't want to move
setBaseAnimation(ANIM_IDLE_BASE);
if (mTopAnimID == ANIM_RUN_TOP) setTopAnimation(ANIM_IDLE_TOP);
}
}

#if OGRE_PLATFORM == OGRE_PLATFORM_IPHONE
void injectMouseMove(const OIS::MultiTouchEvent& evt)
{
// update camera goal based on mouse movement
updateCameraGoal(-0.05f * evt.state.X.rel, -0.05f * evt.state.Y.rel, -0.0005f * evt.state.Z.rel);
}

void injectMouseDown(const OIS::MultiTouchEvent& evt)
{
if (mSwordsDrawn && (mTopAnimID == ANIM_IDLE_TOP || mTopAnimID == ANIM_RUN_TOP))
{
// if swords are out, and character's not doing something weird, then SLICE!
setTopAnimation(ANIM_SLICE_VERTICAL, true);
mTimer = 0;
}
}
#else
void injectMouseMove(const OIS::MouseEvent& evt)
{
// update camera goal based on mouse movement
updateCameraGoal(-0.05f * evt.state.X.rel, -0.05f * evt.state.Y.rel, -0.0005f * evt.state.Z.rel);
}

void injectMouseDown(const OIS::MouseEvent& evt, OIS::MouseButtonID id)
{
if (mSwordsDrawn && (mTopAnimID == ANIM_IDLE_TOP || mTopAnimID == ANIM_RUN_TOP))
{
// if swords are out, and character's not doing something weird, then SLICE!
if (id == OIS::MB_Left) setTopAnimation(ANIM_SLICE_VERTICAL, true);
else if (id == OIS::MB_Right) setTopAnimation(ANIM_SLICE_HORIZONTAL, true);
mTimer = 0;
}
}
#endif

private:

void setupBody(SceneManager* sceneMgr)
{
// create main model
mBodyNode = sceneMgr->getRootSceneNode()->createChildSceneNode(Vector3::UNIT_Y * CHAR_HEIGHT);
mBodyEnt = sceneMgr->createEntity("SinbadBody", "Sinbad.mesh");
mBodyNode->attachObject(mBodyEnt);

// create swords and attach to sheath
mSword1 = sceneMgr->createEntity("SinbadSword1", "Sword.mesh");
mSword2 = sceneMgr->createEntity("SinbadSword2", "Sword.mesh");
mBodyEnt->attachObjectToBone("Sheath.L", mSword1);
mBodyEnt->attachObjectToBone("Sheath.R", mSword2);

// create a couple of ribbon trails for the swords, just for fun
NameValuePairList params;
params["numberOfChains"] = "2";
params["maxElements"] = "80";
mSwordTrail = (RibbonTrail*)sceneMgr->createMovableObject("RibbonTrail", &params);
mSwordTrail->setMaterialName("Examples/LightRibbonTrail");
mSwordTrail->setTrailLength(20);
mSwordTrail->setVisible(false);
sceneMgr->getRootSceneNode()->attachObject(mSwordTrail);


for (int i = 0; i < 2; i++)
{
mSwordTrail->setInitialColour(i, 1, 0.8, 0);
mSwordTrail->setColourChange(i, 0.75, 1.25, 1.25, 1.25);
mSwordTrail->setWidthChange(i, 1);
mSwordTrail->setInitialWidth(i, 0.5);
}

mKeyDirection = Vector3::ZERO;
mVerticalVelocity = 0;
}

void setupAnimations()
{
// this is very important due to the nature of the exported animations
mBodyEnt->getSkeleton()->setBlendMode(ANIMBLEND_CUMULATIVE);

String animNames[] =
{"IdleBase", "IdleTop", "RunBase", "RunTop", "HandsClosed", "HandsRelaxed", "DrawSwords",
"SliceVertical", "SliceHorizontal", "Dance", "JumpStart", "JumpLoop", "JumpEnd"};

// populate our animation list
for (int i = 0; i < NUM_ANIMS; i++)
{
mAnims = mBodyEnt->getAnimationState(animNames);
mAnims->setLoop(true);
mFadingIn = false;
mFadingOut = false;
}

// start off in the idle state (top and bottom together)
setBaseAnimation(ANIM_IDLE_BASE);
setTopAnimation(ANIM_IDLE_TOP);

// relax the hands since we're not holding anything
mAnims[ANIM_HANDS_RELAXED]->setEnabled(true);

mSwordsDrawn = false;
}

void setupCamera(Camera* cam)
{
// create a pivot at roughly the character's shoulder
mCameraPivot = cam->getSceneManager()->getRootSceneNode()->createChildSceneNode();
// this is where the camera should be soon, and it spins around the pivot
mCameraGoal = mCameraPivot->createChildSceneNode(Vector3(0, 0, 15));
// this is where the camera actually is
mCameraNode = cam->getSceneManager()->getRootSceneNode()->createChildSceneNode();
mCameraNode->setPosition(mCameraPivot->getPosition() + mCameraGoal->getPosition());

mCameraPivot->setFixedYawAxis(true);
mCameraGoal->setFixedYawAxis(true);
mCameraNode->setFixedYawAxis(true);

// our model is quite small, so reduce the clipping planes
cam->setNearClipDistance(0.1);
cam->setFarClipDistance(100);
mCameraNode->attachObject(cam);

mPivotPitch = 0;
}

void updateBody(Real deltaTime)
{
mGoalDirection = Vector3::ZERO; // we will calculate this

if (mKeyDirection != Vector3::ZERO && mBaseAnimID != ANIM_DANCE)
{
// calculate actually goal direction in world based on player's key directions
mGoalDirection += mKeyDirection.z * mCameraNode->getOrientation().zAxis();
mGoalDirection += mKeyDirection.x * mCameraNode->getOrientation().xAxis();
mGoalDirection.y = 0;
mGoalDirection.normalise();

Quaternion toGoal = mBodyNode->getOrientation().zAxis().getRotationTo(mGoalDirection);

// calculate how much the character has to turn to face goal direction
Real yawToGoal = toGoal.getYaw().valueDegrees();
// this is how much the character CAN turn this frame
Real yawAtSpeed = yawToGoal / Math::Abs(yawToGoal) * deltaTime * TURN_SPEED;
// reduce "turnability" if we're in midair
if (mBaseAnimID == ANIM_JUMP_LOOP) yawAtSpeed *= 0.2f;

// turn as much as we can, but not more than we need to
if (yawToGoal < 0) yawToGoal = std::min<Real>(0, std::max<Real>(yawToGoal, yawAtSpeed)); //yawToGoal = Math::Clamp<Real>(yawToGoal, yawAtSpeed, 0);
else if (yawToGoal > 0) yawToGoal = std::max<Real>(0, std::min<Real>(yawToGoal, yawAtSpeed)); //yawToGoal = Math::Clamp<Real>(yawToGoal, 0, yawAtSpeed);

mBodyNode->yaw(Degree(yawToGoal));

// move in current body direction (not the goal direction)
mBodyNode->translate(0, 0, deltaTime * RUN_SPEED * mAnims[mBaseAnimID]->getWeight(),
Node::TS_LOCAL);
}

if (mBaseAnimID == ANIM_JUMP_LOOP)
{
// if we're jumping, add a vertical offset too, and apply gravity
mBodyNode->translate(0, mVerticalVelocity * deltaTime, 0, Node::TS_LOCAL);
mVerticalVelocity -= GRAVITY * deltaTime;

Vector3 pos = mBodyNode->getPosition();
if (pos.y <= CHAR_HEIGHT)
{
// if we've hit the ground, change to landing state
pos.y = CHAR_HEIGHT;
mBodyNode->setPosition(pos);
setBaseAnimation(ANIM_JUMP_END, true);
mTimer = 0;
}
}
}

void updateAnimations(Real deltaTime)
{
Real baseAnimSpeed = 1;
Real topAnimSpeed = 1;

mTimer += deltaTime;

if (mTopAnimID == ANIM_DRAW_SWORDS)
{
// flip the draw swords animation if we need to put it back
topAnimSpeed = mSwordsDrawn ? -1 : 1;

// half-way through the animation is when the hand grasps the handles...
if (mTimer >= mAnims[mTopAnimID]->getLength() / 2 &&
mTimer - deltaTime < mAnims[mTopAnimID]->getLength() / 2)
{
// so transfer the swords from the sheaths to the hands
mBodyEnt->detachAllObjectsFromBone();
mBodyEnt->attachObjectToBone(mSwordsDrawn ? "Sheath.L" : "Handle.L", mSword1);
mBodyEnt->attachObjectToBone(mSwordsDrawn ? "Sheath.R" : "Handle.R", mSword2);
// change the hand state to grab or let go
mAnims[ANIM_HANDS_CLOSED]->setEnabled(!mSwordsDrawn);
mAnims[ANIM_HANDS_RELAXED]->setEnabled(mSwordsDrawn);

// toggle sword trails
if (mSwordsDrawn)
{
mSwordTrail->setVisible(false);
mSwordTrail->removeNode(mSword1->getParentNode());
mSwordTrail->removeNode(mSword2->getParentNode());
}
else
{
mSwordTrail->setVisible(true);
mSwordTrail->addNode(mSword1->getParentNode());
mSwordTrail->addNode(mSword2->getParentNode());
}
}

if (mTimer >= mAnims[mTopAnimID]->getLength())
{
// animation is finished, so return to what we were doing before
if (mBaseAnimID == ANIM_IDLE_BASE) setTopAnimation(ANIM_IDLE_TOP);
else
{
setTopAnimation(ANIM_RUN_TOP);
mAnims[ANIM_RUN_TOP]->setTimePosition(mAnims[ANIM_RUN_BASE]->getTimePosition());
}
mSwordsDrawn = !mSwordsDrawn;
}
}
else if (mTopAnimID == ANIM_SLICE_VERTICAL || mTopAnimID == ANIM_SLICE_HORIZONTAL)
{
if (mTimer >= mAnims[mTopAnimID]->getLength())
{
// animation is finished, so return to what we were doing before
if (mBaseAnimID == ANIM_IDLE_BASE) setTopAnimation(ANIM_IDLE_TOP);
else
{
setTopAnimation(ANIM_RUN_TOP);
mAnims[ANIM_RUN_TOP]->setTimePosition(mAnims[ANIM_RUN_BASE]->getTimePosition());
}
}

// don't sway hips from side to side when slicing. that's just embarrasing.
if (mBaseAnimID == ANIM_IDLE_BASE) baseAnimSpeed = 0;
}
else if (mBaseAnimID == ANIM_JUMP_START)
{
if (mTimer >= mAnims[mBaseAnimID]->getLength())
{
// takeoff animation finished, so time to leave the ground!
setBaseAnimation(ANIM_JUMP_LOOP, true);
// apply a jump acceleration to the character
mVerticalVelocity = JUMP_ACCEL;
}
}
else if (mBaseAnimID == ANIM_JUMP_END)
{
if (mTimer >= mAnims[mBaseAnimID]->getLength())
{
// safely landed, so go back to running or idling
if (mKeyDirection == Vector3::ZERO)
{
setBaseAnimation(ANIM_IDLE_BASE);
setTopAnimation(ANIM_IDLE_TOP);
}
else
{
setBaseAnimation(ANIM_RUN_BASE, true);
setTopAnimation(ANIM_RUN_TOP, true);
}
}
}

// increment the current base and top animation times
if (mBaseAnimID != ANIM_NONE) mAnims[mBaseAnimID]->addTime(deltaTime * baseAnimSpeed);
if (mTopAnimID != ANIM_NONE) mAnims[mTopAnimID]->addTime(deltaTime * topAnimSpeed);

// apply smooth transitioning between our animations
fadeAnimations(deltaTime);
}

void fadeAnimations(Real deltaTime)
{
for (int i = 0; i < NUM_ANIMS; i++)
{
if (mFadingIn)
{
// slowly fade this animation in until it has full weight
Real newWeight = mAnims->getWeight() + deltaTime * ANIM_FADE_SPEED;
mAnims->setWeight(Math::Clamp<Real>(newWeight, 0, 1));
if (newWeight >= 1) mFadingIn = false;
}
else if (mFadingOut)
{
// slowly fade this animation out until it has no weight, and then disable it
Real newWeight = mAnims->getWeight() - deltaTime * ANIM_FADE_SPEED;
mAnims->setWeight(Math::Clamp<Real>(newWeight, 0, 1));
if (newWeight <= 0)
{
mAnims->setEnabled(false);
mFadingOut = false;
}
}
}
}

void updateCamera(Real deltaTime)
{
// place the camera pivot roughly at the character's shoulder
mCameraPivot->setPosition(mBodyNode->getPosition() + Vector3::UNIT_Y * CAM_HEIGHT);
// move the camera smoothly to the goal
Vector3 goalOffset = mCameraGoal->_getDerivedPosition() - mCameraNode->getPosition();
mCameraNode->translate(goalOffset * deltaTime * 9.0f);
// always look at the pivot
mCameraNode->lookAt(mCameraPivot->_getDerivedPosition(), Node::TS_WORLD);
}

void updateCameraGoal(Real deltaYaw, Real deltaPitch, Real deltaZoom)
{
mCameraPivot->yaw(Degree(deltaYaw), Node::TS_WORLD);

// bound the pitch
if (!(mPivotPitch + deltaPitch > 25 && deltaPitch > 0) &&
!(mPivotPitch + deltaPitch < -60 && deltaPitch < 0))
{
mCameraPivot->pitch(Degree(deltaPitch), Node::TS_LOCAL);
mPivotPitch += deltaPitch;
}

Real dist = mCameraGoal->_getDerivedPosition().distance(mCameraPivot->_getDerivedPosition());
Real distChange = deltaZoom * dist;

// bound the zoom
if (!(dist + distChange < 8 && distChange < 0) &&
!(dist + distChange > 25 && distChange > 0))
{
mCameraGoal->translate(0, 0, distChange, Node::TS_LOCAL);
}
}

void setBaseAnimation(AnimID id, bool reset = false)
{
if (mBaseAnimID >= 0 && mBaseAnimID < NUM_ANIMS)
{
// if we have an old animation, fade it out
mFadingIn[mBaseAnimID] = false;
mFadingOut[mBaseAnimID] = true;
}

mBaseAnimID = id;

if (id != ANIM_NONE)
{
// if we have a new animation, enable it and fade it in
mAnims[id]->setEnabled(true);
mAnims[id]->setWeight(0);
mFadingOut[id] = false;
mFadingIn[id] = true;
if (reset) mAnims[id]->setTimePosition(0);
}
}

void setTopAnimation(AnimID id, bool reset = false)
{
if (mTopAnimID >= 0 && mTopAnimID < NUM_ANIMS)
{
// if we have an old animation, fade it out
mFadingIn[mTopAnimID] = false;
mFadingOut[mTopAnimID] = true;
}

mTopAnimID = id;

if (id != ANIM_NONE)
{
// if we have a new animation, enable it and fade it in
mAnims[id]->setEnabled(true);
mAnims[id]->setWeight(0);
mFadingOut[id] = false;
mFadingIn[id] = true;
if (reset) mAnims[id]->setTimePosition(0);
}
}

Camera* mCamera;
SceneNode* mBodyNode;
SceneNode* mCameraPivot;
SceneNode* mCameraGoal;
SceneNode* mCameraNode;
Real mPivotPitch;
Entity* mBodyEnt;
Entity* mSword1;
Entity* mSword2;
RibbonTrail* mSwordTrail;
AnimationState* mAnims[NUM_ANIMS]; // master animation list
AnimID mBaseAnimID; // current base (full- or lower-body) animation
AnimID mTopAnimID; // current top (upper-body) animation
bool mFadingIn[NUM_ANIMS]; // which animations are fading in
bool mFadingOut[NUM_ANIMS]; // which animations are fading out
bool mSwordsDrawn;
Vector3 mKeyDirection; // player's local intended direction based on WASD keys
Vector3 mGoalDirection; // actual intended direction in world-space
Real mVerticalVelocity; // for jumping
Real mTimer; // general timer to see how long animations have been playing
};

#endif

Tubulii

10-02-2013 17:47:07

Thanks for sharing,

you could setup an wiki page if you want to and add a lovely screenshot :D of "sinbad, powered by mogre"