What's New In 1.2.2
EntityPrefabViewUpdater
You can configure an entity prefab (EntityPrefabRoot) with additional information that will control when the game object is instantiated and destroyed by the EntityPrefabViewUpdater. For the majority of your objects you want to create a prefab instances for entities when they appear in the Current frame. But sometimes entities should only appear when the server acknowledged them. Chose between:
EntityPrefabCreateBehaviour.NonVerified
EntityPrefabCreateBehaviour.Verified
The entity prefab destruction will now happen during the OnGameDestroyed()
callback and not delayed between the frame updates any more.
QuantumGame Refactoring
Why did we do this:
- Make multiple simulations possible without confusing singletons: QuantumGame.Instance, QuantumRunner.Current.
- Iterate over the QuantumGame callbacks and make them more easy to use.
- Make the recording API more intuitive and not tied to Unity.
What did we do:
- Moved the QuantumGame class away from Unity to the quantum.system project.
- Made QuantumRunner the hub for all Quantum objects: runner, session and game. The runner has and updates the session which controls the game. Because we want to support multiple game simulation at the same time their runners are accessible via
QuantumRunner.ActiveRunners
. Although most of the time you will only use the first and only one you can get byQuantumRunner.Default
.
C#
// old
QuantumRunner.Current
// new
QuantumRunner.Default
// old
QuantumGame.Instance
// new
QuantumRunner.Default.Game
// old
if (QuantumGame.Instance != null)
// new
if (QuantumRunner.Default != null)
- Moved the recording (replays, input, checksums, snapshots) tools to the QuantumGame class.
- Check out the
RuntimeConfig.RecordingFlags
to start the game with recording enabled. - This is the API to record and save recordings:
- The DB can now be saved offline without running the actual game.
- Check out the
C#
public class QuantumGame : IDeterministicGame {
public InputProvider RecordedInputs { get; }
public ChecksumFile RecordedChecksums { get; }
public static void ExportDatabase(Stream stream, IReplaySerializer serializer, string folderpath, int navmeshSerializationBuffer);
public static void ExportRecordedChecksums(QuantumGame game, Stream stream, IReplaySerializer serializer);
public static void ExportRecordedReplay(QuantumGame game, Stream stream, IReplaySerializer serializer);
public ReplayFile GetRecordedReplay();
public Frame GetRecordedSnapshot(int frame);
public void StartRecordingChecksums();
public void StartRecordingInput();
public void StartRecordingSnapshots(float bufferSizeSec, int snapshotFrequencyPerSec);
public void StartVerifyingChecksums(ChecksumFile checksums);
}
- Added a class that contains all relevant callbacks that QuantumGame and DeterministicSession forward to the application:
QuantumGameCallbacks
. It is created when starting the game or can be passed as a parameter ofQuantumRunner.StartParameters
.- Unity related code that was running in QuantumGame (input, map loading) now is directly attached to the callbacks. Checkout the following files:
QuantumGameCallback_LocalInput.cs
QuantumGameCallback_MapLoading.cs
QuantumGameCallback_UnityCallbacks.cs
QuantumGameCallback_DebugDraw.cs
- You can continue to use the MonoBehaviour bound QuantumCallbacks where we added new signatures and encourage you to switch to the callbacks with the QuantumGame argument.
C#
public abstract class QuantumCallbacks : MonoBehaviour {
// Example old callback
public virtual void OnGameStart() { }
// New and additional callback with game argument
public virtual void OnGameStart(QuantumGame game) { }
// Renamed from OnSnapshotLoaded to OnGameStartFromSnapshot
public virtual void OnGameStartFromSnapshot(QuantumGame game, int frameNumber) { }
// Renamed from OnMapChangeBegin to OnUnitySceneLoadBegin
public virtual void OnUnitySceneLoadBegin(QuantumGame game) { }
// Renamed from OnMapChangeDone to OnUnitySceneLoadDone
public virtual void OnUnitySceneLoadDone(QuantumGame game) { }
// New
public virtual void OnGameDestroyed(QuantumGame game) { }
// New
public virtual void OnSimulateFinished(QuantumGame game, Frame frame) { }
}
- Or directly register to the new callbacks.
C#
public override void OnGameStart(QuantumGame game) {
game.Callbacks.OnUpdateView += OnUpdateView;
}
Event Callbacks With Game Argument
Quantum events have an additional OnRaised Action called OnRaisedFromGame. It introduces the parameter IDeterministicGame. This way the receiving end will know from which game the event originated from. Again removing QuantumGame.Instance usages. And also giving you the information that the event was spawned by an instant replay.
C#
public abstract class EventPlayerKilled : EventBase {
public static Action<EventPlayerKilled> OnRaised;
public static Action<EventPlayerKilled, IDeterministicGame> OnRaisedFromGame;
}
C#
void Start() {
Quantum.EventPlayerKilled.OnRaisedFromGame += PlayerKilledFromGame;
}
void PlayerKilledFromGame(EventPlayerKilled obj, IDeterministicGame game) {
Debug.Log("Game Mode: " + ((QuantumGame)game).GameMode);
}
Physics
The following changes apply to 2D and 3D dynamics.


