Player
Overview
Player
(Player
script, Player
prefab) represents a connected peer in the game and has no visuals. Player provides access to common metadata - UserID, Nickname, selected character, and other data that should survive the respawn of the Agent
(visual representation spawned in the game world).
Agent
(Agent
script, AgentBase
prefab, and its variants) represents an in-game character that is controlled by the player. It is spawned by GameplayMode
and has Health
, Weapons
, and other components. The character is spawned and despawned as needed.
The following diagram shows the component hierarchy as an execution waterfall:
Input Processing
The following diagram illustrates processing input and execution of actions:
Notes:
Frame
: Unity frame number (UnityEngine.Time.frameCount
). Frames 101 and 102 are incomplete for better readability.- At the start of each frame, input from the device is collected in
ProcessFrameInput()
and written to theRender Input
data structure (input authority only) - The same input is also accumulated into
Accumulated Input
(for example, look rotation delta) - If the accumulated delta time is big enough to simulate the next fixed tick (frame 103):
Accumulated Input
is polled/consumed throughNetworkEvents.OnInput()
callback- Player input is read from Fusion in
BeforeTick()
and stored inFixed Input
(executed on input and state authority) AgentInput.FixedUpdateNetwork()
call is forwarded toAgent.EarlyFixedUpdateNetwork()
which usesFixed Input
to process movement. This ensures all agents move before anyone starts shooting in current simulation tickAgent.FixedUpdateNetwork()
readsFixed Input
and processes shootingAgentInput.Render()
call is forwarded toAgent.EarlyRender()
which usesRender Input
(can also useAccumulated Input
) to process render predicted movement and look rotation.Agent.Render()
does NOT processes input for render predicted shooting as it is out of the scope of this sample. Shooting is done only in fixed simulation.
- If the accumulated delta time is NOT big enough to simulate the next fixed tick (frames 101, 102)
AgentInput.Render()
call is forwarded toAgent.EarlyRender()
(not shown in the graph above for better readability) which usesRender Input
(can also useAccumulated Input
) to process render predicted movement and look rotation.
This diagram is a simplified version and doesn’t cover all edge cases. For more details check the documented code.
Look Rotation Smoothing
The BR200 comes with a custom solution for a smooth look rotation under any conditions.
The following log illustrates the aliasing problem with raw input when using common hardware (mouse polled with 125Hz rate) and high render/output refresh rate (200+ FPS). The rendered output will always feel jittery, no matter how good the CPU/GPU is.
To fix this problem, input values are recorded with a timestamp, then the value for the current frame is calculated as the average of a defined time span (BR200 uses a 25ms window by default). This results in a butter-smooth perception experience (especially noticeable with a high refresh rate monitor) but introduces a very small input lag. Using hardware with a higher sampling rate allows you to decrease the smoothing window to a minimum.
The following graph shows raw mouse delta (bottom line) and character look rotation (upper line) in time:
The following graph shows smoothed mouse delta (bottom line) and character look rotation (upper line) in time:
This also helps to reduce sampling errors (desk surface) and micro-jitter caused by uneven hand/mouse movement (desk friction, muscles).
Character Animations
The project has a custom animation controller implementation based on the Playables API. It provides support for tick-accurate animation evaluation and dynamic performance scaling.
The following diagram shows the architecture which is similar to the Mecanim
:
Animation layers and states setup in the agent's object hierarchy:
Animation Layers:
Locomotion
: Base full body layer for movementFullBody
: Override layer for full body actions, blended with LocomotionLowerBody
: Override layer for lower body character turningUpperBody
: Override layer for upper body actions, usually blended with LocomotionShoot
: Override layer for hands actions (shooting), usually blended with LocomotionLook
: Additive upper body layer for looking up and down
Depending on the animation controller complexity, evaluating 200 players can easily become a bottleneck on the server. For better performance, the server allows for interlaced PlayableGraph
evaluation every n-th frame based on the connected players count. All important properties like layer or state weight are still calculated every frame. The following table shows the rules for an interlaced evaluation.
Players Connected | PlayableGraph Evaluation |
---|---|
> 150 | Every 6th frame |
> 100 | Every 4rd frame |
> 50 | Every 2nd frame |
Otherwise | Every frame |
Character Controller
This sample uses Fusion KCC (an advanced kinematic character controller addon) for movement. It is a generic low-level character controller with a strong focus on performance, gameplay interactions, and customization.
Fusion KCC features:
- Control over position and look rotation (pitch + yaw)
- Shape defined by Capsule collider
- Predicted Render movement for local player
- Combined dynamic (physics-like) and kinematic (unrealistic) velocity based movement
- External forces - from explosions, moving platforms, …
- Move acceleration and friction
- Advanced KCC Processors pipeline for customization (speed and direction override, blocking)
- Out of the box network synchronization for default properties (radius, height, mass, ...), optional synchronization of other - properties
- Custom Collider filtering and ignore list
- CCD (Continuous Collision Detection)
- Collision callbacks
- Support for ground snapping and step height
- Support for local mode (no network traffic)
- Network & performance optimized
- Platform independent, mobile friendly
- Basic support for frame by frame debug - editor drawings and logging
When a player's health is below a certain threshold, and the player is currently out of combat, auto heal kicks in and starts replenishing the player's health.
Jetpack
The jetpack provides the ability to fly and quickly navigate in the level while draining fuel. Fuel can be replenished by picking up fuel cans found in item boxes.
The state of the jetpack is handled by the Jetpack
script which handles fuel consumption, propellers, sounds, and on/off state. The actual movement in the air is handled by JetpackKCCProcessor
. This script overrides KCC velocities and suppresses default behavior.
Spectator Mode
When players are eliminated or join the game too late, they enter the spectator mode. In spectator mode, players can observe the game from other player's perspectives. In code, this is actually handled quite simply. The camera and UI act based on the ObservedAgent
assigned in the SceneContext
. The ObservedAgent
can be either the local player agent or spectated player agent.