Clean 1st Person movement [solution here!]

shoki

22-05-2006 15:29:26

Hi there!

So i've looked on the different posts to find a solution about character movement but I've only found a post where they give a bit of code. Sorry but that's not enough for me, I begin with ogreNewt...

I've looked on the demo too but the problem is that the only one which use "movement" is the vehicle demonstration, so it's using the vehicle class of ogreNewt.
I don't understand what the addForce and the setTorque are doing, why I need both.

What I'm looking for is a clean movement of my character. My movement must depends of where my camera is looking.
The problem is that I don't manage to rotate my cylinder body when I yaw my camera, that's doing wired things... :?
I used opCode before, so it was easy to have a clean movement, but now with forces that's more difficult.
I've choosen ogreNewton because I need to climb a staircase, else I want the same movement, without inertia if that's possible...

I hope you'll help me!

Thx a lot,
shoki

shoki

23-05-2006 13:59:01

Hi again!

So I've tried to found solutions. My movement works but not perfectly.
I have to release a key to change the direction of my character. I don't know besause I recalculate the vector on each time.
One other thing is when I collide a wall, I'd like that my body slide along it. For yet that's not "smooth" at all.

I've implemented it with velocity vectors not strengh, because I don't want my body slows when I hit a key. Maybe that's better with strengh but I don't how tu use it.
So have a look on my code, maybe you'll find my mistakes!

material initilisation :

void MainApplication::initOgreNewt(){

OgreNewt::MaterialID* mat1 = player->getMaterialID();
OgreNewt::MaterialID* mat3 = oScene.getMaterialID();

OgreNewt::MaterialPair* fPlayerFloor = new OgreNewt::MaterialPair(mWorld,mat1,mat3);


fPlayerFloor->setDefaultElasticity(0);

}


player constructor :


Player::Player(SceneManager* _mSceneMgr,OgreNewt::World* _mWorld){

initialPosition = Vector3(400,200,400); //y>10
lastPosition = initialPosition;

mSceneMgr = _mSceneMgr;
mWorld = _mWorld;
mDirection = Vector3::ZERO;
pitchAngle = 0;

body = mSceneMgr->createEntity("player","cylinder.mesh");
body->setNormaliseNormals(true);

mainNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("playerMainNode");
cameraNode = mainNode->createChildSceneNode("playerCameraNode");
bodyNode = mainNode->createChildSceneNode("playerBodyNode");

mainNode->setPosition(initialPosition);


bodyNode->attachObject(body);
bodyNode->rotate(Vector3::UNIT_Z,Radian(Degree(90)));
//bodyNode->roll(Radian(Degree(90)),Node::TS_WORLD);

Vector3 siz(170,20,20);
bodyNode->setScale( siz );

//collision settings
col = new OgreNewt::CollisionPrimitives::Cylinder(mWorld,20,170,Quaternion(Radian(Degree(90)),Vector3::UNIT_Z));
bod = new OgreNewt::Body( mWorld, col );
delete col;

bod->attachToNode( mainNode );
bod->setPositionOrientation(bodyNode->getWorldPosition(),Quaternion::IDENTITY);


//gravity settings
Real mass = 75.0;
Vector3 inertia = OgreNewt::MomentOfInertia::CalcCylinderSolid(mass,20,170);
bod->setMassMatrix(mass, inertia);
bod->attachToNode(mainNode);

//bod->setStandardForceCallback();
bod->setAutoFreeze(0);

bod->setCustomForceAndTorqueCallback<Player>(&Player::forceCallback,this);
OgreNewt::Joint* joint;
joint = new OgreNewt::BasicJoints::UpVector( mWorld, bod, Vector3(Vector3::UNIT_Y));

bodyMat = new OgreNewt::MaterialID(mWorld);
bod->setMaterialGroupID(bodyMat);

cameraNode->setPosition(0,80,-21);


}//constructor



Orientation :

