grazianobolla / godot4-multiplayer-template

Client/Server Authoritative Multiplayer Framework for the Godot4 Engine
MIT License
141 stars 13 forks source link

MoveAndSlide() #8

Closed brogan89 closed 1 day ago

brogan89 commented 3 days ago

The current issue with the player movement. It's updating the position and not using MoveAndSlide() which is should so IsOnFloor() can return correct results.

The larger issue is that it kills rollback as you can't pass in a custom delta for deterministic physics simulation. There is an issue currently in discussion about it here: https://github.com/godotengine/godot-proposals/issues/2821

Looks like there is already a PR for the feature: https://github.com/godotengine/godot/pull/84665

So really the rollback wont be very accurate for CharacterBody3D movement until this feature is in main.

inFamousOne commented 3 days ago

while kinda true you can still use MoveAndSlide() just fine. as a work around you can just use some simple math to cancel out the delta before making the call. and re apply it after.

public void ReceiveState(NetMessage.EntityState state, int forTick){
    ...
    expectedVelocity = PlayerMovement.ComputeMotion(
        expectedTransform,
        expectedVelocity,
        PlayerMovement.InputToDirection(userInput.Input));

    Velocity = expectedVelocity;
    Velocity *= (float)GetPhysicsProcessDeltaTime() / (float)GetProcessDeltaTime();
    MoveAndSlide();
    Velocity /= (float)GetPhysicsProcessDeltaTime() / (float)GetProcessDeltaTime();

notes you would also need to just return the velocity from compute motion no need to make test_motions or slide checks. this works just fine in my case. adding jolt also helps as its a more stable physics engine. also the approach taken here for the prediction is a bit backwards. since we are re calculating every received tick. I instead would recommend storing the last few states. and compare the past tick to the incoming one for deviations. then run the simulation.

all together it seems to be quite robust for my case. (making a multiplayer fps game) using this template.

also this work around have been known for a while so def give it a try https://github.com/godotengine/godot-proposals/issues/2821#issuecomment-854081858

brogan89 commented 3 days ago

Thanks @inFamousOne , I did see that comment but didn't really quite understand how to implement it.

I had a bit of a work around for processing the inputs, I would queue the in coming states and the dequeue them on the physics process so that MoveAndSlide() was always being called on a physics frame.

Trying that solution you posted (and even with my little work around) I still get a pretty inconsistent positioning after reapplying inputs. It could well be that I'm just missing an important step that I'm not seeing.

I'm running the client side simulation on a different CharacterBody3D, a "ghost" of what the server see's basically. But its always next to the player and not on the same position. You can see on this image.

image

The server position is correct, but after I apply the outstanding inputs on the client its always off and I'm not really sure why. In this image I've only just spawned in, no movement inputs have been applied, so I'm not sure how the simulation things it should be at a different positions.

PS: I am using Jolt in my project.

inFamousOne commented 1 day ago

The solution I proposed should fix it based on the current implementation as of the master branch. in your case there could be a number of things that could be causing this issue. such as the ghost collision layer is actually colliding with the "active" players. the spawning being set incorrectly for the ghost. corrections not being applied after a deviation was found ect. Its hard to say with out seeing the code. I would just recommend maybe starting from the current solution which is to check for them as they are incoming and trying out the work around I suggested and going from there.

grazianobolla commented 1 day ago

Hi All, Yes, I'm still trying to figure out how to properly do "actual" physics in the template... As far as I remember, I couldn't use MoveAndSlide() (which would solve soooo many problems) because of the issue of not being able to call it multiple times inside a loop, so what I did was more or less just implement a simplified MoveAndSlide() inside the ComputeMotion() method (hence why I do the test_motion and in some older examples I even did slide checks multiple times exactly as it was donde inside the actually MoveAndSlide() cpp source code). All of this was just a big ugly workaround that limited a lot what you can do with your character physics. I will definitely take a look at @inFamousOne comment and specially at this https://github.com/godotengine/godot-proposals/issues/2821#issuecomment-854081858 which at first glance seems to be exactly what we needed.

If any of you manage to make that work and want to share it I will be more than glad!

inFamousOne commented 1 day ago

@grazianobolla Sure! if you would like I can make a pr using the solution from my first comment.

grazianobolla commented 1 day ago

Yeah I would love that, I will take a look at it and we can discuss it. Thank you @inFamousOne

inFamousOne commented 1 day ago

@grazianobolla feel free to review the pr when you can. also @brogan89 you my take a look to see how I handled using move and slide if you would like

brogan89 commented 1 day ago

Thanks so much @inFamousOne . That was very helpful to see the changes. I noticed a couple things I did wrong in my project! I'm just using the ghost for visual reference on client now.

I think the core of the issue was not storing the clients state along with the input, that seems to the main part that I think.

brogan89 commented 1 day ago

Fixed by PR #9