Advertisement

Unit movement synchronization in fast paced games

Started by November 20, 2014 05:11 PM
10 comments, last by jeskeca 9 years, 11 months ago

Hello!

I have a task, to make an ARPG game work on multiplayer at work. Unfortunately the game was developed without considering the networking problems, so I'm in a bit hard situation. Please help me :(

As I said, the game is an ARPG, similar to diablo, there are a lot of moving units at the same time, who are finding paths for themselves, colliding with each other, and attacking the player, or each other.

The units communicate with the control with commands. Control can be AI or player input.

So, when the player clicks on an enemy, a command structure is created, holding the target unit ID, or the position/direction where the spell will be casted.

Looks like something like this:


struct SBattleCommand
{
   ECommandType m_eType;   // the type of the current command
   DWORD m_dwUnitID;          // the ID of the unit, who is performing this command
   DWORD m_dwTargetID;      // the target ID, may be invalid
   D3DXVECTOR2 m_vTargetPosition;
   D3DXVECTOR2 m_vTargetOrient;
};

Similar thing happens, when the AI decides to attack the player/cast a spell somewhere or on something.

Then the Unit receives this command, stores it, and executes in time.

The pathfinding works in a little tricky way( because there are many units ), it refreshes on a random timer( not really random, has a seed generated from it's ID, what decides when to update the path ). The path finding only looks for a certain depth in one tick, so the whole path may not be found in the frame the command is added.

Also, the units are trying to avoid each other when finding a path, also, if something happens, and they collide, they push each other out.

So this is it, this is what I have to synchronize between the server and up to 10 players( usually max 4, but there are game modes which can be played with 10 players ).

My approach was this:

When the player inputs a command, it is immediately executes, and when the server receives it, it broadcasts to everyone else.

The AI only runs on the server side, when it assigns a command to the unit, it is broadcasted to everyone. The commands are sent with reliable_unsequenced flag, meaning that it is guaranteed that it will arrive, but can arrive out of order, and I handle if one of the packets are too late, it will be dropped.

The path finding is done on every client side, because the target position, and the source position is known, and it would be a lot of data, synchronizing.

Also, when receiving a command on the client side, the program checks if there is too big difference between the current position of the unit, and the position what is sent with the command, and interpolates if neccessary.

The problem with this is that when there are a lot of units moving in the same direction( e.g. trying to attack the player ), they will collide with each other, and because the positions are not pin-point accurate, the pathfinding may give back different path. Like trying to get around of a group from left, when it's doing it from the right on the server side. Thus, it desynces easily, and the constant position lerping is really ugly.

Is my approach correct? Or should I do it in other way? Please help me!

Also, sorry for my English, I'm not native.

Thanks in advance!

First of all: "Just add networked multiplayer" is a little bit like "just add a baby to this couple" -- it actually changes EVERYTHING.

Second, if the game wasn't built for multiplayer, the best you can probably do without TOTALLY re-architecting everything would be to treat one game instance as the server, and have the commands from the other clients go to the server to be executed. Then, the server continually sends the state/reactions of all the units to the clients, and the clients are all passive viewers of the world state. This introduces latency for the players who are on clients, but that's a small price to pay for a method of networking that is actually possible to add to an existing game without total re-architecting.

Note that you'd still need to re-architect the UI -- instead of directly applying to objects, it would have to send requests to apply to entities, and the actual display updates would have to happen when the updated data arrives from the server.

You cannot use input-only synchronization with Unity -- it does not use a deterministic simulation engine. Your best bet is to just send "position/velocity/animation" data for each entity with significant frequency. Maybe more frequent for player characters than monsters. Then just interpolate those entities along the known path on the viewing clients -- don't attempt to do path finding.
enum Bool { True, False, FileNotFound };
Advertisement

Thank you for responding!

Yeah, I didn't like the idea either, but this is my job, so I have to do..

I've rewrote a lot of things to be compatible to multiplayer, everything is working, just the interpolating is looking bad.

Also, I don't use Unity engine, I'm using an engine made by my company. Any rework is possible, if neccessary.

So, I should throw out the command synchronization, and just save a state of the close units at every timestep( like 15 ms? ) and just lerp? Shouldn't I predict anything with the units? Only the local player?

Also, what's the ideal bandwidth usage for an ARPG?

Thank you for your help! :)