void Player::moveOrientation(){

//camera orientation
cameraNode->setOrientation(Quaternion::IDENTITY);
bod->setPositionOrientation(lastPosition,Quaternion::IDENTITY);

cameraNode->pitch( Degree(pitchAngle),Node::TS_WORLD);
bod->setPositionOrientation(lastPosition,Quaternion(Radian(Degree(yawAngle)),Vector3::UNIT_Y));

}


KeyPressed :


void Player::playerKeyListener(KeyEvent* e){

Vector3 curVel = bod->getVelocity();
Vector3 vel;

switch (e->getKey()){
case KC_UP:
case KC_W:
mDirection.z -= W_MOVESPEED;
break;

case KC_DOWN:
case KC_S:
mDirection.z += W_MOVESPEED;
break;

case KC_LEFT:
case KC_A:
mDirection.x -= W_MOVESPEED;
break;

case KC_RIGHT:
case KC_D:
mDirection.x += W_MOVESPEED;
break;
}

vel = mainNode->getOrientation() * mDirection;
bod->setVelocity(vel);
}


key released :


void Player::playerKeyReleased(KeyEvent* e){

torque = 0.0f;
bod->setVelocity(Vector3(0,0,0));

switch(e->getKey()){

case KC_UP:
case KC_W:
mDirection.z += W_MOVESPEED;

break;

case KC_DOWN:
case KC_S:
mDirection.z -= W_MOVESPEED;
break;

case KC_LEFT:
case KC_A:
mDirection.x += W_MOVESPEED;
break;

case KC_RIGHT:
case KC_D:
mDirection.x -= W_MOVESPEED;
break;
}

}


Anf finally my custom callback :

void Player::forceCallback(OgreNewt::Body* bod){

bod->addLocalForce(Vector3(0,-30,0)*mass,Vector3::ZERO);
}


My callback is used because when my body fall that's too slow.. So with the gravity of Mars that's quicker :lol:

All the other objects are statics (treecollision).

Thanks to look at my code,
See U
Shoki

GiDEoN

23-05-2006 14:28:32

i'm new at both c++ / Ogre. Please don't use as excuse you can't solve things just cause you're new to it. Maybe you just need to get a c++ book, or redo all tutorials. If you want to understand how this works, invest loads of time and keep repeating.

