ramokz / phantom-camera

👻🎥 Control the movement and dynamically tween 2D & 3D cameras. Built for Godot 4. Inspired by Cinemachine.
https://phantom-camera.dev/
MIT License
1.93k stars 62 forks source link

Damped follow of physics objects produces jitter at <60 FPS #206

Open WeaselOnaStick opened 5 months ago

WeaselOnaStick commented 5 months ago

Issue description

The video should explain it pretty well. Basically if the camera is set to follow a rigid body, and the follow has damping, then if FPS dips below 60 you will get this nasty jitter. I added a simple overhead camera to verify that the actual position of said rigidbody isn't jittering itself. Tested with all camera follow modes with/without damping (even toggled between jolt/godot's physics) and I can confirm that it's because of damping.

https://github.com/ramokz/phantom-camera/assets/10953814/3ccae39f-111d-41bd-bb7a-5394b7822d4e

Steps to reproduce

Simple vehicle Phantom Camera with follow + damping

(Optional) Minimal reproduction project

dispensable.zip

ramokz commented 5 months ago

Thanks for reporting this and sharing the project files!

Am doing a refactor of damping system in the near future (#92). Will validate the changes with your attached project and see if that solves it.

TCROC commented 4 months ago

I managed to solve this on my project today! Good news is: it didn't require making any changes to phantom-camera! I'm on mobile right now and will post a more detailed summary tomorrow as the problem is a tricky one to wrap your head around but is actually quite simple to solve.

There are a few things that can cause this problem:

1. Different update rates

I suspect this is the exact issue you are encountering above. The solution is to make sure your camera and physics object update their positions at the same rate. When you reduce the frame rate, your camera is updating its position at a slower rate than the physics object because the physics object updates independent of your frame rate. Physics engines tend to run at either 50 or 60 steps per second. In your cause probably 60 since 60 appeared smooth for you.

How to solve?

3 approaches:

  1. Set the process mode on the camera to disabled and manually call the camera's _process method from another script's _physics_process method. This will ensure the physics and camera move in sync with each other. The trade off is that your surrounding environment will likely appear jumpy. It's less noticeable, but we can do better ;)

  2. Set your physics update rate to match your fps. Then your camera and physics objects are again updating in sync! :) ... DONT DO THIS! This will cause your physics engine to do strange things as the frame rate fluctuates. Especially low FPS. You will see your vehicle turn into a rocket, launch to Mars, and turn into the latest and greatest Mars rover ;)

  3. Interpolate the 3d physics nodes yourself between fixed updates! This is the one I use! :) Basically, you cache the previous point in 3d space the physics object was at, its current point, and then smoothly interpolate to those points in the _process method between _physics_process steps. As always, there are some trade offs to this approach.

The physics body will always be rendering the previous physics step so will always be in the past. It may be prone to clipping (very very small) and prone to not visually showing exact contact (again very very small). So small that it's unnoticeable in fast paced games.

It's what we use in our game over here: https://store.steampowered.com/app/1343040/Blocky_Ball/

We are currently porting it from Unity to Godot. This asset is proving to be a tremendous Cinemachine alternative!

I'll grab some more details for you tomorrow and share the resources I studied. And if I get some time later, I'll take a look at your example project and see if I can fix the stutter. Then we'll have a nice example to share with the community if you want to contribute it! :)

(Typed all this on my phone. Hopefully autocomplete did its job well :) )

TCROC commented 4 months ago

Oh and I realized I only talked about point 1 that I have found to cause jitter (which I believe to be this issue). But I'll list them all out for clarity tomorrow as well

williamviktorsson commented 4 months ago

I had quite som jitter as well on both my Mac and PC which went away completely when i swapped the _process(delta: float) to a _physics_process(delta: float) in the phantom_camera_2D.gd script.

I´d be happy to learn why this might be a bad or good idea.

When searching for the same issue on using Position Smoothing on the built in Camera2D I often saw changing to physics processing as a suggested fix so I applied it to the PhantomCamera2D and it had the same result.

ramokz commented 3 months ago

I had quite som jitter as well on both my Mac and PC which went away completely when i swapped the _process(delta: float) to a _physics_process(delta: float) in the phantom_camera_2D.gd script.

I´d be happy to learn why this might be a bad or good idea.

The issue with shifting the addon's movement logic to _physics_process instead of _process is that any movement that would occur in the _process would then become jittery. Which would be things like when you use the built-in Tween system or generally moving things around that doesn't need to be frame rate independent. There is a good example in #179 for why it's both an issue and why the solution isn't straightforward, but at least mitigated in the upcoming 0.7 release.

TCROC commented 3 months ago

This is correct. I am very curious to see which mitigation techniques are used in 0.7! :) Between Unity's Cinemachine and now this amazing addon, I have not yet found a general purpose solution to the jitter. Even in my own custom projects. The only solution I've been able to find to consistently work and improve the experience is in summary:

Update and move the camera at the same rate as the objects it is focused on. Then move the surrounding objects at the same rate as well. But the most important one is the objects in focus as that is where the player's eyes will be drawn to.

Sorry I haven't posted my example project. Had some personal things in life come up that detracted away. I'll get around to it eventually as I think it'll be helpful for learning and a good point for us to collectively improve upon.