– Revamped the code that shows other player’s vehicles in multiplayer. Their movement is now smoother, while not being any more delayed
– Much improved vehicle collisions between laggy players as well. No more flying across the map!
– Reduced some unnecessary network traffic
– Added option to have a radar in melee games
– Minor GUI adjustments
– Hover effect no longer shows on Melee mode button while an overlay is up
– Fixed real-time shadows not matching up with baked shadows at low graphics settings on the Test Map
– Fixed a timing bug on joining multiplayer games that could get you disconnected with a vehicle build error
Radar in Melee games
I said in a previous update that I wouldn’t add the radar I designed for Gauntlet mode into melee mode, because you should be able to hide from other players. But I figured it may as well be a custom option. Tick it at the lobby to enable it for everyone in the game (except AI – they still need line-of-sight to see you).
No more inadvertently flying across the map
This week I managed to fix an issue that’s been plaguing Scraps multiplayer since it was first released, and that I actually I thought might be impossible to fix. Something that might not seem very important at the moment while Internet multiplayer is quiet, but still a relief to conquer.
Scraps has always had an issue where sometimes, if you collided with another player and the latency between you was high, you could end up flying across the map. Like at 1:43 in this video where I’m playing from New Zealand on a US-based server:
It was… “fun”, sometimes, with quotes as necessary around “fun”. It was definitely not how things should work, particularly since you could end up taking mega collision damage as well.
To set the scene:
- In Scraps, each client PC is in charge of the physics for their own vehicle.
- The server also runs its own physics, but for movement it’s just checking what the client said (for most other things it’s the authority).
- Other vehicles you see are sending you their positions – they’re not running their own movement physics on your machine.
- Having said that, they still need collision boxes and momentum so that you can collide with them.
So each vehicle is doing their movement physics on their own machine, and getting sent the positions of everyone else. Since the “fly across the map” problem got worse as your latency got worse, I theorised that the problem was as follows:
- You and an enemy vehicle collide.
- Latency means that although your vehicle bounces back right away, they keep moving forward for a moment because both of you are seeing each other a little in the past, and you’re only doing physics locally for yourself.
- They therefore clip their vehicle’s collision boxes into your vehicle further than they should, sending you flying across the map due to the physics engine’s repulsion force that tries to keep things apart.
Basically their ghost invades your personal space and your vehicle tries to get away. And that would be potentially impossible to fix.
But it turned out that wasn’t the problem!
The Real Problem part 1: How games show other players
Let’s look at how a game shows other players’ positions first.
The above applies to any real-time multiplayer game. You simply cannot know where other players are right now, because there’s always some latency between your computers. There are two typical ways to handle this: Extrapolation and Interpolation.
With extrapolation, you say OK they were here half a second ago and going this fast in this direction, so they’re probably here now. But extrapolation sucks: In Scraps and especially in FPS games where players can change direction even faster. If players always ran in a straight line at the same speed extrapolation would be great, but it’s just a guess and often it turns out to be a bad one -meaning once the next real position update comes through, you’ll see things rapidly correct themselves to a new position.
So usually interpolation is the way to go, in Scraps and in other games. You show the other player a little in the past (1s in the diagram above is a big exaggeration just for easy numbers. Quake 3 used 0.1s for instance), so that you can have real data on both their past and future positions, and then show a smooth movement between them. No more guessing. There can still be some issues like the “bouncing ball problem“, but that’s getting picky.
Fun fact: Scraps actually dynamically modifies the interpolation delay to compensate for the latency of each player, so I can keep things smooth while also having as little delay on other players as possible. Scraps can also extrapolate a little bit, but only if it has to.
The Real Problem part 2: The real problem
Anyway, that’s all fine and good. But in your average FPS game the other players are just a set of colliders that have no actual velocity or momentum. I mean, they “move” around, but it’s more like they teleport each frame to a new position. Whereas in Scraps if an enemy vehicle collides with you they should be able to knock your vehicle away – not just keeping you out of their collision boxes but really pushing you with force based on their current velocity. They need to be actual physics objects working with the physics engine, while also just being data fed in from other PCs.
Scraps uses a networking library called uLink, and along with uLink came an example script for just this sort of thing. They had an interesting system, and I used the same sort of system for Scraps. It worked like this:
It only looked at the most recent data coming in from the other player, and it set their vehicle up with a velocity that would get it to that point at the right time. Therefore you had a vehicle with real physics that nevertheless always moved exactly where it was told to.
But what I noticed last week, and what I should really have thought about in the past, was that unreliable data could cause big velocity variations (of course this is also a lesson in trusting other people’s code…). For instance if you didn’t get any data from the other player for a bit, their vehicle would keep moving wherever it had been going for a bit and then suddenly have to do a big correction, giving it a big velocity – whether or not it actually had a big velocity on the other player’s PC (although really big corrections would just teleport instead, it wasn’t that dumb).
I noticed that the velocity actually fluctuated all the time, and I wondered why I hadn’t just tried doing what a normal FPS would do, but interpolating the velocity as well as the position, like so:
So I did that – and it works really well! Why didn’t I try that in the first place? Velocity is now interpolated along with position, rotation etc. The vehicles have real physics but still go where they are told to go, and the flying-across-the-map problem ended up fixed as well. And there’s no additional delay in where you see the enemy – the interpolation is set the same amount in the past as it was before. Turns out it must have been velocity fluctuations that were causing it, and what I thought was a problem with higher latency was actually a problem with less reliable connections.
You will still see a delay on high-latency connections in the other player getting pushed back in a collision. You hit them, you bounce back, they seem to stay still for a moment, then they bounce back too. That’s just because you’re only doing physics for yourself, so you’re waiting for the new collision to get sent out and then some back to you. The good thing is, everyone gets pushed back the right amount!
The bad thing is… no more exciting unplanned flights into orbit?