Materialization
Introduction
The process of creating an entity or component instance from a Component Prototype
or Entity Prototype
is called Materialization .
The materialization of scene prototypes baked into the map asset follow the same rules and execution flow as the materialization of code created instances using the Frame.Create
API.
Prototype vs Instance
The component instances and entity instances are part of the game state; in other words they can be manipulated at runtime. Components declared in the DSL are used to generate their corresponding Component Prototypes
. The code generated prototypes follow the naming convention MyComponent_Prototype
.
Component Prototypes
and Entity Prototypes
are both assets; this means they are not part of the game state, immutable at runtime and have to be identical for all clients at all time. Each Component Prototype
has a ComponentPrototypeRef
which can be used to find it the corresponding asset using the Frame.FindPrototype<MyComponentName_Prototype>(MyComponentPrototypeRef)
.
Component Prototypes
It is possible to extend a Component Prototype
to include data which may not be directly used in materialization. This allows, for example, to have shared data between instances of a particular component or exclude read-only data from the frame to keep the game state slim.
Code generated Component Prototypes
are partial classes which can be easily extended:
- Create a C# file called
MyComponentName_Prototype.cs
; - Place the body of the script into the
Quantum.Prototypes
namespace; - ( Optional ) Add
using Quantum.Inspector;
to have access to the inspector attributes presented in theAttributes
section of theManual \ ECS
page.
It is then possible to add extra data to the Component Prototype
asset and implement the partial MaterializeUser()
method to add custom materialization logic.
Example
The following example presents the materialization of the Vehicle
component as found in the Arcade Racing Template.
The Vehicle
component holds mainly dynamic values computed at runtime. Since these cannot be initialized, the component definition in the DSL uses the ExcludeFromPrototype
attribute on those parameters to exclude them from the Vehicle_Prototype
asset designers can manipulate in the Unity editor. The Nitro
parameter is only part that can be edited to allow designers to decide with how much nitro a specific Vehicle
is initialized.
C#
component Vehicle
{
[ExcludeFromPrototype]
ComponentPrototypeRef Prototype;
[ExcludeFromPrototype]
Byte Flags;
[ExcludeFromPrototype]
FP Speed;
[ExcludeFromPrototype]
FP ForwardSpeed;
[ExcludeFromPrototype]
FPVector3 EngineForce;
[ExcludeFromPrototype]
FP WheelTraction;
[ExcludeFromPrototype]
FPVector3 AvgNormal;
[ExcludeFromPrototype]
array<Wheel>[4] Wheels;
FP Nitro;
}
The Vehicle_Prototype
asset is extended to provide designers with customizable read-only parameters. The Vehicle_Prototype
asset can thus hold shared values for all instances of a specific vehicle entity prototype "type". The Prototype
parameter in the Vehicle
component is of type ComponentPrototypeRef
which is the component specific equivalent to AssetRef
. To populate it, the partial MaterializeUser()
method is used to assign the reference of the Vehicle_Prototype
.
C#
using Photon.Deterministic;
using Quantum.Inspector;
using System;
namespace Quantum.Prototypes
{
public unsafe partial class Vehicle_Prototype
{
// PUBLIC METHODS
[Header("Engine")]
public FP EngineForwardForce = 130;
public FP EngineBackwardForce = 120;
public FPVector3 EngineForcePosition;
public FP ApproximateMaxSpeed = 20;
[Header("Hand Brake")]
public FP HandBrakeStrength = 10;
public FP HandBrakeTractionMultiplier = 1;
[Header("Resistances")]
public FP AirResistance = FP._0_02;
public FP RollingResistance = FP._0_10 * 6;
public FP DownForceFactor = 0;
public FP TractionGripMultiplier = 10;
public FP AirTractionDecreaseSpeed = FP._0_50;
[Header("Axles")]
public AxleSetup FrontAxle = new AxleSetup();
public AxleSetup RearAxle = new AxleSetup();
[Header("Nitro")]
public FP MaxNitro = 100;
public FP NitroForceMultiplier = 2;
// PARTIAL METHODS
partial void MaterializeUser(Frame frame, ref Vehicle result, in PrototypeMaterializationContext context)
{
result.Prototype = context.ComponentPrototypeRef;
}
[Serializable]
public class AxleSetup
{
public FPVector3 PositionOffset;
public FP Width = 1;
public FP SpringForce = 120;
public FP DampingForce = 175;
public FP SuspensionLength = FP._0_10 * 6;
public FP SuspensionOffset = -FP._0_25;
}
}
}
The parameters in the Vehicle_Prototype
hold values necessary to compute the dynamic values found in the component instance which impact the behaviour of the entity to which the Vehicle
component is attached. For example, when a player picks up additional Nitro
, the value held in the Vehicle
component is clamped to the MaxNitro
value found in the Vehicle_Prototype
. This enforces the limits under penality of desynchronization and keeps the game state slim.
C#
namespace Quantum
{
public unsafe partial struct Vehicle
{
public void AddNitro(Frame frame, EntityRef entity, FP amount)
{
var prototype = frame.FindPrototype<Vehicle_Prototype>(Prototype);
Nitro = FPMath.Clamp(Nitro + amount, 0, prototype.MaxNitro);
}
}
}
Materialization Order
Every Entity Prototype
, including the scene prototypes, the materialization executes the following steps in order:
- An empty entity is created.
- For each
Component Prototype
contained in theEntity Prototype
:- the component instance is created on the stack;
- the
Component Prototype
is materialized into the component instance; - ( Optional )
MaterializeUser()
is called; and, - the component is added to the entity which triggers the
ISignalOnComponentAdded<MyComponent>
signal.
ISignalOnEntityPrototypeMaterialized
is invoked for each materialized entity.- Load Map / Scene: the signal is invoked for all entity &
Entity Prototype
pair after all scene prototypes have been materialized. - Created with
Frame.Create()
: the signal is invoked immediately after the prototype has been materialized.
- Load Map / Scene: the signal is invoked for all entity &
The Component Prototype
materialization step materializes default components in a predetermined order.
C#
Transform2D
Transform3D
Transform2DVertical
PhysicsCollider2D
PhysicsBody2D
PhysicsCollider3D
PhysicsBody3D
PhysicsJoints2D
PhysicsJoints3D
PhysicsCallbacks2D
PhysicsCallbacks3D
CharacterController2D
CharacterController3D
NavMeshPathfinder
NavMeshSteeringAgent
NavMeshAvoidanceAgent
NavMeshAvoidanceObstacle
View
MapEntityLink
Once all default components have been materialized, the user defined components are materialized in alphabetically order.
C#
MyComponentAA
MyComponentBB
MyComponentCC
...
Back to top