Character and gravity

Night Elf

06-02-2008 23:17:38

@Betajaen:

Hi, I'm trying to make a character jump and so I began by trying to understand the character/conroller class. I've worked with it in my last project, but there was no vertical movement in that.

As I understand your code, the character will fall down with a constant velocity rather than with an accelerated movement. I think the current velocity should be involved (added?) in the calculation of the new velocity to have acceleration, but the code is just:

void CharacterMovementVectorController::move(NxVec3 &out, NxVec3 &moveVector, NxQuat &direction, NxVec3 &g, float t, Character*) {
out = ((direction.rot(moveVector) * 1.5) + g) * t;
}


Am I right? If not, could you explain how the acceleration happens? Thanks.

betajaen

06-02-2008 23:49:29

But Characters are kinematic so "out" is an acceleration added to the existing position and not a velocity is it not? It doesn't explain why the characters fall slower than the normal rigid bodies though.

Night Elf

07-02-2008 17:25:39

That's what I thought, but I found this in the Ageia docs:

In each frame, characters can be moved using the following function:

NxController::move(constNxVec3& disp, NxU32 activeGroups, NxF32 minDist, NxU32& collisionFlags, NxF32 sharpness=1.0f, const NxGroupsMask* groupsMask=NULL);
disp is the displacement vector for the current frame. It is typically a combination of a vertical motion due to gravity and a lateral motion when your character is moving. Note that this is a displacement vector, i.e., a first order control. This is not an impulse (second order control) or a force (third order control) vector.


I'm no sure if I'm interpreting this correcly... I understand it's saying that the disp vector is the final motion of the object, so any forces (like gravity) shoud be already considered. Is that right?

If so, how would you say I should modify the character controller code so that it properly accelerates when falling?

Arcanor

03-03-2008 17:48:00

Did anyone figure out how to make characters fall properly? My character falls in a "floating" way, never accelerating with gravity. How can I add the gravity acceleration back in?

Arcanor

10-03-2008 02:55:08

So is this just not possible in NxOgre? Does anybody have characters falling property with gravity acceleration?

novaumas

10-03-2008 06:28:39

I've started recently to use NxOgre and PhysX, and today I was trying to get a character to make semi-believable jumps... Not quite there yet, but getting to it.

What I did was override the CharacterMovementVectorController.

As I understand, 'out' should be the desired position increment from the current character position, so you must manually apply velocity and acceleration to it.

So if you just want your character to fall, you could do something like this:

...
out += ( direction.rot( moveVector ) * speed ) * t;
...
out += ( 0.5 * g * m_fallingSeconds * m_fallingSeconds );
...


You just have to keep a record of when the character starts falling then.

