Entity View
Introduction
QuantumEntityViews
are linked to Entities
via the Quantum View
component. It contains an AssetRef
to the GameObject which represents the view and should be instantiated for a specific entity. When configuring an Entity Prototype prefab or scene object with a QuantumEntityView
in Unity, the Quantum View
component is automatically filled into the prototype.
The QuantumEntityViewUpdater
is responsible for handling the view side of all entities in Unity such as destroying, creating and updating the view game objects of associated entities, based on data from the simulation. The QuantumEntityViewUpdater
script needs to be added to all scenes that contain a QuantumMap.
QuantumEntityViewComponents
can be used to add view related features to individual EntityViews. See the Entity View Component section.
Pooling
By default, the QuantumEntityViewUpdater
creates new instances of the QuantumEntityView
prefabs whenever an entity gets created and destroys the view GameObjects respectively.
To enable pooling of entity views add the QuantumEntityViewPool
script to the QuantumEntityViewUpdater
game object. The pool works seamlessly with the EVU and can be replaced by a custom implementation by using the IQuantumEntityViewPool
interface.
HidePooledObjectsInHierarchy | Toggle on to set HideFlags.HideInHierarchy on pooled objects |
ResetGameObjectScale | Toggle on to reset the local scale of objects to one |
Precache Items | Object will be instantiated during Awake() and made available in the pool |
EntityViews that subscribe to Quantum callbacks or events must make sure to either:
- Unsubscribe before the game objects are returned to the pool
- Must enable the
onlyIfActiveAndEnabled
parameter as shown below
C#
QuantumEvent.Subscribe<EventPlayerKilled>(this, OnKilled, onlyIfActiveAndEnabled: true);
Bind Behaviour
The QuantumEntityView
script in Unity has a property called Bind Behaviour
which can be set to either:
Non Verified
: the view Game Object can be created in Predicted framesVerified
: the view Game Object can only be created in Verified frames
Using Non Verified
is usually better for the views of entities which are instantiated in high frequency and/or they need to show up as quickly as possible on the player screen due to gameplay reaction time mechanics and such. For example, creating projectiles in a fast paced shooting game should be done using this alternative.
Using Verified
on the other hand is mostly useful for views of entities which does not need to show up immediately and can afford the small delay of waiting for a Verified frame. This can be useful to avoid creating/destructing view objects during mispredictions. A good example of when to use this is for the creation of playable character entities.
Manual Disposal
If the Manual Disposal
property is toggled on the destruction methods in the QuantumEntityViewUpdater
are skipped. This allows for manual destruction using the OnEntityDestroyed
callback of the QuantumEntityView
or to destroy them via custom destroy events.
View Flags
The view flags configure smaller details and allows performance tweaks on the entity views.
DisableUpdateView | QuantumEntityView.UpdateView() and QuantumEntityView.LateUpdateView() are not processed and forwarded to entity view components. |
DisableUpdatePosition | Will completely disable updating the entity view positions. |
UseCachedTransform | Use cached transforms to improve the performance by not calling Transform properties. |
DisableEntityRefNaming | By default, the entity game object is named to resemble its EntityRef value. Set this flag to disable this behaviour. |
DisableSearchChildrenForEntityViewComponents | Disable searching the entity view game object children for entity view components. |
Prediction Error Correction
Fine tune the error correction settings for an entity view. Each parameter has a detailed description in the Unity inspector.
Events
Add Unity events to the creation (also from the pool) and destruction of entity views. It uses UnityEvent<QuantumGame>
Teleporting Entities
The QuantumEntityView
script interpolates the entity GameObject
visuals by default. This adjusts for the difference in simulation rate, render rate and for error correction in terms of mis-prediction.
When moving an entity to a distant location in a single frame (i.e "teleporting" it), even though the entity data in the simulation snaps to the target position, the view interpolation will lerp it between the start and end positions over a few frames.
It can be noticeable and the view game object could be seen moving very fast on the screen, which is not desired.
In order to prevent this from happening, when an entity's Position should change so much (usually when respawning an entity or moving if the game has teleport features), use transform->Teleport(frame, newPosition);
. This makes the Entity view component automatically apply non-lerped movement.
Finding Views
A very common use case is to find the view of a specific entity. Since the simulation side is not aware of QuantumEntityViews
, EntityRefs
must be passed to the view via events, or polled (read only) from the frames. The QuantumEnityViewUpdater
has a GetView(EntityRef)
function that can be used to find the view of a specific EntityRef
. The views are cached in a dictionary, so the lookup is very efficient.
Events and Update Order
The OnObservedGameUpdated
function on the QuantumEntityViewUpdater
, which is responsible for creating, destroying and updating EntityViews
, gets called before events get processed. This means that destroyed entities in an event might already had their views destroyed.
Custom Destroy Events
A common pattern is to destroy an entity but still wanting to execute an event with additional information about the destruction to the view. To prevent the QuantumEntityView
from getting destroyed before the event gets processed set its Manual Disposal
field to true.
This will keep the view alive instead of passing it into the QuantumEntityViewUpdater's
DestroyEntityViewInstance
function which by default destroys the GameObject.
With that the event handler can still find the view and execute the destroy event with the view present. The QuantumEntityView
needs to be cleaned up manually by destroying it or returning it to the object pool.
AutoFindMapData
AutoFindMapData
has to be enabled when using maps with QuantumEntityView
on them. If enabled the view will search for the corresponding MapData object and match map entities with their views. Disable this if you are not using maps with entities to allow for having scenes without a MapData
script present.
Customizations
The QuantumEntityViewUpdater
gives the following possibilities to customize.
Overriding Create
To get the GameObjects from a pool instead of having them instantiated, override the CreateEntityViewInstance
function. The function has a Quantum.EntityView
parameter indicating which view to spawn. The QuantumEntityView.AssetGuid
can be used as a key in a dictionary of pooled objects.
C#
protected override QuantumEntityView CreateEntityViewInstance(Quantum.EntityView asset, Vector3? position = null, Quaternion? rotation = null) {
Debug.Assert(asset.View != null);
// view pooling can also be customized by using IQuantumEntityViewPool
EntityView view = _myObjectPool.GetInstance(asset);
view.transform.position = position ?? default;
view.transform.rotation = rotation ?? Quaternion.identity;
return view;
}
The result of CreateEntityViewInstance()
gets assigned to the Entity in OnEntityViewInstantiated()
. This method is virtual as well and can be overridden but in most cases this is not necessary. When overriding it is important to keep the EntityRef assignment in place.
Overriding Destroy
To return views to the pool, instead of destroying them, override DestroyEntityViewInstance()
.
C#
protected virtual void DestroyEntityViewInstance(QuantumEntityView instance) {
_myObjectPool.ReturnInstance(instance);
}
Map Entities
For map entities, ActivateMapEntityInstance()
is responsible for activating the views and can be overridden for custom behavior if needed.
DisableMapEntityInstance()
gets called which by default disables the GameObject. This function can be overridden for custom behavior as well.