pmndrs / ecctrl

🕹ī¸ A floating rigibody character controller
MIT License
533 stars 63 forks source link

Character Sleeping State Optimization #15

Closed MikeFernandez-Pro closed 10 months ago

MikeFernandez-Pro commented 11 months ago

This feature prevents physics bugs during page navigation and optimizes performance by pausing physics calculations when the character doesn't move or the user leaves the page. I also removed line 1036 canSleep={false} to use the default Rigid Body sleep behavior (canSleep = true).

ErdongChen-Andrew commented 11 months ago

Thank you @MikeFernandez-Pro ! This is fantastic. I have removed the canSleep={false} line. I think these code is not necessary, since the moving force should auto wakeup the character:

if ((forward || backward || leftward || rightward || jump) && characterRef.current.isSleeping()) {
      characterRef.current.wakeUp()
}

As for the window visibility check, I'm considering moving it outside the controller. This way, we can pause the entire physics simulation rather than just the character itself. What do you think? Here is an example how I would achieve this:

const [paused, setPaused] = useState(false)

useEffect(() => {  
    document.addEventListener("visibilitychange", () => {
      setPaused(true)
    })
    return()=>{
      document.removeEventListener("visibilitychange",()=>{})
    }
  }, []);

 return (
   <Canvas onClick={()=>setPaused(false)}>
   <Physics timeStep="vary" paused={paused}>
    ....
)

I truly appreciate your contribution. Thanks again! 😊

MikeFernandez-Pro commented 11 months ago

Thank you for the feedback, @ErdongChen-Andrew, I really appreciate it!

Regarding this part of the code, you're absolutely right, it can be removed:

if ((forward || backward || leftward || rightward || jump) && characterRef.current.isSleeping()) {
      characterRef.current.wakeUp()
}

However, to ensure that the character wakes up every time these two parameters must be set to true when those two methods are called:

characterRef.current.applyImpulseAtPoint(
      moveImpulse,
      {
        x: currentPos.x,
        y: currentPos.y + moveImpulsePointY,
        z: currentPos.z,
      },
      true
    );

   characterRef.current.setLinvel(
        jumpDirection
          .set(0, (run ? sprintJumpMult * jumpVel : jumpVel) * slopJumpMult, 0)
          .projectOnVector(actualSlopeNormalVec)
          .add(jumpVelocityVec),
        true
      );

Regarding the window visibility check, I have two concerns:

The first is that if we place it outside the controller and use useState to control it, it will refresh the entire physics component and its child every time the page visibility changes. Additionally, it would become an external element that the user has to add in addition to the controller.

The second concern is that users may not necessarily want to stop all physics when changing pages but only the controller. There may be other elements in their scene that need to continue having their physics calculated at all times so removing pausing all the physics could be problematic.

The second concern is that users may not necessarily want to stop all physics when changing pages, but only the controller. There may be other elements in their scene that need to continue having their physics calculated at all times, so pausing all physics could be problematic.

I think that the most important aspect of this window visibility check is that it helps prevent any physics errors when changing pages and avoids unnecessary controller physics calculations. Unfortunatly there are still physics bugs even with those changes. It often happens that when changing pages, if you're on a sloped surface, you might pass through it even though the character is supposed to be in a sleeping state.

If you want / agree, I will continue to explore this issue to see if it's also possible to definitively solve this physics bug.

ErdongChen-Andrew commented 11 months ago

Thank you @MikeFernandez-Pro ! Those two impulse parameters were already set to true in version 1.0.40. 😁

You're absolutely right, pausing the entire physics simulation might not be an ideal solution. I've encountered similar issues you mentioned. 😅

In my demo case, those floating platforms also need to "sleep" when the tab changes, as they behave similarly to the character. If you're willing to look into this and help solve the bug, that would be wonderful! I'll also try some other methods to address this. 👍