PhysX - Stick controllers to kinematic actors

Started by
8 comments, last by NumberXaero 9 years, 9 months ago

By default kinematic actors in PhysX will simply push controllers out of the way or ignore them:

[media]http:

[/media]

This is obviously not the desired behavior for things like elevators or escalators.

I'm unsure on how to actually 'stick' the controller to the platform to make sure the player doesn't fall off.

I tried adding the kinematic target offset of the platform to the displacement vector when moving the controller every simulation step, however that doesn't prevent the 'pushing' from the kinematic actor and wasn't very accurate either.

How is this usually accomplished? The documentation mentions using obstacles for moving platforms, but I don't see how that would help in this case.

I'm using PhysX 3.3.0.

Advertisement

The bridges demo shows how it works.

This is explained in the sample code, SampleBridgesCCT.cpp, by implementing the PxControllerBehaviorCallback, and allowing riding between the controller and a touched shape (which you would have to id as being able to ride).

The bridges demo shows how it works.

This is explained in the sample code, SampleBridgesCCT.cpp, by implementing the PxControllerBehaviorCallback, and allowing riding between the controller and a touched shape (which you would have to id as being able to ride).

Thanks, but I still haven't been able to get it working.

I can't actually run/try any of the PhysX examples since I don't have a nvidia card, so I kind of have to fumble around in the dark.

This is my callback class:


class WVPxControllerBehaviorCallback
	: public physx::PxControllerBehaviorCallback
{
	physx::PxControllerBehaviorFlags getBehaviorFlags(const physx::PxShape &shape,const physx::PxActor &actor)
	{
		return physx::PxControllerBehaviorFlag::eCCT_CAN_RIDE_ON_OBJECT;
	}
	physx::PxControllerBehaviorFlags getBehaviorFlags(const physx::PxController &controller)
	{
		return physx::PxControllerBehaviorFlags(0);
	}
	physx::PxControllerBehaviorFlags getBehaviorFlags(const physx::PxObstacle &obstacle)
	{
		return physx::PxControllerBehaviorFlag::eCCT_CAN_RIDE_ON_OBJECT;
	}
};

Which I use for the 'behaviorCallback' in the PxCapsuleControllerDesc for my controller.

The callback is definitely called properly when my controller and the kinematic actor collide, however it doesn't seem to matter at all what I return here - The controller is always sliding off of it:

[media]http:

[/media]

I can't see anything else special in the bridges samples, so I'm not sure what's going on.

I also dont have nvidia in this comp, but I just did a test with some kinematic actors (platforms much like yours) and a controller jumping onto it, you need the ride and slide flags.

physx::PxControllerBehaviorFlags getBehaviorFlags(const physx::PxShape &shape,const physx::PxActor &actor)
{
return physx::PxControllerBehaviorFlag::eCCT_CAN_RIDE_ON_OBJECT | physx::PxControllerBehaviorFlag::eCCT_SLIDE;
}

without it, my platforms slide away from under the controller.

Edit: you probably want to check that the actor is kinematic first before returning those flags, riding dynamics might not work well.

Thank you. It turned out the sliding was actually caused by another part of my code (Without it I stay on the platform properly), which I'm also unsure on how to fix:

The problem is this:

I'm applying my own gravity to the player. The gravity-force (As well as player movement) is applied to a velocity vector stored on the player object (Let's call it m_internalVelocity), which is used for the displacement in the move-calls. However, this results in the following issue:

1) Player is on ground

2) Gravity force(0,-50,0) is added to m_internalVelocity

3) m_internalVelocity is applied through move-call

4) Physics are simulated

5) m_internalVelocity is obviously still (0,-50,0), even though the actual velocity is (0,0,0), because the player is on the ground and cannot move downwards

6) Gravity force is added again, new m_internalVelocity is (0,-100,0)

7) etc

So basically the velocity would just increase infinitely. For this reason I'm grabbing the actual velocity after the simulation by comparing it with the previous velocity before the simulation:

1) Player is on ground

2) Player actor's current velocity is stored in a variable -> oldVelocity(0,0,0)

3) Gravity force(0,-50,0) is added to m_internalVelocity

