Lag Compensation using Bolt Physics

There are many ways to implement hit detection in multiplayer games.

In client authoritative games, often the client determines if something is a hit and notifies the server. He can implement this without any custom Bolt infrastructure using standard Unity colliders on the armature of the target. This happens in client space, so it is accurate for the client and very easy to implement but clearly not secure. This is a minor change to essentially how a single player game would work.

For server authoritative games, the primary goal is for the server to arbitrate hits. This adds a fundamental problem - if the server receives a command indicating the client has fired, and he evaluates this on the server at the current server frame, this will not accurately reflect what the client was seeing on his machine. This means, for example, a client could clearly hit another player but the server wouldn’t agree. In general this a bad paradigm; it is fundamental to the “feel” of multiplayer games and the reason why server authoritative games typically “favor the shooter” in industry-speak. In order to implement this Bolt adds an additional layer called lag compensation. By using custom Bolt hitboxes instead of standard Unity colliders on the character armature (described below), the server tracks the location of every hitbox in the game at each server frame over a short period in the past (around the last second). When receiving a command from a player indicating he fired at a certain frame, the server can accurately determine if the client hit his target on the frame he actually fired his weapon rather than the current server frame.

There are caveats to this approach, however:

  • Since you can no longer use Unity physics for hit detection, you must use Bolt’s two methods which are a raycast and an overlap sphere. These methods take a frame as a parameter and they are custom methods that query Bolt’s lag compensated space. There are no shapecast functions available. For example, if you wanted to mimic a volume of space for a projectile, you couldn’t use a spherecast - you would need to mimic this with multiple raycasts occurring simultaneously.
  • Bolt’s raycasts do not hit normal Unity colliders - they query bolt hitboxes only. Your hit detection methods will need to include standard Unity raycasts to determine if the raycast hit static geometry. Since static geometry doesn’t move, there is no need to lag compensate them so it is much more efficient to use standard raycasts.
  • The proximity hitbox will be returned as a hit - you will generally want to ignore this hitbox as it is only really used internally by Bolt for early out.
  • Bolt’s raycast raycasts every bolt hitbox in the scene on the input vector, and the results are not sorted on distance. This means you will need to implement your own sorting, and combine that with the static raycasts described above to arbitrate the final hit.
  • Currently scaled hitboxes are somewhat broken. Box hitboxes currently seem to work with scale, but the distance to the hitbox returned by Bolt raycasts doesn’t factor in this scale, so the distance will be wrong and you will need to manually remediate it. Sphere hitboxes will not work at all if any of their parent transforms are scaled.
  • Keep in mind one of the problems with favoring the shooter is that clients with high latency will cause a lot of angst for other players. This is because since we favor the shooter, often other players will duck behind a wall and still be hit because the firing player had high latency, but this player did fire when he saw the player on his machine and the server determined it was a hit - but the player being hit, having low latency, has already moved several feet behind a wall before he receives this notification. In some modern games like BF4, they have attempted to limit high latency players to a maximum of around 250ms of lag compensation - this prevents players with really high ping from damaging the play experience for the other players to some degree.

To use Bolt physics, add Bolt Hitbox Body component and Bolt Hitboxes in the children, then click Find Hitboxes on the Bolt Hitbox Body, the same way the player in the tutorial project is set up.

A proximity hitbox is an early out hitbox for optimization purposes. Bolt will check proximity hitboxes first before checking any of the underlying hitboxes. Since often you will have many hitboxes attached to an armature, it is wise to use a proximity hitbox to encapsulate that overall volume so that Bolt can avoid, in most cases, checking the hitboxes underneath.

Then use the functions RaycastAll or OverlapSphereAll:

Example use of RaycastAll:

void FireWeaponOwner(PlayerCommand cmd, BoltEntity entity)
{
    if(entity.isOwner)
    {
        using(var hits = BoltNetwork.RaycastAll(new Ray(entity.transform.position, cmd.Input.targetPos), cmd.ServerFrame))
        {
            var hit = hits.GetHit(0);
            var targetEntity = hit.body.GetComponent<BoltEntity>();

            if(targetEntity.StateIs<ILivingEntity>()) {
                targetEntity.GetState<ILivingEntity>().Modify().HP -= activeWeapon.damage;
            }
        }
    }
}

Example use of OverlapSphereAll:

void GrenadeOwner(PlayerCommand cmd, BoltEntity entity, IThrownWeapon grenade)
{
    if(entity.isOwner)
    {
        using(var hits = BoltNetwork.OverlapSphereAll(cmd.targetPos, grenade.explosionRadius, cmd.ServerFrame))
        {
            for(int i = 0; i < hits.count; i++)
            {
                var hit = hits.GetHit(i);
                var targetEntity = hit.body.GetComponent<BoltEntity>();

                if(targetEntity != entity && targetEntity.StateIs<ILivingEntity>())
                {
                    targetEntity.GetState<ILivingEntity>().Modify().HP -= grenade.damage;
                }
            }
        }
    }
}

PS: Please note that in Unity 5+, it is possible to write your own lag compensation layer by tracking PhysX colliders and reverting them back to their state at a certain frame. This allows you to use all of the PhysX features (including spherecasts, etc.). However, this is not implemented in Bolt and fairly non-trivial to get working in a performant fashion. Bolt’s implementation was written before this was possible.

To Document Top