Havoc PhysicsCharacterController supportedState - supported for next frame after jump?

PG based on doc example:

In the console I am outputting frame count number and if controller is considered as supported.
So what is happening on the playground:

  1. Controller attempts to jump on frame 1 - controller is considered ‘SUPPORTED’- that makes perfect sense.
  2. Frame 2 - In my mind controller position should change at that point and considered ‘UNSUPPORTED’.

So…what is happening, why are we working around that?

Hi @Eshku Welcome to the forum!

First frame is on_ground, second is start_jump. Next state of on_ground is start_jump and next state of start_jump is in_air.
console.log in getNextState will log the state transition.

You are describing what getNextState returns, not controller as part of a library.
in_air \ wants to jump - is just workaround the fact that player is “Supported” for 2 frames instead of 1 - initial frame before moving controller.

I am trying to figure out why we have to do that - is there something I am not aware of (within library itself) - why it is still “supported” on 2nd frame? Should it be like this or Is it a bug?

This is very unintuitive.
I was trying to make my own controller, so I was using logic provided by engine \ library - supportedState.

  1. Player jumps - I check supportedState - if it’s SUPPORTED - I let player jump and apply jump impulse.
  2. Next logical step would be to check after that frame when player lands, but supportedState still returns state as still SUPPORTED. (which initially made me think I am doing something wrong, so I went into PG example and it behaves the same there)

So in order for it to work we need our own wrapper \ our own ground state detection.
I do believe this should not be like that, but…maybe there is a reason for it and I just don’t understand something?

Holy crap… When did we get a Havok Character Controller ?

BABYLON.PhysicsCharacterController

Yo @Cedric … Is there Havok Vehicles Yet ?

Changed title to PhysicsCharacterController.

Since last December, just before Christmas :smiley: Physics character controller

No planned vehicle yet. Maybe for Babylon 9 ?

I like this havok vehicle controller quite a lot even if not official ^^

1 Like

frame 0
State is on_ground, user presses space, wantJump = true
frame 1
physics tick
State is on_ground, state START_JUMP, up vector added to desiredLinearVelocity

For frames 0 or 1, velocity has not changer, nor position so support is still on.
Basically log happens before any change and physics tick so I’m not surprised by the support state here.
Let me know what I’m missing.

1 Like

Same here. This PG is a good intro to more complex car physics model.

Yea, you are right, on this PG we are setting velocity only after physics observable.
I might setup better example at some point based on what I’m doing in my actual project thing, I have same result when setting velocity on the same frame \ update cycle.

I get all data - input velocity , impulse velocity , gravity, then add everything together - pass it to controller and get same thing - player considered ‘UNSUPPORTED’ only on 3rd frame (impulse applied and velocity set on 1st) unless I do some messy workarounds (which I did)

Take care of before/after physics ticks. I think you can get a clear picture with console.log.
If there is a sync issue or latency in the controller, it will be easier to track with complete sequence.

1 Like

I think I see the issue now.

I had no idea about order in which everything happens in babylon.

  1. Physics
  2. Render

(I think?)

On top of that - support provides information based on previous physics step?

So it is not like a real time thing, it is basically always one step behind - if I check onBeforePhysicsObservable - it is going to give me information based on previous frame physics, if I check onAfterPhysicsObservable - it is going to give me information based on current frame, but physics already did it’s thing, so for next physics step that information would be, again - 1 frame behind?

Is that correct? My initial thought was it’s like “request real time ground state” of some sort, which seems wrong.

I think it’s a bit of a mix.
HP_World_ShapeProximityWithCollector will perform proximity testing and will fill the collector when it’s called. But physics bodies state (position/velocity) has been update the frame before.

cc @eoin

Yeah, this is expected, due to the way the character is written. For posterity, here’s an explanation of what’s happening. First of all, important to note that the character is completely decoupled from the physics step. The playground is using onAfterPhysicsObservable(), but the character could be updated without ever ticking the physics world. There’s three important functions for updating the character; two of which are in the BABYLON.CharacterController, which handle the geometry and one function in the playground, which handles the “game logic” parts to determine what kinds of actions (walking, jumping, double-jumping, flying, etc.) which are specific to the game.

CharacterController.checkSupport()

  • Examines contacts to determine support state

Playground getDesiredVelocity()

  • Updates character state, return a desired velocity for this frame

CharacterController.integrate()

  • Query+save new contacts based on where the character currently is as well as where they want to move in the future (the desired velocity.) Then, we modify the desired velocity to avoid penetration with all those contacts. Finally, we update the character position based on that new velocity.

Suppose we start standing still on the ground, with no user inputs.

  1. checkSupport() sees a single contact, pointing up
  2. getDesiredVelocity() returns 0
  3. integrate() updates contacts (unchanged), velocity is zero, character position stays the same

Then, user presses “jump” button

  1. checkSupport() sees a single contact, pointing up
  2. getDesiredVelocity() changes state to START_JUMP, wants a large upwards velocity
  3. integrate() updates contacts; we’re still in contact with the ground, but this doesn’t affect the desired velocity because it’s pointing the same direction; character moves upwards a little

Next frame, we’re in START_JUMP

  1. checkSupport() sees the single contact from last frame, sees the character as supported
  2. getDesiredVelocity() changes state to IN_AIR, uses gravity to decrease last frame’s large upwards velocity
  3. integrate() updates contacts; no longer in contact with the ground; character continues moving upwards

Next frame, we’re in IN_AIR

  1. checkSupport() no longer sees any contacts, returns UNSUPPORTED

So yes, this does result in a one-frame latency as checkSupport() sees the set of contacts which we calculated at the start of the previous frame’s integrate(), before the character’s position was updated. While it would be possible to update the set of contacts at the beginning of checkSupport(), we chose not to for performance reasons - generating those contacts requires a bunch of geometry processing.

2 Likes