Custom transforms
You can customize the built-in transform system to address the specific transform functionality needs of your project. This section explains how to create a custom transform system, and uses the 2D custom transform system as a concrete example of one.
Write groups
Write groups enable you to override the built-in transform system with your own transforms. The built-in transform system uses write groups internally and you can configure them to make the built-in transform system ignore entities that you want to process with your custom transform system.
More precisely, write groups exclude specific entities from queries. These queries are passed to the jobs that the built-in transform system uses. You can use write groups on certain components to exclude entities with those components from being processed by the jobs of the built-in transform system, and those entities can instead be processed by your own transform systems. For more information, refer to the documentation on write groups.
Create a custom transform system
The following steps outline how to create a custom transform system:
- Substitute the
LocalTransform
component. - Create an authoring component to receive your custom transforms.
- Replace the
LocalToWorldSystem
.
Substitute the LocalTransform component
The built-in transform system adds a LocalTransform
component to each entity by default. It stores the data that represents an entity's position, rotation, and scale. There are also a variety of static helper methods defined on this component.
To create your own custom transform system, you have to substitute the LocalTransform
component with your own.
- Create a .cs file that defines a substitute for the built-in
LocalTransform
component. You can copy the built-inLocalTransform.cs
file from the Entities package into your assets folder and then edit the contents. To do this, go to Packages > Entities > Unity.Transforms in your project, copy theLocalTransform.cs
file, and rename it. - Change the properties and methods to suit your needs. See the following example of a custom
LocalTransform2D
component:
// By including LocalTransform2D in the LocalToWorld write group, entities
// with LocalTransform2D are not processed by the standard transform system.
[WriteGroup(typeof(LocalToWorld))]
public struct LocalTransform2D : IComponentData
{
public float2 Position;
public float Scale;
public float Rotation;
public override string ToString()
{
return $"Position={Position.ToString()} Rotation={Rotation.ToString()} Scale={Scale.ToString(CultureInfo.InvariantCulture)}";
}
public float4x4 ToMatrix()
{
quaternion rotation = quaternion.RotateZ(math.radians(Rotation));
return float4x4.TRS(new float3(Position.xy, 0f), rotation, Scale);
}
}
The above example modifies the built-in LocalTransform
in the following ways:
- Adds the
[WriteGroup(typeof(LocalToWorld))]
attribute. - Reduces the
Position
field from afloat3
to afloat2
. This is because in the 2D sample, entities only move along the XY plane. - Reduces the
Rotation
field to afloat
that represents the number of degrees of rotation around the z-axis. The built-in transform system'sRotation
property is a quaternion that represents a rotation in 3D space. - Removed all methods apart from
ToMatrix
andToString
. TheToMatrix
method has been modified to work in 2D. The other methods aren't needed for the custom 2D transform system.
Note
LocalTransform2D
is in the global namespace. In the above linked sample project it's in a sub-namespace to ensure that it doesn't interfere with the other samples in the same project. Both options work as long as all the files of the custom transform system are within the same namespace.
Create an authoring component
Each entity that your custom transform system needs to process must fulfill the following criteria:
- Has a custom replacement for the
LocalTransform
component, with a different name. - Has a
LocalToWorld
component - If the entity has a parent entity, then it must have a
Parent
component that points to it.
To meet this criteria, add an authoring component to each entity, and use transform usage flags to prevent the entity from receiving any components from the built-in transform system:
public class Transform2DAuthoring : MonoBehaviour
{
class Baker : Baker<Transform2DAuthoring>
{
public override void Bake(Transform2DAuthoring authoring)
{
// Ensure that no standard transform components are added.
var entity = GetEntity(TransformUsageFlags.ManualOverride);
AddComponent(entity, new LocalTransform2D
{
Scale = 1
});
AddComponent(entity, new LocalToWorld
{
Value = float4x4.Scale(1)
});
var parentGO = authoring.transform.parent;
if (parentGO != null)
{
AddComponent(entity, new Parent
{
Value = GetEntity(parentGO, TransformUsageFlags.None)
});
}
}
}
}
The above example adds the custom LocalTransform2D
component and the built-in LocalToWorld
component to the authoring component. If applicable, it also adds a Parent
component that points to the entity's parent entity.
Replace the LocalToWorldSystem
The built-in LocalToWorldSystem
computes the LocalToWorld
matrices of root and child entities in the two corresponding jobs ComputeRootLocalToWorldJob
and ComputeChildLocalToWorldJob
. You need to replace this system with your own transform system.
- Copy the built-in
LocalToWorldSystem.cs
file into your assets folder and then edit the contents. To do this, go to Packages > Entities > Unity.Transforms in your project, copy theLocalToWorldSystem.cs
file, and rename it. - Replace all instances of the
LocalTransform
component with the name of your custom transform component (LocalTransform2D
in the example). - Remove the
WithOptions(EntityQueryOptions.FilterWriteGroup);
lines from the queries. If you don't remove these lines, your system excludes the corresponding entities like the built-in transform system does.
Note
LocalToWorldSystem
uses unsafe native code so, to avoid errors, enable the Allow unsafe code property in your project. To enable this property, go to Edit > Project Settings > Player > Other Settings and select Allow unsafe code.