4) m_internalVelocity is applied through move-call

5) Physics are simulated

6) I compare the new velocity of the actor with oldVelocity -> Since the player can't move downwards, the velocity I get here is (0,0,0), which is correct

7) m_internalVelocity is set to the delta velocity (0,0,0)

8) etc

This worked so far, but doesn't work if there's outside forces like a moving platform in this case:

1) Player is on moving platform

2) Player actor's current velocity is stored in a variable -> oldVelocity(0,0,0)

3) Gravity force(0,-50,0) is added to m_internalVelocity

4) m_internalVelocity is applied through move-call

5) Physics are simulated

6) I compare the new velocity of the actor with oldVelocity -> The velocity I get here is the movement that was caused by the platform

7) m_internalVelocity is set to the delta velocity (Platform movement)

8) In the next move-call the m_internalVelocity will be applied again which means the platform-movement is essentially added a second time

9) With every simulation the platform-movement is added to the previous m_internalVelocity, causing the player to keep increasing velocity until he falls off the platform

How can I keep track of the player's internal velocity (Character movement +gravity) without affecting outside forces(platform), but still retrieving changes caused by physical contacts between the controller and other actors?

The px controller only uses a move interface, the way I did it is each frame calculate a move offset (displacement), pass it to the controller, and get the corrected position back after the update.

I dont pull velocity out of the physx controller, but I do use velocity in a way similar to how your doing it. I use my own force/acceleration/velocity/position to create a displacement amount, I pass my position change to the controller, and the controller move call returns collision flags. If the flags indicate a collision below, you simply zero out the y velocity, and similar for collisions on the side.

Hm... I can see how that would work for collisions below / above the player, but what about the sides? The only information I can retrieve about that is physx::PxControllerFlag::eCOLLISION_SIDES, which doesn't tell me which side(s) there was a collision on.

Besides, this also wouldn't work properly on slopes. It would report a downwards collision which would invalidate the gravity, even though the player should still be pushed down.

Did you find a way to avoid that?

Well keep in mind these are move flags, returned as the result of a move call, I believe the sides flag is means that you hit something in the direction of the move around the middle part of the controller shape, meaning you are blocked from moving sideways by something which Im guessing cant be auto stepped, PxControllerDesc::stepOffset.

The controllers really just to keep you from go through the world (although you still do sometimes :-( ).

If your talking about getting side hit by projectiles in a gameplay fashion, you might want to catch hits in the callbacks and check the direction between the controller and the actor/shape hitting you to know exactly which side you were hit from.

For slopes, grab the world info in a PxUserControllerHitReport callback, check the normal of the object youre standing on and factor the slope angle and gravity force into your next move adjustment to slide move down the slope if the angles suitable.

They have changed things in PhysX so often I cant remember if there was an auto slide at one time or not depending on the slope angle, might still be Im not sure.

Well keep in mind these are move flags, returned as the result of a move call, I believe the sides flag is means that you hit something in the direction of the move around the middle part of the controller shape, meaning you are blocked from moving sideways by something which Im guessing cant be auto stepped, PxControllerDesc::stepOffset.

I mean that if you move with an angle against an axis-aligned wall, your movement would stop in one axis (The one perpendicular to the wall), but the other axis wouldn't be affected. (You 'slide' against the wall)

However, since I only know if there HAS been a collision at any side, but not which one, all I can do is reset both axes to 0, which would mean the movement would come to a complete halt.

In essense it's the same problem as with slopes.

Yeah the move flags wont be able to solve everything, the horizontal stuff is trickier. The horizontal movement behavior would probably be best handled in the PxUserControllerHitReport which would return the world normal of the wall you hit and at what point, then you could handle custom sliding by reducing the xz velocity based on the angle you hit at, where the move flags would be used to kill vertical movement from hitting above or below and provide a hint about side hits.

My velocity use was more limited to vertical movement, for falling and jumping. When it came to horizontal movement I simply passed a move amount because the horizontal movement was tied to the motion extracted from the walk/run animations, so I knew how much to move based on the animation otherwise he would look like he was sliding if I moved more (or less) then what looked right according to the animation.

This topic is closed to new replies.

Advertisement