It would be nice if we could make a working character controller implementation helping each other :D ( I'm interested in getting it to work with movable platforms... )

betajaen

10-03-2008 09:16:25

If it helps - Not only the source for the NxCharacter controller is now available, that I'm implementing it directly into bleeding (and some more). Perhaps we can look at the source for all the quirks it has.

novaumas

10-03-2008 16:16:15

Hi betajaen, maybe I'm missing something, but I'm checking http://svn.nxogre.org/branches/1.0 and the character code is empty ( rev 19 ).

It's nice that you're focusing on this part of the code though, I'm willing to help with what I can... I'm finding myself having to implement this anyway for my game project, so I might as well contribute.

betajaen

10-03-2008 16:28:40

The PhysX 2.8.0 code I meant, and I'll take you up on your offer. Anything code you can post or say will be greatly appreciated.

novaumas

10-03-2008 16:42:20

Oh... Bummer...

I'm not really interested in reworking the whole character controller, so I'll be working just on what I need, being that character jumping and movable platforms basically ( it's a platform game :D ).

I'm having to implement this anyway, so I'll post progress when I have some...

Night Elf

10-03-2008 17:00:08

I made a quick prototype and managed to implement some jumping code. I didn't get it 100% right, but I can tell you what I did.

Like novaumas, I overrode the CharacterMovementVectorController. What I did is keep track of the previous velocity and add gravity.y * interval to that. The problem I found is that the velocity accumulates even when the player is not jumping and my quick and dirty solutions to that were... well, quick and dirty. I didn't have much time to think about a good solution.

I'll need to implement proper jumping in the not so distant future, so I'll be really interested in what you can come up with. I hope this helps.

betajaen

10-03-2008 17:10:50

There won't be a CharacterMovementVector controller. In fact the current system you are using won't be used.

Roughly, this is what the new CharacterSystem will look like:

class Walk : public CharacterMovementModel {

public:


void simulate(NxReal dTime) {
mMovement.y += -9.677;
}

void simulateAfter(NxReal dTime) {
// Clear existing artifical movement vector.
mMovement.zero();
}

void forward() {
mMovement.x += 1;
}

void backward() {
mMovement.x -= 1;
}

void left() {
mMovement.z -= 1;
}

void right() {
mMovement.z += 1;
}

void up() {
mMovement.y += 15;
}

};


class Human : public CharacterModel {

public:

Walk* mCharacterModel_Walk;

Human() {
mCharacterModel_Walk = new Walk();
mMovementModels.insert("Walk", mCharacterModel_Walk);
mDefaultMovementModel = mCharacterModel_Walk;
}

~Human() {
if (mCharacterModel_Walk)
delete mCharacterModel_Walk;
}

};



mRed = mScene->createCharacter("Red", Vector3(0,4,0), new Human(), "");

You will be able to switch between different CharacterMovementModels such as Walking, Running, Falling, Climbing, Swimming, etc. fairly easily - You define exactly how the character moves. As well as being given roughly what is around the character, such as a wall so your character can animate properly, or not move. Or switch to another CharacterMovementModel such as Falling.

It's still in beta, so the above will probably change. But so far the basic implementation works.

novaumas

10-03-2008 17:50:16

That seems to be roughly what we have now, but using a state pattern.

Don't get me wrong, I think it's the right direction as it gets rid of some condition checking in the movement code that makes it look ugly.

If using a kinematic actor, the problems faced will be roughly the same, as we will still have to use the NxController move interface to move the actor ( this is sort of a question ).

The problem isn't the switching from one movement model to another, but deciding when should we switch from one movement model to another. Hope that makes some sense :? So it really will depend on the events you send to the character or the way it has to know what's going on (eg. looseGround, landOnGround... ). Don't really know what's your approach on that issue, but I'm quite interested.

betajaen

10-03-2008 18:05:42

I'll be giving some utility functions to the CharacterMovementModel such as; Is there something in front of me? Am I on Ground? Is there a ceiling?. Things like that. As well as more simple functions as getting the material of the ground/shape below you.

novaumas

10-03-2008 21:34:43

I know maybe it's asking for too much, but will the utility functions be compatible with the old character controller ( or compatible enough for me to write them for 0.9 ), or would you recommend the switch to bleeding once the system is in place? ( Will it be painfull? :D )

I like the new approach to the character system, it's way cleaner, and the utility functions will make a real difference.

@Night Elf:
This is my jumping code, it more or less does the trick.

out.zero();
...
out += ( direction.rot( moveVector ) * speed ) * t;
...
if ( m_jumping )
{
m_jumpSeconds += t;
out += NxVec3( 0, jumpSpeed, 0 ) * m_jumpSeconds + ( 0.5 * g * m_jumpSeconds * m_jumpSeconds );
}


To make the character jump, set m_jumping to true and m_jumpSeconds to 0.

Once the character hits the ground, change m_jumping to false ( I haven't worked that out yet... have to figure out how to get the hit records with other actors )

betajaen

10-03-2008 21:46:31

I know maybe it's asking for too much, but will the utility functions be compatible with the old character controller ( or compatible enough for me to write them for 0.9 ), or would you recommend the switch to bleeding once the system is in place? ( Will it be painfull? :D )

I like the new approach to the character system, it's way cleaner, and the utility functions will make a real difference.


When the new CharacterSystem is complete it won't even use the NxCharacter.dll anymore, so it won't be compatible - although the code will be roughly the same.

Switching to Bleeding will be a little painful yes; CubeShape is called Cube, SphereShape is called Sphere, and so on. As well as amendments to the Containers using BetajaenCC now. Triggers are SimpleShapes, and the massive RenderingSystem change. But it'll be worth the change.

Night Elf

11-03-2008 17:00:35


@Night Elf:
This is my jumping code, it more or less does the trick.

out.zero();
...
out += ( direction.rot( moveVector ) * speed ) * t;
...
if ( m_jumping )
{
m_jumpSeconds += t;
out += NxVec3( 0, jumpSpeed, 0 ) * m_jumpSeconds + ( 0.5 * g * m_jumpSeconds * m_jumpSeconds );
}



Thanks. But I see a problem with that code: I think it doesn't account for the character falling without jumping, does it? That's why I think just adding g * t to the current velocity works better. You would do that always, unless the character is on the ground.

betajaen

11-03-2008 17:05:44

I think there should be a third state. Not falling and Not jumping only some of the gravitational vector should be applied then. For example if you ever stand on a box, you'll notice the box is quite jumpy and shudders a lot, that is because the Kinematic Actor is clipping inside the box and causing tunneling.

novaumas

13-03-2008 01:16:14

@betajaen: Don't have the issue when standing on boxes, but I do get some weird stuff when pushing dynamic actors with my character, like them being pushed inside other actors.

@Night Elf: Tried your approach, and it does indeed give nicer results. I'm doing something very much like the following now.


out.y = g.y * t + positionIncrement.y;


I'm actually using g * 0.5 instead of g, as characters look better when falling somewhat slow.

I'm retrieving the positionIncrement like so ( I'm looking for better approaches, this sort of feels a little bit like a hack )

// be sure to init posNew to the actor's position.
m_posOld = m_posNew;
m_posNew = character->getNxController()->getActor()->getGlobalPosition();
NxVec3 positionIncrement = ( m_posNew - m_posOld );


For jumping, beware of the following prototyping code :D

m_onGround = ( ! m_jumpRequested && -0.0001 < velocity.y && velocity.y < 0.0001 );
if ( m_jumpRequested )
{
out.y += 0.8;
m_jumpRequested = false;
}


I'm sure there are lots of issues in the code above... still trying to get everything right. I'm interested in hearing alternatives.

It's visually nice though, so if I have to choose between a physically accurate solution, and something that feels right, I'll probably choose something that feels right ( for character control at least ).

almondega

31-08-2008 02:55:19

sry for bump the post
but here is what i did to fall down correctly (almost)

in the frameStarted method:
isOnAir is a bool var, seted to true when the body ends the touch with the ground (nxOgre) and seted to false when touch the ground again
MassFactor = 10 (mActor mass is 8)
mAirTime is a Ogre::Real var that starts in 0.8 and get incremented every frame time when the body is on the air. Return to 0.8 when touch the ground
if (isOnAir)
{
mAirTime += evt.timeSinceLastFrame;
mActor->addForce(NxVec3(0,-mActor->getMass() * MassFactor * mAirTime * mAirTime * 10 * evt.timeSinceLastFrame,0),NX_IMPULSE);
}


Preview: http://www.youtube.com/watch?v=1QRhAtqNEp0

mcaden

31-08-2008 05:34:00

I have callbacks that set whether he can jump or not to true/false when he touches/leaves the ground. problem is that if I land on a box, I can't jump, because it's not triggering that I landed...

About the only options I can think of would be to test the bottom of my collision mesh for collisions with objects (otherwise I could jump when my head or side hit an object), or to test my downward velocity each frame. Testing downward velocity has a problem of making it so that when I'm at the very peak of my jump, I could jump again, basically holding down spacebar would rocket me into the sky. I'm thinking that I'm going to have to implement a velocity test + timer to see if I'm falling/jumping rather than callbacks to different objects I could possibly jump from. I think this is probably the best solution as it also allows for measuring air time for things like falling damage.

My main issue at the moment is that currently my 'gravity' is corrupting my forward/backward motion, making my character veer to one side, then another. I think I'm going to have to find a different gravity fix rather than just pushing straight down on my actor.

almondega

31-08-2008 08:41:19

look what i did:

i create a Ground group, that detect the touch with the caracter

and then, i create a box, and put a small plane box over, with the Ground group

so, when i touch the top of the box, its like i touched the ground
and i can jump again

=]

mcaden

31-08-2008 10:51:32

Not possible for me. I have rocks than can be rolled around, so I'm going to need a more complicated solution.

I was thinking about having collision callbacks, but checking the general direction of the collision from the center of one object to the center of another. It'd be pretty complicated I think to find the point of collision, but if I simply compare the centers of the 2 objects, I can tell if the box, rock, bridge, etc...is below the model. I guess compare the 2 vectors to get the direction from the character to the rock, then make sure it falls within a certain tolerance, but I foresee other problems with that, like getting that "tolerance" just right, and having it be rectangle shaped. Maybe raycasting?

Or I suppose the simplest solution would be to attach 2 different actors to my character, one of them being simply the feet, and the other actor encasing the actual body. I'm not sure if that's possible. I'm still learning a lot of this stuff.

novaumas

31-08-2008 11:33:13

if you're using 0.9 and the character controller, you can add the following function to the Character class:


bool getCollisionDown() const
{
return ( ( mCollisionFlags & NXCC_COLLISION_DOWN ) != 0 );
}


At least that is what I ended up doing to check if the character is touching the ground and see if it can jump.

Also, on the collision normal checking in the hit report: I do that to check if the actor under the character is a moving platform ( it's a platform game :D ), and works just fine.

mcaden

31-08-2008 11:52:34

That's sounds PERFECT...problem is I'm using 1.0'21 and it doesn't seem to have that functionality.