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
LocalTransformcomponent. - 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
LocalTransformcomponent. You can copy the built-inLocalTransform.csfile 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.csfile, and rename it. - Change the properties and methods to suit your needs. See the following example of a custom
LocalTransform2Dcomponent:
// 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
Positionfield from afloat3to afloat2. This is because in the 2D sample, entities only move along the XY plane. - Reduces the
Rotationfield to afloatthat represents the number of degrees of rotation around the z-axis. The built-in transform system'sRotationproperty is a quaternion that represents a rotation in 3D space. - Removed all methods apart from
ToMatrixandToString. TheToMatrixmethod 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
LocalTransformcomponent, with a different name. - Has a
LocalToWorldcomponent - If the entity has a parent entity, then it must have a
Parentcomponent 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.csfile into your assets folder and then edit the contents. To do this, go to Packages > Entities > Unity.Transforms in your project, copy theLocalToWorldSystem.csfile, and rename it. - Replace all instances of the
LocalTransformcomponent with the name of your custom transform component (LocalTransform2Din 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.