Also take a look at the Tutorials. (i've worked through all basic ones 10 times or more). Probably you didn't.

Tip: Look at buffered / unbuffered input. As it looks like you're using unbuffered input. While you want buffered input.

shoki

23-05-2006 15:07:43

Ok ok, I accept your remark.
But I don't have time... I'm doing a placement in a infographism company which ask me to do a visualiser with ogre. So I've 3 weeks left to understand how it works and finih my visualiser. (1 week left to finish physic integration). If people doesn't want to help me, then no problem I accept it.
If I want to understand faster or gain time I have to ask, and a forum is used for that! I only ask advices, not solutions.
Maybe some guys have more experience than me and could easily solve my problem...

When I know how to help someone I help him, I don't say him that he has to search and redo 10 times the same tutorials. What a world : tutorial! How many tutorials about OgreNewt have you seen? I've only seen one on the wiki,So thanks for your answer!

Shoki

PS : I know how to use unbuffered/buffered input... but thx!

shoki

23-05-2006 16:05:27

I've got a rotation of my body when it collides with a wall, I think the collison apply a rotation force but I don't know where it comes from. Is it a function who block the rotation, or is it possible to block it with a join?

I've looked on the ContactCallback, is it there that the movement is recalculate when there's a collision?
Because i'll need to modify it if I want staircase management.

thx, shoki

shoki

23-05-2006 17:58:17

Thx to this topic :

http://www.ogre3d.org/phpBB2addons/viewtopic.php?t=914&highlight=joint+rotation

I've found a solution. It was because of the friction. You've to set it to 0.

For the gravity problem it came from the unit. I have a Gravity of 9.81 (m/s) and when I defined my cylinder it was in centimeters :? yeah i know, noob mistake lol.

So I set my gravity to 980 cm/s.
For the direction which did'nt depends of my camera it was because i set my velocity in the keyreleased function.

I'll post the solution when it will be finished.
See U soon,
shoki

fizzle

24-05-2006 00:32:50

Your progress is good, keep it up, I would love to see the finished project when you are done .. as i have had many problems with ogreNewt and managed to fix a lot on my own.

shoki

25-05-2006 02:30:24

So, for guys who don't want to waste time : here is a solution to control a character via the velocity. The character has a gravity and manage staircases.

Sorry if my code is not perfect, i had to hurry up, maybe some things have to be recode.

player.h

#ifndef __PLAYER_H__
#define __PLAYER_H__

#include "Ogre.h"
#include "OgreConfigFile.h"
#include "OgreOSMScene.h"
#include "OgreEventListeners.h"
#include "OgreKeyEvent.h"
#include "OSMListener.h"
#include "DynamicLines.h"
#include "OgreNewt.h"

using namespace Ogre;
using namespace std;

const int W_MOVESPEED = 500;
const int W_ROTATESPEED = 70;

const Real mass = 7500.0f;

const int TLINES_SIZE = 31;

class Player{

private:

//mainNode positions
Vector3 initialPosition;
Vector3 lastPosition;

SceneManager* mSceneMgr;
Camera* mCamera;

//nodes
SceneNode* mainNode;
SceneNode* cameraNode;
SceneNode* bodyNode;

Entity* body;

Vector3 mDirection;
float pitchAngle;
float yawAngle;

DynamicLines** lines;
SceneNode* rayNode;

//OgreNewton objects
OgreNewt::World* mWorld;
OgreNewt::Collision* col;
OgreNewt::Body* bod;
OgreNewt::MaterialID* bodyMat;
Vector3 currentVelocity;

public:

Player(SceneManager*,OgreNewt::World*);
~Player();

//main function to move the player
void movePlayer(Real _time);

//key pressed, called by the frame listener
void playerKeyListener(KeyEvent* e);

//key released, called by the frame listener
void playerKeyReleased(KeyEvent* e);

//mouse moved
void playerMouseListener(MouseEvent* e);

//to specify the camera to attach
void attachCamera(Camera* mCamera);

//to reinit the position of my player
void reinitPosition();

//orientation changement
void moveOrientation();

//to see the rayscasts
void showRay(Vector3 point1,Vector3 point2,int nb);
void createLines();

//ogre newton functions
//return the material id of the player
OgreNewt::MaterialID* getMaterialID(void);

//custom callback used for gravity
void forceCallback(OgreNewt::Body* bod);

//staircase management
void climbStairs();
};

#endif


player.cpp

#include "Player.h"

using namespace Ogre;
using namespace std;


Player::Player(SceneManager* _mSceneMgr,OgreNewt::World* _mWorld){

initialPosition = Vector3(0,300,0); //y>10
lastPosition = initialPosition;

mSceneMgr = _mSceneMgr;
mWorld = _mWorld;
mDirection = Vector3::ZERO;
pitchAngle = 0;

body = mSceneMgr->createEntity("player","cylinder.mesh");
body->setNormaliseNormals(true);

mainNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("playerMainNode");
cameraNode = mainNode->createChildSceneNode("playerCameraNode");
bodyNode = mainNode->createChildSceneNode("playerBodyNode");

mainNode->setPosition(initialPosition);


bodyNode->attachObject(body);
bodyNode->rotate(Vector3::UNIT_Z,Radian(Degree(90)));

Vector3 siz(170,20,20);
bodyNode->setScale( siz );

//collision settings
col = new OgreNewt::CollisionPrimitives::Cylinder(mWorld,20,170,Quaternion(Radian(Degree(90)),Vector3::UNIT_Z));
bod = new OgreNewt::Body( mWorld, col );
delete col;

bod->attachToNode( mainNode );
bod->setPositionOrientation(bodyNode->getWorldPosition(),Quaternion::IDENTITY);


//gravity settings
Vector3 inertia = OgreNewt::MomentOfInertia::CalcCylinderSolid(mass,20,170);
bod->setMassMatrix(mass, inertia);
bod->attachToNode(mainNode);

//bod->setStandardForceCallback();
bod->setAutoFreeze(0);

bod->setCustomForceAndTorqueCallback<Player>(&Player::forceCallback,this);
PlayerJoint* joint = new PlayerJoint( mWorld, bod);

bodyMat = new OgreNewt::MaterialID(mWorld);

bod->setMaterialGroupID(bodyMat);

cameraNode->setPosition(0,80,0);
//cameraNode->roll(Radian(Degree(90.0f)));
createLines();

currentVelocity = Vector3::ZERO;
stepForce = Vector3::ZERO;
gravity = true;
}//constructor





Player::~Player(){
}//destructor




bool Player::canMove(){

return true;
}//canMove




void Player::playerKeyListener(KeyEvent* e){

switch (e->getKey()){
case KC_UP:
case KC_W:
mDirection.z -= W_MOVESPEED;
break;

case KC_DOWN:
case KC_S:
mDirection.z += W_MOVESPEED;
break;

case KC_LEFT:
case KC_A:
mDirection.x -= W_MOVESPEED;
break;

case KC_RIGHT:
case KC_D:
mDirection.x += W_MOVESPEED;
// bod->setPositionOrientation(Vector3(0,800,0),Quaternion::IDENTITY);
break;
}
}




void Player::playerMouseListener(MouseEvent* e){

pitchAngle += -e->getRelY () * W_ROTATESPEED;
yawAngle += -e->getRelX () * W_ROTATESPEED;

}

void Player::attachCamera(Camera* _mCamera){

mCamera = _mCamera;
cameraNode->attachObject(mCamera);

}


//function called by frameStarted
void Player::movePlayer(Real time){


lastPosition = mainNode->getPosition();

climbStairs();

lastPosition = mainNode->getPosition();

moveOrientation();

currentVelocity = mainNode->getOrientation() * mDirection;
bod->setVelocity(currentVelocity);

}


void Player::moveOrientation(){

//camera orientation
cameraNode->setOrientation(Quaternion::IDENTITY);
bod->setPositionOrientation(lastPosition,Quaternion::IDENTITY);

cameraNode->pitch( Degree(pitchAngle),Node::TS_WORLD);
bod->setPositionOrientation(lastPosition,Quaternion(Radian(Degree(yawAngle)),Vector3::UNIT_Y));
}


void Player::playerKeyReleased(KeyEvent* e){

bod->setVelocity(Vector3(0,currentVelocity.y,0));

switch(e->getKey()){

case KC_UP:
case KC_W:
mDirection.z += W_MOVESPEED;
break;

case KC_DOWN:
case KC_S:
mDirection.z -= W_MOVESPEED;
break;

case KC_LEFT:
case KC_A:
mDirection.x += W_MOVESPEED;
break;

case KC_RIGHT:
case KC_D:
mDirection.x -= W_MOVESPEED;
break;
}

}

void Player::reinitPosition(){

mainNode->setPosition(initialPosition);
mDirection = Vector3::ZERO;
yawAngle = 0;
pitchAngle = 0;
}

void Player::showRay(Vector3 point1, Vector3 point2,int nb){
//show a ray and put it in the table[nb]


lines[nb]->clear();

lines[nb]->addPoint(point1);
lines[nb]->addPoint(point2);

lines[nb]->update();

}
void Player::createLines(){

lines = new DynamicLines* [TLINES_SIZE];
int i = 0;

for(i=0;i<TLINES_SIZE;i++){
lines[i] = new DynamicLines(RenderOperation::OT_LINE_LIST);
lines[i]->update();
mSceneMgr->getRootSceneNode()->attachObject(lines[i]);
}
}

void Player::forceCallback(OgreNewt::Body* bod){

bod->addLocalForce(Vector3(0,-9810 * mass,0),Vector3::ZERO);

}

OgreNewt::MaterialID* Player::getMaterialID(){

return bodyMat;
}

//staircase management
void Player::climbStairs(){

// get the y position of the floor

//Ogra ray to get the collision point
Ogre::Ray camRay(bodyNode->getWorldPosition(),Vector3(0,-1,0));

//normal of the contact
Vector3 normal;

//y position of the floor
Vector3 floor;

//begin and end of the ray
Vector3 start = camRay.getOrigin();
Vector3 end = camRay.getPoint(300.0f);


OgreNewt::BasicRaycast* verticalRay = new OgreNewt::BasicRaycast(mWorld,start,end);

OgreNewt::BasicRaycast::BasicRaycastInfo info;
info = verticalRay->getFirstHit();

normal = info.mNormal;
floor = camRay.getPoint(info.mDistance * 300.0f);

//____________________________________________


OgreNewt::BasicRaycast** rayTable = new OgreNewt::BasicRaycast* [31];
int i,closest,farest;

int highest = 0;

bool collided = true;
bool finished = false;
bool tooHigh = false;
//height of the stair


float high= 31.2;
//Real low = 0.0f;

//for the degree of the stair if it's not vertical
float close = 0.0;
//float far = 5.1;

Vector3 rayBegin;
Vector3 rayDirection;

Vector3 upVector;

OgreNewt::BasicRaycast::BasicRaycastInfo infos[31];
Ogre::Ray* ray[31];

//check collision for 31 cm, 5cm in front
for (i=0;i<31;i++){

//send a ray each 1 cm high
rayBegin = floor;
rayBegin.y += i + 0.1;
rayDirection = mainNode->getOrientation()*mDirection;
rayDirection.y = rayBegin.y;
rayDirection = rayDirection.normalisedCopy();

//cout<<rayDirection.y<<endl;

ray[i] = new Ogre::Ray(rayBegin,rayDirection);

start = ray[i]->getOrigin();
end = ray[i]->getPoint(25.0f);

//cout<<end<<endl;

rayTable[i] = new OgreNewt::BasicRaycast(mWorld,start,end);


showRay(start,end,i);
}

//begin at 0.1 to the highest point
i=0;
do{

infos[i] = rayTable[i]->getFirstHit();
if (infos[i].mBody){
highest = i;
}
i++;
}while(i<31);
//cout<<i<<endl;

//don't climb if it's higher than 31
if (highest==0) collided = false;
if (highest==30) tooHigh=true;


//moving of the body
if (!tooHigh && collided){

cout<<"la"<<endl;

Vector3 highestPt = ray[highest]->getPoint(infos[highest].mDistance * 25.0f);
Vector3 lowestPt = ray[0]->getPoint(infos[0].mDistance * 25.0f);

Vector3 upVector = highestPt - lowestPt;
upVector.y += 2;

Vector3 transVector = highestPt - ray[highest]->getOrigin();
transVector.y = 0;

Vector3 pos;
Quaternion orient;

//stepForce = Vector3(0,1000 * mass,0);
bod->getPositionOrientation(pos,orient);
bod->setPositionOrientation(pos + upVector + transVector ,Quaternion::IDENTITY);

}
}


The gravity management has to be perfecionned because I'm using wired values...

Voila! If you've any question, just ask me...
See U,
shoki

lance

25-05-2006 05:21:13

I heard set position or velocity directly is not the right way.
I think we definitely need a demo in OgreNewt which demonstrate FPS camera.

shoki

25-05-2006 09:59:52

Yes, I know that, the thing is I have problems with forces. You can see that the gravity value is not real (in centimeters).
And with forces my body deccelerates and has an inertia, i don't want that...
That's an alternative solution, but thx for the remark,

++
Shoki

GiDEoN

25-05-2006 21:12:06

I heard set position or velocity directly is not the right way.
I think we definitely need a demo in OgreNewt which demonstrate FPS camera.
Yes, Í could use one.

Thanks for this code, gonna have a closer look and experiment with this :D

e04margu

27-05-2006 13:41:42

I heard set position or velocity directly is not the right way.
I think we definitely need a demo in OgreNewt which demonstrate FPS camera.


I think you are going to have a very hard time stopping the character in a good way if you do it properly.
The problem becomes that if you want to stop him fast you have to either set the velocity, apply an opposing force or set the friction to something astronomical and all three have problems.
The first is "not right". The third is also "not right". And the second is very complicated.

Creating an opposing force large enough to stop the player is no big deal but you have to take a few things into consideration which may pose problems.
Do you want outside forces to still apply? That is probably the case if you are using a physics engine in the first place so that means you shouldnt simply apply a large enough force to stop him or you will be canceling out any velocity he had gotten from outside forces as well as his own movement.
You can also not just use the same force all the time to prevent the above because if you want it realistic the character should be able to scale the amount of force he uses to stop just like a real person would. I mean you use a lot more force when trying to stop quickly from running than from walking.
The best solution might be to use threshold values. Just set a maximum opposing force value that the player is allowed to use when stopping.
But then we run into another problem. Do we want to stop over the course of more than one frame?
If so, how do we know that this is the players wish? If he releases the movement button, what does that mean? Does he want to stop completely or just stop adding to his movement and keep whatever extra velocity he has from outside forces? This is maybe more of a HCI question but it's still important.

I could probably go on like this for a while but I run a risk of going in circles here so it's time to cut this off.
I did my controls using a mix of setvelocity, addforce and a bucketload of flags and checks. When the player is standing on the ground (simple raycast check) I use setvelocity, unless he is above a speed threshold in which case he starts to slide cause he has lost his footing, then I switch over to the slide controls that use forces, the same controls used while in the air or jumping.
I also set the friction to 0 when jumping/flying/sliding so that the player can't press against walls to stop falling and use a rather high friction otherwise so he can stand in slopes.
I also added a little setposition "cheat" like the one in the newton character control tutorial that sucks the player down to the ground if he is close enough. This is useful in slopes to prevent him from loosing footing fast when going downhill or doing those annoying little jumps when stopping while going uphill. That has to be carefully balanced not to upset the jump functionality though. I did it by only doing the raycasting check every three physics updates. This has to be tuned according to your jump force/velocity and the rate of the physics update of course.

I hope this helps someone.

My main point is that there are perhaps more variables to making this kind of control work than you have taken into account here. I don't think there is a single good control that will work for every fps game but this is a good initiative anyway ;)

shoki

27-05-2006 18:28:14

Thx a lot e04margu, I think it'll be usefull. Sorry but I don't have time to implement it.
I've tried to do it with simple forces one time but it results in a unrealistic movement.
So thx again for your explanation.
see U,
shoki

PC-maniak

28-03-2007 13:09:12

So, for guys who don't want to waste time : here is a solution to control a character via the velocity. The character has a gravity and manage staircases.

Sorry if my code is not perfect, i had to hurry up, maybe some things have to be recode.

player.h

#ifndef __PLAYER_H__
#define __PLAYER_H__

#include "Ogre.h"
#include "OgreConfigFile.h"
#include "OgreOSMScene.h"
#include "OgreEventListeners.h"
#include "OgreKeyEvent.h"
#include "OSMListener.h"
#include "DynamicLines.h"
#include "OgreNewt.h"

using namespace Ogre;



helo pls help mi i test camera solution i no found OSMListener.h pls link to download pls

sorry mi english is very bad :oops:

tlc

22-01-2008 16:38:08

So, for guys who don't want to waste time : here is a solution to control a character via the velocity. The character has a gravity and manage staircases.

Sorry if my code is not perfect, i had to hurry up, maybe some things have to be recode.

shoki


Thanks shoki!!

Does anyone know which SceneNode should the FPS Camera attached to? mainNode, cameraNode or bodyNode?

Also, when FPS moves (via keyboard), which SceneNode should I update?

Hansel

23-01-2008 14:42:15

There is a function to attach the camera:

//to specify the camera to attach
void attachCamera(Camera* mCamera);


Read it ;)