You can save/send/sync unit locations every 200 milliseconds and it will look fine. 200 milliseconds is very little time for a "clicky" game and is still quite playable for a controller-FPS game like HALO. Only the twitchiest of shooters (Unreal Tournament, etc) would be bothered by 200 ms.

I would not predict anything -- I'd run everything in the past. This means that the player character itself would also run in the past. The reason being:
1) I click where to go
2) packet makes it to server
3) server starts moving my character
4) position/velocity update makes it to my client
5) I see my character start to move

This is similar to the RTS game networking principle.

what's the ideal bandwidth usage for an ARPG?


If you have deterministic simulation, and basically run the RPG like a RTS game, the bandwidth usage would be less than a kilobyte per second or less. All you need to send is initiating events like user clicks, time stamped, and initial entity spawn events. The simulation (including AI) would then run the same on all the machines. Note that this model *requires* the command delay that is outlined above. A good article for that model is here: http://www.gamasutra.com/view/feature/131503/1500_archers_on_a_288_network_.php

If you do not have a deterministic simulation (and chances are, you won't,) then each entity would have to update position/orientation/velocity/goal at some frequency, and you'd multiply the size-of-update by number-of-entities to get the bandwidth used, after adding protocol/framing/packet overhead. Twenty units, times 32 byte updates each, times 10 updates per second, would cost about 10 kB/second with those overheads included.
enum Bool { True, False, FileNotFound };

Not predicting makes the game really sloppy( i've tried that in the past ), with like 200 ms latency, the game is unplayable.

So what bandwidth should I aim for? What's the limit? Currently, without really caring about compressing the packages, it was about ~20 kbytes per second, with 100 or more moving units. Ofc I'll do some compressing, also will cut the packets into 2 parts - a reliable packet, which updates things what don't always change, but is essential( like HPs, animations, status ), and an unreliable packet, which will be sent more frequent, but contains only positions, and so, which won't be sent to some users( who wouldn't see it either way ), and doesn't matter if some of the packets are dropped.

Am I doing it right?

with like 200 ms latency, the game is unplayable.


Diablo would be totally playable with 200 ms latency, so you must be doing something different.

Note that some pretty high-end games, like Guild Wars, seem to be doing it this way. When the internet (or the server) is slow, actions take a significant amount of time to take effect.

So what bandwidth should I aim for? What's the limit?


I can't answer that question without more information. What kinds of users are you aiming for? Do people play over mobile networks? Are you aiming only for the inner cities of Korea and Japan? Should it work fine over Hughes Satellite with 1.6 second downstream latency, and 56 kbps modem upstream?
Will your users also do other things in the household, like calling on a VoIP phone, or watching Netflix, or sharing files with Bittorrent?
enum Bool { True, False, FileNotFound };
Advertisement

Well, the game will be on PC - xbox - ps platforms, I think it's acceptable if it doesn't work on mobile internet.

Users usually talk on Skype, I guess, when playing, but lagging while bittorent is active is acceptable.

I don't really know about Korea and Japan, but we have some whiners from russia, who has relatively bad internet connection..

the game will be on PC - xbox - ps platforms


You should get ahold of the qualification requirements ("tech certs") for the console platforms, and see what they think about network throughput usage. They will fail you if you use more than their allotted budget. For the previous-generation consoles, that was about 8 kilobytes per second; I can't say what it is now.
As long as you pass tech cert on consoles, you'll probably be OK on PC, too.

I don't really know about Korea and Japan, but we have some whiners from russia, who has relatively bad internet connection..


If Russia is on one end of the spectrum, the well-connected parts of Japan and Korea are on the other end :-) A few cities in the US have gotten Google Fiber, too. A game that targets only gigabit-level throughput is quite different than one that targets the dial-up-like speeds you find in many places like Russia.
enum Bool { True, False, FileNotFound };

In the XBOX One documentation, it says 192 kbps is the limit.

Currently, after optimalizing the packet size, and frequency, i have now around 56 kbps. I think it's fine, right?

Yes, that looks fine. (Assuming you're not confusing bits/bytes somewhere :-)
enum Bool { True, False, FileNotFound };

This topic is closed to new replies.

Advertisement