Exposing Solver Parameters (Green)
We exposed a the following values to down-tune the jitter of dynamic bodies.
Penetration Allowance
refers to how much one object may sink into another one so they don't get pushed out completely and cause more jitter.Penetration Correction
refers to the percentage of how much the separation of two bodies is forced in one physics loop.Min Linear Integration
will hard clamp the the linear velocity and helps to reduce jitter. The default value is zero and it should only be used if you still have problems with getting bodies to rest.
Resting Bodies (Yellow)
The Resting or Sleeping feature yields two benefits:
- It frees up performance, because sleeping bodies will not check their surroundings on their own and
- deals with small remaining jitter in bodies.
If you have dynamic physics objects other then your characters and NPCs you normally want to activate this feature. You can deactivate it per object by assigning different PhysicsMaterials
to them (see the AllowSleeping
field on the DefaultPhysicsMaterial
below). FYI: The default material will have that option unchecked.
If your dynamic objects stack a lot you also want to activate UseIslandsForSleepDetection
. This will find all connecting bodies update them and put them to sleep in one step while otherwise the objects would wake up each other all the time.
LinearSleepTolerance
refers to the distance an object moves and AngularSleepToleranceInRad
to it's rotation speed. If the objects velocities will be below those thresholds for SleepTimeSec
it would be marked as sleeping.
You can test the feature by looking at the gizmos of the Quantum entities that have dynamic bodies. They will become purple when the body goes to sleep.
Known Issues
- If you have a sleeping body resting on top of another one and and that one starts to move downwards or disappear for example, the body on top will not wake up. For this to work we would need to save much more data on the frame state, which we decided against at this point. You can work around this situation by calling
DynamicBody.WakeUp()
on bodies received during a collision callback of the "platform" every frame (which resets the sleep timer as well) or useDynamicScene.WakeUpBodies()
to wake up all bodies in an area.
Multi-Threading (Pink)
Activating IsMultithreaded will do the following:
- Parallelize the narrow phase
- Parallelize the solver, if AllowSleeping and UseIslandsForSleepDetection is active
We measured a performance gain of 2.5x to 10x depending on the load of your dynamics. If you have very few dynamic objects then activating this will create overhead. Also be aware, that the second optimization part only yields a performance gain if islands (a graph of touching bodies) can be created.
Instant Replay Or Killcam
Rewind a couple seconds, playback a recorded simulation and resume the original game.
The feature is available in source code in your Unity project. Look for QuantumInstantReplay.cs
and QuantumInstantReplayDemo.cs
. The latter just handles the start and stop logic you can see in the Unity inspector. You can check out the functionality of the instant replay class and use it in your game or use it as blueprint for your own custom logic. It should explain what and why things are done to make instant replays using simultaneous simulations work.
The QuantumGame class will take snapshots in regular intervals of the game over the last n seconds by coping the verified frame into a ring buffer. When we trigger the replay the QuantumInstantReplay class will find the best snapshot to start from, then simulate ticks until we hit the desired rewind time. With this snapshot a new simulation and new QuantumRunner is started that runs for the time being just like a normal session.
Note that the live simulation is still ticked normally by it's own runner. It will also send data to the server because we can't stop the game for other player of course. So we have two runners and two games at the same time.
QuantumGame.Instance and QuantumRunner.Current will continue to point to the live game.
Known Issues
- There is a bug when using this with UseBackgroundThread enabled (Map has shared data) which we want to fix soon.
- Make sure the order of Quantum systems initialized for your game is correct: first PhysicsSystemPre then NavMeshAgentSystem (see below). There still is a dependency between the system we want to get rid of in the future.
C#
public static class SystemSetup {
public static SystemBase[] CreateSystems(RuntimeConfig gameConfig, SimulationConfig simulationConfig) {
return new SystemBase[] {
// pre-defined core systems
new Core.PhysicsSystemPre(),
new Core.NavMeshAgentSystem(),
...
Test The Feature
- Make sure you start the game with recording input (
RuntimeConfig.RecordingFlags
). - Attach the QuantumInstantReplayDemo script to your game scene. Can also be done during runtime.
- Run the game and trigger the debug buttons in the Unity inspector to start and stop an instant replays.

Buffer Size Sec
: How much history is recorded in seconds.Snapshot Frequency Per Second
: How many snapshots are done per second. Tuning this can reduce the number of frames we need to "skip" before getting to the desired frame to start from.Replay Length Sec
: How many seconds to go back in time when hitting Start.