Announcement

Collapse
No announcement yet.

Networked "Physics-Vehicle Movement Component": existing examples or implementation hints?

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

    Networked "Physics-Vehicle Movement Component": existing examples or implementation hints?

    I'm working on a multiplayer project that will include vehicle types such as hovercrafts + spaceships. I would like to be able to enable client-side prediction in the same vein as used by the existing CharacterMovementComponent, but it looks like this really hasn't been done elsewhere in the engine for non-character anything, even for the stock wheeled-vehicle class. Flying vehicle types seem like they should actually be a bit easier than the character movement (and certainly easier than wheeled vehicles), so two questions here, in decreasing order of priority:
    1. I'm wondering if there is a prototypical example for this floating around (even if just for 6DOF, without additional physics constraints), or if I'm stuck rolling my own? It feels like a common enough problem that there ought to be, but Googling hasn't turned up anything.
    2. If I'm stuck rolling my own, it seems like implementing INetworkPredictionInterface (and maybe RVOAvoidance) is the place to start in a PawnMovementController subclass. Am I missing any of the major moving pieces (pun intended) here?

    #2
    I was about to come in here and be all "but vehicles do have rewind/replay" because that's what I have legitimately believed for the last, I don't know, 11 months? Then I dug through some code and of course I am completely mistaken.

    There are a few people who have looked into this and made some nice progress (HateDread and Acren come to mind, and I'm sure there are others). It's worth reading through their threads (like this one) and answerhub posts to get an idea of some of the challenges.

    To get to the point:

    1. I haven't seen one, but that's obviously (see paragraph one) not a good indicator of reality. If I come across something promising I will leave it here. Having realized that my vehicle game does not have rewind/replay implemented, I have decided to spend the end of this week and the weekend attempting an implementation for my projectiles and the wheeled vehicle class itself. I haven't looked into how to replay physics deterministically but the way the vehicle wheel states are currently replicated as a function of inputs seems promising, and I suspect the CharacterMovementComponent implementation will reveal how to correctly acknowledge move timestamps between server and clients.
    2. I glanced through some of the CharacterMovementComponent code again, and I think implementing the INetworkPredictionInterface makes sense. I authored the RVOAvoidanceInterface class and I don't think that you should implement that unless you're anticipating a need for Reciprocal Velocity Obstacle Avoidance. You can always implement it later you need it; if I recall you should be able to just execute it in one place (eg, the server) and depend on the movement component's velocity in order to supply an adjusted velocity.

    Before getting started I recommend reading Glenn Fiedler's Introduction to Networked Physics .
    Work in Progress: King of Kalimpong
    piinecone.com | youtube | @p11necone

    Comment


      #3
      I'll be honest: I was really hoping to avoid implementing the whole Fiedler dance-routine myself from scratch, but I'm glad my basic analysis of the situation was pretty much correct.

      I'll probably be needing AI players at some point, but I guess RVO isn't a priority out of the gate.

      Thanks for the response, and please let me know how your experiments go this week. I'll be sure to post updates as well if I find anything particularly interesting in CMC.

      Comment


        #4
        Originally posted by elfprince13 View Post
        Thanks for the response, and please let me know how your experiments go this week. I'll be sure to post updates as well if I find anything particularly interesting in CMC.
        Cool. I will do that, and I'll publicize the relevant code if it goes well.
        Work in Progress: King of Kalimpong
        piinecone.com | youtube | @p11necone

        Comment


          #5
          Just a quick update. I didn't have time last weekend to get into attempting to predict and correct vehicle movement, but I did do an experimental physics projectile implementation. It went well, but I still prefer blending the client's simulation with the server's and snapping velocity in most cases. However, it occurred to me (because I think I saw HateDread float this idea somewhere) that instead of replaying position and velocity deltas for a projectile, I could `Tick` the scene component. Perhaps that would be a better replay than what I implemented, which was very simplified and would often discard the move queue to avoid a feedback cycle after a correction. Ticking the component feels like the equivalent of replaying the move for actors that move as a result of input.

          I started digging into the vehicle code but didn't have time to try anything interesting.
          Work in Progress: King of Kalimpong
          piinecone.com | youtube | @p11necone

          Comment


            #6
            Thanks for the mentions, piincone

            I actually didn't get too deep into this stuff; we ended up switching to LAN-only, and I basically implemented Valve's buffer interpolation method, however to do so you need a nice synchronized timestamp system that works regardless of ping, so if you have trouble with that just let me know!

            I'm also happy to answer anything that I can. This stuff is tough at times, and I'm not envious of you guys trying to get wheeled vehicles to work! So, good luck

            Comment


              #7
              Did this already for my Hovertank Movement Component:



              The system is really simple, what I do is first check 'Replicated Movement', which will first ensure that both vehicles remain in-sync with one another from Server-Side Movement. What I then do is create a Struct which has all of my input states. I later plan on quantizing these for optimization, since in reality I only care about the first decimal point in terms of granularity:

              Code:
              float ThrustInput;
              float StrafeInput;
              float SteerInput;
              float PitchValue;
              bool bIsJumping;
              I create two variables of that struct in the class, ONE is replicated and the other isn't. The non-replicated struct is used in the movement component to actually move the craft and perform all calculations, and ultimately is what updates the physics of the craft. That Struct is ALSO the one that is updated via the keyboard/input component - so clients simulate their own movement locally and instantly.

              I then send this movement data, on-tick, via an unreliable RPC to the server. This is the unfortunate downside as it requires me to send the RPC on tick, though for now it's fine since I'm only doing this for one vehicle per-client and therefore the overhead is pretty small. As it's also unreliable, the engine can choose when to send and not send packets depending on bandwidth. I might be able to get away with sending this on a Timer in future to reduce the amount of sends too, since Tick isn't always going to have the same Delta time, and therefore bandwidth usage will fluctuate up and down depending on framerate.

              The server then processes the movement on the craft from the same Input, and sends it back via the 'ReplicatedInput' struct. When this struct is received on the client, they update their local un-replicated copy of the struct, so that their input level matches that on the Server at that time. Additionally, the ReplicatedMovement from the Server (since it also processed the input) is sent back with it, and the position is updated on the client from that automatically.

              ---

              There are two caveats to this approach at the moment. The first is that unless the Server is sending back Replicated Movement constantly (I.e, calculating it) - there's the potential for a Client-Side collision to knock the two out-of-sync. I don't think this is an issue really, since I'm fairly certain that this is what the CharacterMovementComponent does anyway.

              The other issue (that is caused by the one above), which only became apparent in high-latency situations - is that the clients input gets simulated, but it gets overridden by the 'Replicated Movement'. Effectively what happens is the Client waits for the Server to simulate the movement and then moves accordingly. If I can get some finer control over the order of operations here, I can work around this though. For LAN play, the controller latency is practically nothing anyway.

              ---

              EDIT: I also forgot, I haven't yet got my implementation working with AI / pathfinding, since it's based on Input from the keyboard, and the current AI system in engine quite literally just 'set's the velocity of an object. That's fine, if you're working with characters. For anything else it pretty much sucks. I'll have to write my own interface between the two eventually, which talks directly to the input of the craft rather than essentially hacking it.

              Comment


                #8
                Originally posted by TheJamsh View Post
                Did this already for my Hovertank Movement Component:
                The server then processes the movement on the craft from the same Input, and sends it back via the 'ReplicatedInput' struct. When this struct is received on the client, they update their local un-replicated copy of the struct, so that their input level matches that on the Server at that time. Additionally, the ReplicatedMovement from the Server (since it also processed the input) is sent back with it, and the position is updated on the client from that automatically.
                Very cool. Do you then replay the client's inputs since the client move that was executed on the server and received by the owning client again (in the case of a disagreement)? If so, what does the replay look like? Are you ticking the component outside of the owning actor's `Tick()`, or just reapplying deltas?

                Originally posted by TheJamsh View Post
                Did this already for my Hovertank Movement Component:
                There are two caveats to this approach at the moment. The first is that unless the Server is sending back Replicated Movement constantly (I.e, calculating it) - there's the potential for a Client-Side collision to knock the two out-of-sync. I don't think this is an issue really, since I'm fairly certain that this is what the CharacterMovementComponent does anyway.

                The other issue (that is caused by the one above), which only became apparent in high-latency situations - is that the clients input gets simulated, but it gets overridden by the 'Replicated Movement'. Effectively what happens is the Client waits for the Server to simulate the movement and then moves accordingly. If I can get some finer control over the order of operations here, I can work around this though. For LAN play, the controller latency is practically nothing anyway.
                Character movement uses clientside prediction + rewind/replay, so the server lets the owning client know when it disagrees with the results of a move so the client can rewind back to the move in question, correct it, and replay all the clientside moves since then. If the moves aren't replayed, you'll notice the client's simulation snapping back to the last one received from the server, which is of course movement from the past as far as the client is concerned.

                I noticed with the wheeled vehicles that the controls start to get a bit floaty as soon as some normal latency is involved. I'm planning to give the client control, but I'll have to sort out replaying physics states and acking timestamps between client and server (might be pestering you about that soon, @HateDread).
                Work in Progress: King of Kalimpong
                piinecone.com | youtube | @p11necone

                Comment


                  #9
                  Originally posted by HateDread View Post
                  Thanks for the mentions, piincone

                  I actually didn't get too deep into this stuff; we ended up switching to LAN-only, and I basically implemented Valve's buffer interpolation method, however to do so you need a nice synchronized timestamp system that works regardless of ping, so if you have trouble with that just let me know!

                  I'm also happy to answer anything that I can. This stuff is tough at times, and I'm not envious of you guys trying to get wheeled vehicles to work! So, good luck
                  Of course! I am very curious about the timestamp acking. When I implemented the moves buffer for projectiles I just used a move count. Not sure if that's a horrible idea or not.
                  Work in Progress: King of Kalimpong
                  piinecone.com | youtube | @p11necone

                  Comment


                    #10
                    Originally posted by piinecone View Post
                    Of course! I am very curious about the timestamp acking. When I implemented the moves buffer for projectiles I just used a move count. Not sure if that's a horrible idea or not.
                    Not sure on that either! Could work out well or be totally terrible. Do you have any idea how you'd sync a time value across the network? Think about the value you get from GetRealWorldSeconds(), and the fact that it'll differ on clients and server, so you could calculate an offset maybe an apply it? Maybe

                    Comment


                      #11
                      Originally posted by piinecone View Post
                      Very cool. Do you then replay the client's inputs since the client move that was executed on the server and received by the owning client again (in the case of a disagreement)? If so, what does the replay look like? Are you ticking the component outside of the owning actor's `Tick()`, or just reapplying deltas?
                      No not yet, because 'Replicated Movement' just sets the actors location anyway, and usually the discrepancy is so tiny or non-existent than the movement is fine. In latent situations though that probably won't be the case, so I'll need something a bit more robust for that.

                      Originally posted by piinecone View Post
                      Character movement uses clientside prediction + rewind/replay, so the server lets the owning client know when it disagrees with the results of a move so the client can rewind back to the move in question, correct it, and replay all the clientside moves since then. If the moves aren't replayed, you'll notice the client's simulation snapping back to the last one received from the server, which is of course movement from the past as far as the client is concerned.

                      I noticed with the wheeled vehicles that the controls start to get a bit floaty as soon as some normal latency is involved. I'm planning to give the client control, but I'll have to sort out replaying physics states and acking timestamps between client and server (might be pestering you about that soon, @HateDread).
                      Yeah this is something I'm probably going to have to do eventually, the issue I have is because the Physics System in Unreal isn't deterministic - there's so much room for error. I still want to keep my physics-based movement because it's so lightweight and fluid, but I may simply have to bite the bullet eventually and switch to something like CMC.

                      Comment


                        #12
                        Glad to find this thread - I'm going to need to implement a vehicle 6DOF MovementComponent implementing INetworkPredictionInterface soon myself.

                        I've implemented a basic velocity and acceleration based movement in Blueprint but of course found it isn't good enough for anything greater than local latency.

                        @TheJamsh - would you be willing to share your Hovertank Movement Component code with us? I'd be happy to share any adaptation of it for 6DOF movement back with everyone.
                        Descent: Underground |

                        Comment


                          #13
                          Originally posted by HateDread View Post
                          Not sure on that either! Could work out well or be totally terrible. Do you have any idea how you'd sync a time value across the network? Think about the value you get from GetRealWorldSeconds(), and the fact that it'll differ on clients and server, so you could calculate an offset maybe an apply it? Maybe
                          I still haven't had much time to implement this (or even think about it), but it occurred to me that I could send the client move timestamp with the move, and the server would return the same move's timestamp when it returned the result of the move to the client. Seems like it can all remain relative since the purpose is to ack the move request/response for correction - the client doesn't need to know what time it is on the server, just needs to know if the server disagrees and which move it's talking about. Think that would work? I'm hoping to find time to try it out in the next week or so.
                          Work in Progress: King of Kalimpong
                          piinecone.com | youtube | @p11necone

                          Comment


                            #14
                            - Post Redacted - Some Content now NDA
                            Last edited by TheJamsh; 09-19-2015, 09:07 AM.

                            Comment


                              #15
                              Hey TheJamesh, thanks for sharing that! I'm looking at getting back to my 6DOF movement stuff (the old way was all Client-authoritative and only worked on LAN).

                              Before I get into your code in the morning, I had a few quick questions. I apologise if you've explained this (it's late!), but are you using a Character? I wasn't sure if you were replacing the default CharacterMovementComponent or just adding one to a Pawn. The latter definitely makes more sense though.

                              Additionally; how can you be replicating movement with the default checkbox, yet have your system at the same time? Are they not at odds?

                              Thanks again for the code. Did you manage to solve the problems you were talking about?

                              EDIT:

                              piinecone, did I ever explain the time sync stuff to you? I can't remember if we talked about it at some stage.

                              Comment

                              Working...
                              X