Miscellaneous
SDK Modifications
The FPS Template handles several features differently than the default Quantum SDK.
All of code paths where the default Quantum and FPS Template implementations diverge are marked with a QUANTUM_DEFAULT
if-else define.
C#
#if QUANTUM_DEFAULT
var matrix = Matrix4x4.TRS(t.position, t.rotation, t.localScale);
#else
var matrix = Matrix4x4.TRS(t.position, t.rotation, t.lossyScale);
#endif
Quantum SDK
The changes made to the Quantum simulation in the FPS Template compared to the Quantum SDK are:
- redefinition of the
FrameContextUser
to support a constructor with more parameters and custom processing. - increase of the
RuntimeConfig
size limit from 1kB to 8kB. - disabling all systems in
OnGameEnded()
. - added profiling timers for systems (configurable and enabled by default) and certain code paths.
- To support all features, entities have to be created and destroyed using frame.CreateEntity() and frame.DestroyEntity() (except a very special cases)
- Added several methods to get components and controllers to frame API
Unity Integrations
The changes made to the Unity-side in the FPS Template compared to the ones included in the Quantum SDK are:
- an Extended Entity inspector to support child and owner entities.
- an additional option to Bake Prefabs via the
MapData
component inspector and theQuantum > Bake > Prefabs
menu. - bypassed some
QuantumRunner
functionalitoes (except theInit()
method) and replaced with theSimulator
because of the different architecture; N.B.: it is still used to bridgeQuantumGame
with other tools such as the state inspector. - bypassed
EntityViewUpdater
and replaced withEntities
because of the different architecture. - bypassed
EntityView
and replaced with theEntity
component because of different architecture. EntityComponentBase
inherits from a customEntityComponent
script.- disabled
QuantumRunnerLocalDebug
andQuantumRunnerLocalReplay
. - replaced all default replay scripts by custom implementation because of the different architecture.
QuantumStaticBoxCollider3D
andQuantumStaticSphereCollider3D
use lossyScale instead of localScale.- added more transform synchronization variants for various scenarios.
- entities are always spawned on predicted frame by default; spawning in verified frame must be handled directly in the simulation.
- used Newtonsoft.Json instead of Unity JsonUtility to support serialization of fields with [SerializeReference] attribute to preserver compatibility with server-side deserialization.
Workflow Recommendations
The following workflow recommendations are to facilitate updating and upgrading the FPS Template by preventing conflicts.
- Always try to keep your code and assets organized according to their respective core environment (Framework / FPS / YOUR_PROJECT). This is applicable for both Unity and Quantum solutions.
- If modifying or extending the core Framework code, use partial implementation whenever possible. Most of the classes and structs are marked as partial or have partial methods for exactly that purpose.
- Always prefer
AssetRefEntityPrototype
fields over hard-coded strings to create entities. - All controllers are stateless - do NOT modify their fields at runtime as they cannot be rolled back. Caching non-serialized static properties is possible; however, the assignment must be an atomic operation or a race condition will appear in multi-threaded environments.
Using Unity Engine
It is not allowed to link the UnityEngine.dll
with the Quantum solution as this could negatively impact server performance. It is, however, possible to mimic its API by using data structures and attributes through DLL hijacking.
A solution on how to build a custom UnityEngine.dll
with the same signature can be found in REPOSITORY/extras/UnityEngine
. It contains data structures as well as attributes, and can be modified as needed. After building the solution, UnityEngine.dll
is copied to REPOSITORY/assemblies
. This library is NOT referenced in Unity, but its original UnityEngine.dll
is.
This custom UnityEngine.dll
has to be included when uploading a custom Quantum plugin to the server (only available on an Enterprise server, please contact developer@photonengine.com for more information on the subject).
Framework Performance
This is target configuration and statistics for low performance mobile devices:
- Simulation rate: 30 Hz
- Target refresh rate: 30 FPS
- Hard tolerance: 7 frames (~230ms)
- Supported ping: ~250ms
- Primitive colliders: 500 (75% box)
- Players: 2 + 6 AI
- Active projectiles: 50
- Other actors with custom controllers: 50
- CPU budget: 16.667ms (50% of frame)
- GPU budget: 16.667ms (50% of frame)
- Simulation budget: 4.167ms
- Simulation time of 1 verified frame with all features enabled: ~2.8ms
- Simulation time of 1 predicted frame with all features enabled: ~1.1ms
During 1 Unity frame, the game computes on average 1 verified Quantum frame and 3 - 5 predicted Quantum frames; this takes around 7-8ms. The final performance numbers heavily depend on the actual feature set used. A lot of performance can be saved by implementing simpler AI, not using simulation based autofire, and / or only enabling systems which are absolutely needed.
The number of players is an important factor as well. The fewer players are in the same gameplay scene, the more expensive the logic can be and vice versa; this is assuming the empty player spots are filled up with AI actors.
Network conditions resulting in a spike of predicted frames (up to 40) are extremely rare. However, these kind of occurence are not easily avoided on low performance devices and is a key-consideration for the settings in performance profiles.
Debugging
Logging
Some subsystems of the FPS Template contain logs. These logs can be enabled to see more information on what is happening at runtime.
Global level of logging can be changed via the Severity
property in the GameSettings
asset. You can also specify log group overrides.
Alternatively, the level of logging can be changed at runtime via the following APIs:
- globally:
Log.SetSeverity(ELogSeverity.Warning);
- per log group:
Log.SetSeverity(ELogGroup.Matchmaking, ELogSeverity.Info);
Desynchronization
Debugging state desynchronization is not a straightforward process; especially when there are lot of one-time actions.
These are our recommendations for facilitating the debugging process:
- Try to find a situation with stable and quickly reproducible desynchronization.
- Calculate and log checksums inside the Quantum simulation in the first, middle and last system (only on verified frames!). This will provide a baseline.
- Depending on the checksum differences, a proceed with a new interval division. The new first system will be:
- the center system if the checksum was different between it and the last system; or,
- the last system if the checksum was different between it and the center system.
- Repeat step 3. until the system causing the desynchronization has been identified.
- Apply whole process on an entity basis to isolate the component, controller or other aspect of the simulation causing desynchronization.
Common Issues
- Despawning/Respawning predicted entity due to resimulation: toggle the
silent
boolean in theEntity.OnDeinitialize(bool silent)
andEntityComponent.OnDeinitialize(bool silent)
methods on the Unity side. It is advised to resolve this situation by fading or scaling effects make their disappearance less noticeable until the entity is has been verified. Unknown managed type referenced: [quantum.code] Quantum.XYZController
: this error is caused by an existing serialized instance of typeQuantum.XYZController
in a prefab or scene game object. However, the script is now missing; this is usually tied to having removed or renamed the script. To resolve this issue, find the object / serialized file which triggered the error and manually remove the serialized data from it - N.B.: It has to be done from outside Unity as it does not include any tooling to identify and resolve this issue. Alternatively, temporarily adding the missing class back might allow to re-edit the necessary objects before re-baking them.