Transforms comparison
Many of the transform operations available in the UnityEngine.Transform class have equivalents in the Entities API, with differences in syntax and, in some cases, in approach.
The main-thread code examples assume they run inside the OnUpdate(ref SystemState state) method of ISystem. Some code blocks include the state parameter without using it to match the OnUpdate method signature. For more information, refer to Using transforms.
Note
The main-thread examples show individual operations on arbitrary entities. In performance-sensitive code, avoid calling several random-access APIs, such as SystemAPI.GetComponent<T>, as separate helper methods. When you process many entities, prefer a query or job that iterates transform components directly, such as an IJobEntity with an Execute(in LocalToWorld localToWorld) parameter, so Unity can access the data by chunk. If you need several values from one arbitrary entity, get the component once and reuse the local value. For example, fetch LocalToWorld once, then derive the entity's forward, right, and up vectors from that value.
Many sections include a multithreaded implementation. Use these examples when you need to run transform logic across many entities, or when you need to access component data from a job.
For a multithreaded implementation, choose the access pattern that matches the data the job needs:
- To read or write a component on the entity that the job processes, use a parameter of the
Executemethod. Declare the parameter with theinkeyword for read-only access (for example,in LocalToWorld), or with therefkeyword for write access (for example,ref LocalTransform). - To read a component on a different entity, or to read an optional component on the entity that the job processes, declare a read-only
ComponentLookup<T>field on the job struct. - To read from an optional dynamic buffer such as the
Childbuffer, declare a read-onlyBufferLookup<T>field on the job struct. - To add, remove, or change a component from a job, queue the change on an
EntityCommandBuffer.ParallelWriterinstance.
The world-space code examples read from the LocalToWorld component. Its matrix can be stale or include graphical smoothing offsets while SimulationSystemGroup is running. For details and for the TransformHelpers.ComputeWorldTransformMatrix alternative, refer to the LocalToWorld component section.
The examples assume entities have uniform scale. The Scale field on LocalTransform is a single scalar. The PostTransformMatrix component stores non-uniform scale, which the examples below ignore. For the non-uniform case, refer to the PostTransformMatrix component section.
Many of the methods on the TransformHelpers class are declared as C# extension methods on the float4x4 struct. The examples on this page call them as instance methods on the matrix value. For example, localToWorld.Value.Translation() is equivalent to TransformHelpers.Translation(localToWorld.Value).
The page also mentions UnityEngine transform properties and methods that have no equivalent in Entities API.
This page contains the following sections:
UnityEngine transform property equivalents
The following sections show how to get or set common UnityEngine.Transform properties with Entities components and transform helper methods. Most property getters read either LocalTransform for local-space data or LocalToWorld for world-space data.
This section covers the following properties:
childCountforwardlocalPositionlocalRotationlocalScalelocalToWorldMatrixlossyScaleparentpositionrightrootrotationupworldToLocalMatrix
UnityEngine property: childCount
With the Entities API, to achieve an outcome similar to the Transform.childCount property, use SystemAPI.HasBuffer and SystemAPI.GetBuffer. Entities without children don't have a Child buffer, so check for it first to match Transform.childCount returning 0 for childless transforms:
Main-thread implementation:
int ChildCount(ref SystemState state, Entity e)
{
// Entities without children don't have a Child buffer, so a check is
// needed to match Transform.childCount returning 0 in that case.
if (SystemAPI.HasBuffer<Child>(e))
return SystemAPI.GetBuffer<Child>(e).Length;
return 0;
}
SystemAPI.GetBuffer and SystemAPI.HasBuffer can only run on the main thread, so you can't use them inside jobs. To access the child count from a job, use a read-only BufferLookup<Child>:
// Burst-compiled parallel job that reads the Child buffer length of the
// entity that the job processes. The Child buffer is optional, so use a
// BufferLookup field with TryGetBuffer to match Transform.childCount
// returning 0 for childless entities.
[BurstCompile]
public partial struct ChildCountJob : IJobEntity
{
[ReadOnly] public BufferLookup<Child> ChildLookup;
void Execute(Entity entity, in ExampleTag tag)
{
int childCount = ChildLookup.TryGetBuffer(entity, out var children)
? children.Length
: 0;
}
}
UnityEngine property: forward
With the Entities API, to achieve an outcome similar to the Transform.forward property, use the Mathematics package normalize function with the LocalToWorld.Forward vector. You can omit the call to the normalize method if every entity in the transform hierarchy has Scale values set to 1:
Main-thread implementation:
float3 Forward(ref SystemState state, Entity e)
{
return math.normalize(SystemAPI.GetComponent<LocalToWorld>(e).Forward);
}
Multithreaded implementation:
For a multithreaded version, get the LocalToWorld component as an Execute method parameter in a job, as shown in the example for reading LocalToWorld.
UnityEngine property: localPosition
With the Entities API, to achieve an outcome similar to the Transform.localPosition property, use LocalTransform.Position:
Main-thread implementation:
float3 LocalPosition(ref SystemState state, Entity e)
{
return SystemAPI.GetComponent<LocalTransform>(e).Position;
}
SystemAPI.GetComponent can only run on the main thread, so you can't use it inside jobs. To access LocalTransform.Position from a job, get the LocalTransform component as an Execute method parameter:
// Burst-compiled parallel job that reads the entity's LocalTransform
// component as an Execute parameter.
[BurstCompile]
public partial struct LocalPositionJob : IJobEntity
{
void Execute(in LocalTransform transform, in ExampleTag tag)
{
float3 localPosition = transform.Position;
}
}
Setting the localPosition property:
To set the local position, get the current LocalTransform component, then assign it a new value returned by the WithPosition method. The WithPosition method returns a copy that preserves the original Scale and Rotation fields, matching how the Transform.localPosition property preserves local rotation and local scale:
void SetLocalPosition(ref SystemState state, Entity e, float3 localPosition)
{
// WithPosition preserves the existing Scale and Rotation.
LocalTransform transform = SystemAPI.GetComponent<LocalTransform>(e);
SystemAPI.SetComponent(e, transform.WithPosition(localPosition));
}
For a multithreaded version, modify the LocalTransform component as an Execute method parameter in a job, as shown in the example for modifying LocalTransform.
UnityEngine property: localRotation
With the Entities API, to achieve an outcome similar to the Transform.localRotation property, use LocalTransform.Rotation:
Main-thread implementation:
quaternion LocalRotation(ref SystemState state, Entity e)
{
return SystemAPI.GetComponent<LocalTransform>(e).Rotation;
}
Multithreaded implementation:
For a multithreaded version, get the LocalTransform component as an Execute method parameter in a job, as shown in the example for reading LocalTransform.
Setting the localRotation property:
To set the local rotation, get the current LocalTransform component, then assign it a new value returned by the WithRotation method. The WithRotation method returns a copy that preserves the original Position and Scale fields, matching how the Transform.localRotation property preserves local position and local scale:
void SetLocalRotation(ref SystemState state, Entity e, quaternion localRotation)
{
// WithRotation preserves the existing Position and Scale.
LocalTransform transform = SystemAPI.GetComponent<LocalTransform>(e);
SystemAPI.SetComponent(e, transform.WithRotation(localRotation));
}
For a multithreaded version, modify the LocalTransform component as an Execute method parameter in a job, as shown in the example for modifying LocalTransform.
UnityEngine property: localScale
With the Entities API, to achieve an outcome similar to the Transform.localScale property, use LocalTransform.Scale. For the non-uniform case, refer to the PostTransformMatrix component section.
Main-thread implementation:
float3 LocalScale(ref SystemState state, Entity e)
{
// Examples assume uniform scale. LocalTransform.Scale is a single scalar.
return new float3(SystemAPI.GetComponent<LocalTransform>(e).Scale);
}
Multithreaded implementation:
For a multithreaded version, get the LocalTransform component as an Execute method parameter in a job, as shown in the example for reading LocalTransform.
Setting the localScale property:
To set the local scale, get the current LocalTransform component, then assign it a new value returned by the WithScale method. Because these examples assume uniform scale, the WithScale method takes a single scalar and returns a copy that preserves the original Position and Rotation fields. For the non-uniform case, refer to the PostTransformMatrix component section.
void SetLocalScale(ref SystemState state, Entity e, float scale)
{
// These examples assume uniform scale, so WithScale takes a single
// scalar and preserves the existing Position and Rotation.
LocalTransform transform = SystemAPI.GetComponent<LocalTransform>(e);
SystemAPI.SetComponent(e, transform.WithScale(scale));
}
For a multithreaded version, modify the LocalTransform component as an Execute method parameter in a job, as shown in the example for modifying LocalTransform.
UnityEngine property: localToWorldMatrix
With the Entities API, to achieve an outcome similar to the Transform.localToWorldMatrix property, use the LocalToWorld.Value matrix:
Main-thread implementation:
float4x4 LocalToWorldMatrix(ref SystemState state, Entity e)
{
return SystemAPI.GetComponent<LocalToWorld>(e).Value;
}
Multithreaded implementation:
For a multithreaded version, get the LocalToWorld component as an Execute method parameter in a job, as shown in the example for reading LocalToWorld.
UnityEngine property: lossyScale
With the Entities API, to achieve an outcome similar to the Transform.lossyScale property, get the LocalToWorld.Value matrix and call the Scale method on it to extract the per-axis scale values. Because the examples assume uniform scale, all three values are equal:
Main-thread implementation:
float3 LossyScale(ref SystemState state, Entity e)
{
return SystemAPI.GetComponent<LocalToWorld>(e).Value.Scale();
}
Multithreaded implementation:
For a multithreaded version, get the LocalToWorld component as an Execute method parameter in a job, as shown in the example for reading LocalToWorld.
UnityEngine property: parent
With the Entities API, to achieve an outcome similar to the Transform.parent property, use the SystemAPI.TryGetComponent method with the Parent component, and return Parent.Value when the component is present. Root entities don't have a Parent component, so return Entity.Null in that case to match Transform.parent returning null:
Main-thread implementation:
Entity Parent(ref SystemState state, Entity e)
{
// Root entities have no Parent component, so return Entity.Null to match
// Transform.parent returning null in that case.
return SystemAPI.TryGetComponent<Parent>(e, out var parent)
? parent.Value
: Entity.Null;
}
Inside a parallel job, use a read-only ComponentLookup<Parent>:
// Burst-compiled parallel job that reads the Parent component of the
// entity that the job processes. The Parent component is optional, so
// use a ComponentLookup field with TryGetComponent to match
// Transform.parent returning null for root entities.
[BurstCompile]
public partial struct ParentJob : IJobEntity
{
[ReadOnly] public ComponentLookup<Parent> ParentLookup;
void Execute(Entity entity, in ExampleTag tag)
{
Entity parent = ParentLookup.TryGetComponent(entity, out var p)
? p.Value
: Entity.Null;
}
}
UnityEngine property: position
With the Entities API, to achieve an outcome similar to the Transform.position property, use LocalToWorld.Position:
Main-thread implementation:
float3 Position(ref SystemState state, Entity e)
{
return SystemAPI.GetComponent<LocalToWorld>(e).Position;
}
SystemAPI.GetComponent can only run on the main thread, so you can't use it inside jobs. To access LocalToWorld.Position (or any other field of the LocalToWorld component) from a job, get the LocalToWorld component as an Execute method parameter:
// Burst-compiled parallel job that reads the entity's LocalToWorld
// component as an Execute parameter.
[BurstCompile]
public partial struct PositionJob : IJobEntity
{
void Execute(in LocalToWorld localToWorld, in ExampleTag tag)
{
float3 worldPosition = localToWorld.Position;
}
}
Setting the position property:
To set the world-space position, write to the entity's LocalTransform component. If the entity has a parent, convert the world-space position into the local space of the parent entity first using the InverseTransformPoint method on the parent entity's LocalToWorld matrix:
void SetPosition(ref SystemState state, Entity e, float3 worldPosition)
{
// If the entity has a parent, convert the world-space position into
// the local space of the parent entity before assigning it.
float3 localPosition = worldPosition;
if (SystemAPI.HasComponent<Parent>(e))
{
Entity parent = SystemAPI.GetComponent<Parent>(e).Value;
float4x4 parentL2W = SystemAPI.GetComponent<LocalToWorld>(parent).Value;
localPosition = parentL2W.InverseTransformPoint(worldPosition);
}
LocalTransform transform = SystemAPI.GetComponent<LocalTransform>(e);
SystemAPI.SetComponent(e, transform.WithPosition(localPosition));
}
For a multithreaded version, modify the LocalTransform component as an Execute method parameter in a job, as shown in the example for modifying LocalTransform. For the parent branch, also declare a read-only ComponentLookup<Parent> field and a read-only ComponentLookup<LocalToWorld> field on the job struct, as shown in the example for looking up Parent.
UnityEngine property: right
With the Entities API, to achieve an outcome similar to the Transform.right property, use the Mathematics package normalize function with the LocalToWorld.Right vector. You can omit the call to the normalize method if every entity in the transform hierarchy has Scale values set to 1:
Main-thread implementation:
float3 Right(ref SystemState state, Entity e)
{
return math.normalize(SystemAPI.GetComponent<LocalToWorld>(e).Right);
}
Multithreaded implementation:
For a multithreaded version, get the LocalToWorld component as an Execute method parameter in a job, as shown in the example for reading LocalToWorld.
UnityEngine property: root
With the Entities API, to achieve an outcome similar to the Transform.root property, use Parent.Value in a loop that follows the chain of parent references until it reaches an entity with no Parent component (a root entity). A root entity has no Parent to begin with, so the loop returns the starting entity unchanged, matching Transform.root returning the transform itself when called on a root:
Main-thread implementation:
Entity Root(ref SystemState state, Entity e)
{
while (SystemAPI.TryGetComponent<Parent>(e, out var parent))
e = parent.Value;
return e;
}
Multithreaded implementation:
For a multithreaded version, use a read-only ComponentLookup<Parent> in a job, as shown in the example for looking up Parent, looping with TryGetComponent until it returns false.
UnityEngine property: rotation
With the Entities API, to achieve an outcome similar to the Transform.rotation property, get the LocalToWorld.Value matrix and call the Rotation method on it:
Main-thread implementation:
quaternion Rotation(ref SystemState state, Entity e)
{
return SystemAPI.GetComponent<LocalToWorld>(e).Value.Rotation();
}
Multithreaded implementation:
For a multithreaded version, get the LocalToWorld component as an Execute method parameter in a job, as shown in the example for reading LocalToWorld.
Setting the rotation property:
To set the world-space rotation, write to the entity's LocalTransform component. If the entity has a parent, convert the world-space rotation into the local space of the parent entity first using the InverseTransformRotation method on the parent entity's LocalToWorld matrix:
void SetRotation(ref SystemState state, Entity e, quaternion worldRotation)
{
// If the entity has a parent, convert the world-space rotation into
// the local space of the parent entity before assigning it.
quaternion localRotation = worldRotation;
if (SystemAPI.HasComponent<Parent>(e))
{
Entity parent = SystemAPI.GetComponent<Parent>(e).Value;
float4x4 parentL2W = SystemAPI.GetComponent<LocalToWorld>(parent).Value;
localRotation = parentL2W.InverseTransformRotation(worldRotation);
}
LocalTransform transform = SystemAPI.GetComponent<LocalTransform>(e);
SystemAPI.SetComponent(e, transform.WithRotation(localRotation));
}
For a multithreaded version, modify the LocalTransform component as an Execute method parameter in a job, as shown in the example for modifying LocalTransform. For the parent branch, also declare a read-only ComponentLookup<Parent> field and a read-only ComponentLookup<LocalToWorld> field on the job struct, as shown in the example for looking up Parent.
UnityEngine property: up
With the Entities API, to achieve an outcome similar to the Transform.up property, use the Mathematics package normalize function with the LocalToWorld.Up vector. You can omit the call to the normalize method if every entity in the transform hierarchy has Scale values set to 1:
Main-thread implementation:
float3 Up(ref SystemState state, Entity e)
{
return math.normalize(SystemAPI.GetComponent<LocalToWorld>(e).Up);
}
Multithreaded implementation:
For a multithreaded version, get the LocalToWorld component as an Execute method parameter in a job, as shown in the example for reading LocalToWorld.
UnityEngine property: worldToLocalMatrix
With the Entities API, to achieve an outcome similar to the Transform.worldToLocalMatrix property, use the Mathematics package inverse function with the LocalToWorld.Value matrix:
Main-thread implementation:
float4x4 WorldToLocalMatrix(ref SystemState state, Entity e)
{
return math.inverse(SystemAPI.GetComponent<LocalToWorld>(e).Value);
}
Multithreaded implementation:
For a multithreaded version, get the LocalToWorld component as an Execute method parameter in a job, as shown in the example for reading LocalToWorld.
Properties with no equivalent
The following properties have no equivalent in the Entities API:
eulerAngleslocalEulerAngleshasChangedhierarchyCapacity. Not needed, since there is no limit to the number of children an entity can have.hierarchyCount
UnityEngine transform method equivalents
The following sections show how to reproduce common UnityEngine.Transform method behavior with Entities APIs. Most methods either read transform components, update the LocalTransform struct, or queue structural changes for parent-child relationships.
This section covers the following methods:
DetachChildrenGetChildGetLocalPositionAndRotationGetPositionAndRotationInverseTransformDirectionInverseTransformPointInverseTransformVectorIsChildOfLookAtRotateRotateAroundSetLocalPositionAndRotationSetParentSetPositionAndRotationTransformDirectionTransformPointTransformVectorTranslate
UnityEngine method: DetachChildren
With the Entities API, to achieve a behavior similar to the Transform.DetachChildren method, use the EntityManager.DetachChildren method. It removes the Parent component from each child entity and updates the Child buffer immediately. ParentSystem updates the PreviousParent component on its next update. The default value of the method's preserveWorldTransform parameter is true, which keeps each detached child entity at its current world position. Set the preserveWorldTransform parameter to false to leave each child entity's LocalTransform component unchanged:
Main-thread implementation:
void DetachChildren(ref SystemState state, Entity e)
{
// EntityManager.DetachChildren defaults to preserveWorldTransform: true.
state.EntityManager.DetachChildren(e);
}
Multithreaded implementation:
The EntityManager.DetachChildren method performs a structural change and can only run on the main thread. From a job, queue a RemoveComponent<Parent> call on an EntityCommandBuffer.ParallelWriter instance for each child entity, as shown in the example for changing Parent from a job. Once Unity executes the queued commands, ParentSystem updates the Child buffer and the PreviousParent component to reflect the detached child entities. This job-based approach produces the same outcome as calling EntityManager.DetachChildren on the main thread with preserveWorldTransform: false.
To preserve the world transform of each detached child entity from a job:
- Recompute the child entity's new
LocalTransformcomponent from its current world transform using theComputeWorldTransformMatrixmethod. This method computes the current transform instead of using theLocalToWorldmatrix, which Unity might not update until later in the frame. For more information, refer to theLocalToWorldcomponent section. - If the resulting scale isn't uniform, queue an
AddComponent<PostTransformMatrix>call on the command buffer, which attaches aPostTransformMatrixcomponent to the child entity. For details, refer to thePostTransformMatrixcomponent section.
UnityEngine method: GetChild
With the Entities API, to achieve an outcome similar to the Transform.GetChild method, use SystemAPI.HasBuffer and SystemAPI.GetBuffer:
Main-thread implementation:
Entity GetChild(ref SystemState state, Entity e, int index)
{
if (SystemAPI.HasBuffer<Child>(e))
return SystemAPI.GetBuffer<Child>(e)[index].Value;
return Entity.Null;
}
Multithreaded implementation:
For a multithreaded version, use a read-only BufferLookup<Child> in a job, as shown in the example for looking up Child.
UnityEngine method: GetLocalPositionAndRotation
With the Entities API, to achieve an outcome similar to the Transform.GetLocalPositionAndRotation method, use LocalTransform.Position and LocalTransform.Rotation:
Main-thread implementation:
void GetLocalPositionAndRotation(ref SystemState state,
Entity e, out float3 localPosition,
out quaternion localRotation)
{
LocalTransform transform =
SystemAPI.GetComponent<LocalTransform>(e);
localPosition = transform.Position;
localRotation = transform.Rotation;
}
Multithreaded implementation:
For a multithreaded version, get the LocalTransform component as an Execute method parameter in a job, as shown in the example for reading LocalTransform.
UnityEngine method: GetPositionAndRotation
With the Entities API, to achieve an outcome similar to the Transform.GetPositionAndRotation method, get the LocalToWorld.Value matrix and call the Translation and Rotation methods on it:
Main-thread implementation:
void GetPositionAndRotation(ref SystemState state, Entity e,
out float3 position, out quaternion rotation)
{
float4x4 l2w =
SystemAPI.GetComponent<LocalToWorld>(e).Value;
position = l2w.Translation();
rotation = l2w.Rotation();
}
Multithreaded implementation:
For a multithreaded version, get the LocalToWorld component as an Execute method parameter in a job, as shown in the example for reading LocalToWorld.
UnityEngine method: InverseTransformDirection
With the Entities API, to achieve an outcome similar to the Transform.InverseTransformDirection method, get the LocalToWorld.Value matrix and call the Rotation method on it to extract the entity's world-space rotation, then apply the inverse of that rotation to the direction vector with the Mathematics package mul and inverse functions. Transform.InverseTransformDirection ignores scale and preserves the length of the direction argument. Applying only the inverse rotation produces the same result regardless of the entity's scale, so no normalization or length compensation is needed:
Main-thread implementation:
float3 InverseTransformDirection(ref SystemState state,
Entity e, float3 direction)
{
// Transform.InverseTransformDirection ignores scale and preserves
// the length of the direction argument, so apply only the inverse
// of the rotation extracted from the LocalToWorld.Value matrix.
quaternion worldRotation =
SystemAPI.GetComponent<LocalToWorld>(e).Value.Rotation();
return math.mul(math.inverse(worldRotation), direction);
}
Multithreaded implementation:
For a multithreaded version, get the LocalToWorld component as an Execute method parameter in a job, as shown in the example for reading LocalToWorld.
UnityEngine method: InverseTransformPoint
With the Entities API, to achieve an outcome similar to the Transform.InverseTransformPoint method, get the entity's LocalToWorld.Value matrix and call the InverseTransformPoint method on it to apply the inverse of that matrix to the position vector:
Main-thread implementation:
float3 InverseTransformPoint(ref SystemState state, Entity e, float3 position)
{
return SystemAPI.GetComponent<LocalToWorld>(e).Value.InverseTransformPoint(position);
}
Multithreaded implementation:
For a multithreaded version, get the LocalToWorld component as an Execute method parameter in a job, as shown in the example for reading LocalToWorld.
UnityEngine method: InverseTransformVector
With the Entities API, to achieve an outcome similar to the Transform.InverseTransformVector method, get the entity's LocalToWorld.Value matrix and call the InverseTransformDirection method on it to apply the inverse of that matrix to the vector argument. Despite sharing a name with Transform.InverseTransformDirection, this method applies scale, so its behavior matches Transform.InverseTransformVector instead:
Main-thread implementation:
float3 InverseTransformVector(ref SystemState state, Entity e, float3 vector)
{
return SystemAPI.GetComponent<LocalToWorld>(e).Value.InverseTransformDirection(vector);
}
Multithreaded implementation:
For a multithreaded version, get the LocalToWorld component as an Execute method parameter in a job, as shown in the example for reading LocalToWorld.
UnityEngine method: IsChildOf
With the Entities API, to achieve an outcome similar to the Transform.IsChildOf method, use Parent.Value in a loop that follows the chain of parent references and compares each entity to the candidate, stopping at the first entity with no Parent component. The loop starts at the entity, so it returns true when the candidate is the entity itself, its parent entity, or any ancestor, matching the behavior of Transform.IsChildOf:
Main-thread implementation:
bool IsChildOf(ref SystemState state, Entity e, Entity parent)
{
// Transform.IsChildOf returns true when the candidate is the entity
// itself, its parent entity, or any ancestor. The loop follows the
// chain of parent references and compares each entity to the candidate.
while (true)
{
if (e == parent)
return true;
if (!SystemAPI.TryGetComponent<Parent>(e, out var parentComp))
return false;
e = parentComp.Value;
}
}
Multithreaded implementation:
For a multithreaded version, use a read-only ComponentLookup<Parent> in a job, as shown in the example for looking up Parent.
UnityEngine method: LookAt
With the Entities API, to achieve a behavior similar to the Transform.LookAt method, use the LookAtRotation method from TransformHelpers to compute the rotation in world space. If the entity has a parent, convert the world-space rotation into the local space of the parent entity:
Main-thread implementation:
void LookAt(ref SystemState state, Entity e, float3 target, float3 worldUp)
{
// Compute the rotation in world space.
float3 eyeWorld = SystemAPI.GetComponent<LocalToWorld>(e).Position;
quaternion worldRotation = TransformHelpers.LookAtRotation(eyeWorld, target, worldUp);
// If the entity has a parent, convert the rotation into the local
// space of the parent entity.
quaternion localRotation = worldRotation;
if (SystemAPI.HasComponent<Parent>(e))
{
Entity parent = SystemAPI.GetComponent<Parent>(e).Value;
float4x4 parentL2W = SystemAPI.GetComponent<LocalToWorld>(parent).Value;
localRotation = parentL2W.InverseTransformRotation(worldRotation);
}
LocalTransform transform = SystemAPI.GetComponent<LocalTransform>(e);
SystemAPI.SetComponent(e, transform.WithRotation(localRotation));
}
Multithreaded implementation:
For a multithreaded version, get the entity's LocalToWorld component as an Execute method parameter in a job, as shown in the example for reading LocalToWorld. Also modify the entity's LocalTransform component as another Execute method parameter, as shown in the example for modifying LocalTransform. To look up the parent entity and read its LocalToWorld component, declare a read-only ComponentLookup<Parent> field and a read-only ComponentLookup<LocalToWorld> field on the job struct, as shown in the example for looking up Parent.
UnityEngine method: Rotate
With the Entities API, to achieve a behavior similar to the Transform.Rotate method, the approach depends on the rotation space.
Rotations in the Entities transform system are always quaternions and angles are always in radians. Use quaternion.Euler from the Mathematics package to convert Euler angles into a quaternion, and math.radians to convert degrees into radians.
When Transform.Rotate is called with Space.Self as its relativeTo argument (the default), or when the entity has no parent, use LocalTransform.Rotate, which post-multiplies the rotation in local space:
Main-thread implementation:
// For Space.Self, or if the entity has no parent.
void RotateSelf(ref SystemState state, Entity e, quaternion rotation)
{
LocalTransform transform = SystemAPI.GetComponent<LocalTransform>(e);
SystemAPI.SetComponent(e, transform.Rotate(rotation));
}
When Transform.Rotate is called with Space.World as its relativeTo argument, and the entity might have a parent, convert the rotation into the local space of the parent entity first, then pre-multiply:
// For Space.World when the entity might have a parent.
void RotateWorld(ref SystemState state, Entity e, quaternion rotation)
{
// The `rotation` argument is in world space. If the entity has a
// parent, convert it into the local space of the parent entity
// before applying it.
quaternion localRotation = rotation;
if (SystemAPI.HasComponent<Parent>(e))
{
Entity parent = SystemAPI.GetComponent<Parent>(e).Value;
float4x4 parentL2W = SystemAPI.GetComponent<LocalToWorld>(parent).Value;
localRotation = parentL2W.InverseTransformRotation(rotation);
}
LocalTransform transform = SystemAPI.GetComponent<LocalTransform>(e);
// Apply localRotation by pre-multiplication, not with transform.Rotate
// (which applies in the entity's own local space, post-multiplied).
SystemAPI.SetComponent(e, transform.WithRotation(math.mul(localRotation, transform.Rotation)));
}
Multithreaded implementation:
For a multithreaded version, modify the LocalTransform component as an Execute method parameter in a job, as shown in the example for modifying LocalTransform. For the Space.World form, also declare a read-only ComponentLookup<Parent> field and a read-only ComponentLookup<LocalToWorld> field on the job struct, as shown in the example for looking up Parent.
UnityEngine method: RotateAround
With the Entities API, to achieve a behavior similar to the Transform.RotateAround method, convert the angleDegrees and axis arguments to the units the Mathematics package expects. If the entity has a parent, also convert the point and axis arguments into the local space of the parent entity.
The quaternion.AxisAngle method expects a unit-length axis and an angle in radians. Use math.normalize to make the axis vector unit-length, and use math.radians to convert the angleDegrees argument to radians.
If the entity has a parent, also convert the point and axis vectors into the local space of the parent entity. Call the InverseTransformPoint method on the parent entity's LocalToWorld matrix to apply the inverse of that matrix to the point vector. Call the InverseTransformDirection method on the same matrix to apply the same inverse to the axis vector, then renormalize the transformed axis vector because uniform scale changes its length:
Main-thread implementation:
void RotateAround(ref SystemState state, Entity e, float3 point,
float3 axis, float angleDegrees)
{
// quaternion.AxisAngle expects a unit-length axis and an angle in radians.
axis = math.normalize(axis);
float angleRadians = math.radians(angleDegrees);
// The `point` and `axis` arguments are in world space. If the
// entity has a parent, convert them into the local space of the
// parent entity before applying them.
float3 localPoint = point;
float3 localAxis = axis;
if (SystemAPI.HasComponent<Parent>(e))
{
Entity parent = SystemAPI.GetComponent<Parent>(e).Value;
float4x4 parentL2W = SystemAPI.GetComponent<LocalToWorld>(parent).Value;
localPoint = parentL2W.InverseTransformPoint(point);
// Direction transforms scale the axis under uniform scale, so
// renormalize to recover a unit-length axis for quaternion.AxisAngle.
localAxis = math.normalize(parentL2W.InverseTransformDirection(axis));
}
LocalTransform transform = SystemAPI.GetComponent<LocalTransform>(e);
quaternion q = quaternion.AxisAngle(localAxis, angleRadians);
transform.Position = localPoint + math.mul(q, transform.Position - localPoint);
transform.Rotation = math.mul(q, transform.Rotation);
SystemAPI.SetComponent(e, transform);
}
Multithreaded implementation:
For a multithreaded version, modify the LocalTransform component as an Execute method parameter in a job, as shown in the example for modifying LocalTransform. For the parent branch, also declare a read-only ComponentLookup<Parent> field and a read-only ComponentLookup<LocalToWorld> field on the job struct, as shown in the example for looking up Parent.
UnityEngine method: SetLocalPositionAndRotation
With the Entities API, to achieve a behavior similar to the Transform.SetLocalPositionAndRotation method, get the entity's current LocalTransform component, then write back a new value built with the WithPosition and WithRotation instance methods. Both methods return a copy that preserves the original Scale field. This matches the behavior of Transform.SetLocalPositionAndRotation, which leaves local scale untouched. Don't use the LocalTransform.FromPositionRotation factory method as a substitute, because it hard-codes Scale to 1 and would unintentionally reset the entity's scale:
Main-thread implementation:
void SetLocalPositionAndRotation(ref SystemState state, Entity e,
float3 localPosition, quaternion localRotation)
{
// WithPosition / WithRotation preserve the existing Scale.
// LocalTransform.FromPositionRotation would reset Scale to 1.
LocalTransform transform = SystemAPI.GetComponent<LocalTransform>(e);
SystemAPI.SetComponent(e,
transform.WithPosition(localPosition).WithRotation(localRotation));
}
Multithreaded implementation:
For a multithreaded version, modify the LocalTransform component as an Execute method parameter in a job and assign the result of WithPosition(...).WithRotation(...) to it, as shown in the example for modifying LocalTransform.
UnityEngine method: SetParent
With the Entities API, to achieve a behavior similar to the Transform.SetParent method, use EntityManager.SetParent. It adds, updates, or removes the Parent component (use Entity.Null as the new parent entity to remove it) and updates the Child buffer immediately. ParentSystem updates the PreviousParent component on its next update. The method's preserveWorldTransform parameter defaults to true, which keeps the entity's world position fixed (the equivalent of worldPositionStays: true). Set the preserveWorldTransform parameter to false to leave its LocalTransform component unchanged:
Main-thread implementation:
// preserveWorldTransform corresponds to Transform.SetParent's worldPositionStays
// argument. Use Entity.Null as the new parent entity to remove the Parent component.
void SetParent(ref SystemState state, Entity e, Entity parent, bool preserveWorldTransform)
{
state.EntityManager.SetParent(e, parent, preserveWorldTransform);
}
The EntityManager.SetParent method performs a structural change (adding, replacing, or removing the Parent component on an entity) and can only run on the main thread. From a job, queue the change on an EntityCommandBuffer.ParallelWriter instance instead. Once Unity executes the queued commands, ParentSystem updates the Child buffer on the new parent entity and the PreviousParent component on the child entity to reflect the change.
The job implementation below covers the basic cases:
- Attach: the entity has no
Parentcomponent yet. - Change parent: the entity already has a
Parentcomponent pointing at a different entity. - Detach: the new parent value is
Entity.Null, so the existingParentcomponent is removed.
To preserve the world transform of the entity when changing its parent from a job:
- Recompute the entity's new
LocalTransformcomponent using theComputeWorldTransformMatrixmethod. This method computes the current transform instead of using theLocalToWorldmatrix, which Unity might not update until later in the frame. For more information, refer to theLocalToWorldcomponent section. - If the resulting scale isn't uniform, queue an
AddComponent<PostTransformMatrix>call on the command buffer, which attaches aPostTransformMatrixcomponent to the entity. For details, refer to thePostTransformMatrixcomponent section.
// Structural changes can't run from a job. Queue the parent change on an
// EntityCommandBuffer.ParallelWriter. This example doesn't preserve the
// entity's world transform. To preserve it, recompute LocalTransform from
// the entity's current world transform before queuing the change.
[BurstCompile]
public partial struct SetParentJob : IJobEntity
{
public EntityCommandBuffer.ParallelWriter Ecb;
[ReadOnly] public ComponentLookup<Parent> ParentLookup;
void Execute(Entity entity, [ChunkIndexInQuery] int sortKey,
in NewParent newParent)
{
bool hasParent = ParentLookup.TryGetComponent(entity, out var p);
Entity currentParent = hasParent ? p.Value : Entity.Null;
// Match EntityManager.SetParent's no-op behavior: if the requested
// parent already equals the current one, do nothing.
if (currentParent == newParent.Value)
return;
if (newParent.Value == Entity.Null)
{
// Detach: hasParent is true here, otherwise the equality check
// above would have returned.
Ecb.RemoveComponent<Parent>(sortKey, entity);
}
else
{
// Attach (no Parent yet) or change parent (Parent already exists).
var parent = new Parent { Value = newParent.Value };
if (hasParent)
Ecb.SetComponent(sortKey, entity, parent);
else
Ecb.AddComponent(sortKey, entity, parent);
}
}
}
// Component holding the desired new parent entity for the SetParent job above.
public struct NewParent : IComponentData
{
public Entity Value;
}
UnityEngine method: SetPositionAndRotation
With the Entities API, to achieve a behavior similar to the Transform.SetPositionAndRotation method, the approach depends on whether the entity has a parent.
If the entity has no parent, its local space and world space coincide, so get the current LocalTransform component and write back a new value built with the WithPosition and WithRotation instance methods. Both methods preserve the original Scale field. This matches the behavior of Transform.SetPositionAndRotation, which leaves scale untouched. Don't use the LocalTransform.FromPositionRotation factory method as a substitute, because it hard-codes Scale to 1 and would unintentionally reset the entity's scale:
Main-thread implementation:
// If the entity has no parent.
void SetPositionAndRotationNoParent(ref SystemState state, Entity e,
float3 position, quaternion rotation)
{
// WithPosition / WithRotation preserve the existing Scale.
// LocalTransform.FromPositionRotation would reset Scale to 1.
LocalTransform transform = SystemAPI.GetComponent<LocalTransform>(e);
SystemAPI.SetComponent(e,
transform.WithPosition(position).WithRotation(rotation));
}
If the entity might have a parent, convert the world-space position and rotation arguments into the local space of the parent entity first:
// If the entity might have a parent.
void SetPositionAndRotation(ref SystemState state, Entity e,
float3 position, quaternion rotation)
{
// The `position` and `rotation` arguments are in world space. If
// the entity has a parent, convert them into the local space of
// the parent entity before assigning them.
float3 localPosition = position;
quaternion localRotation = rotation;
if (SystemAPI.HasComponent<Parent>(e))
{
Entity parent = SystemAPI.GetComponent<Parent>(e).Value;
float4x4 parentL2W = SystemAPI.GetComponent<LocalToWorld>(parent).Value;
localPosition = parentL2W.InverseTransformPoint(position);
localRotation = parentL2W.InverseTransformRotation(rotation);
}
// WithPosition / WithRotation preserve the existing Scale.
// LocalTransform.FromPositionRotation would reset Scale to 1.
LocalTransform transform = SystemAPI.GetComponent<LocalTransform>(e);
SystemAPI.SetComponent(e,
transform.WithPosition(localPosition).WithRotation(localRotation));
}
Multithreaded implementation:
For a multithreaded version, modify the LocalTransform component as an Execute method parameter in a job, as shown in the example for modifying LocalTransform. For the parent branch, also declare a read-only ComponentLookup<Parent> field and a read-only ComponentLookup<LocalToWorld> field on the job struct, as shown in the example for looking up Parent.
UnityEngine method: TransformDirection
With the Entities API, to achieve an outcome similar to the Transform.TransformDirection method, get the LocalToWorld.Value matrix and call the Rotation method on it to extract the entity's world-space rotation, then apply that rotation to the direction vector with the Mathematics package mul function. Transform.TransformDirection ignores scale and preserves the length of the direction argument. Applying only the rotation produces the same result regardless of the entity's scale, so no normalization or length compensation is needed:
Main-thread implementation:
float3 TransformDirection(ref SystemState state, Entity e, float3 direction)
{
// Transform.TransformDirection ignores scale and preserves the length
// of the direction argument, so apply only the rotation extracted
// from the LocalToWorld.Value matrix.
quaternion worldRotation =
SystemAPI.GetComponent<LocalToWorld>(e).Value.Rotation();
return math.mul(worldRotation, direction);
}
Multithreaded implementation:
For a multithreaded version, get the LocalToWorld component as an Execute method parameter in a job, as shown in the example for reading LocalToWorld.
UnityEngine method: TransformPoint
With the Entities API, to achieve an outcome similar to the Transform.TransformPoint method, get the entity's LocalToWorld.Value matrix and call the TransformPoint method on it to apply that matrix to the position vector:
Main-thread implementation:
float3 TransformPoint(ref SystemState state, Entity e, float3 position)
{
return SystemAPI.GetComponent<LocalToWorld>(e).Value.TransformPoint(position);
}
Multithreaded implementation:
For a multithreaded version, get the LocalToWorld component as an Execute method parameter in a job, as shown in the example for reading LocalToWorld.
UnityEngine method: TransformVector
With the Entities API, to achieve an outcome similar to the Transform.TransformVector method, get the entity's LocalToWorld.Value matrix and call the TransformDirection method on it to apply that matrix to the vector argument. Despite sharing a name with Transform.TransformDirection, this method applies scale, so its behavior matches Transform.TransformVector instead:
Main-thread implementation:
float3 TransformVector(ref SystemState state, Entity e, float3 vector)
{
return SystemAPI.GetComponent<LocalToWorld>(e).Value.TransformDirection(vector);
}
Multithreaded implementation:
For a multithreaded version, get the LocalToWorld component as an Execute method parameter in a job, as shown in the example for reading LocalToWorld.
UnityEngine method: Translate
With the Entities API, to achieve a behavior similar to the Transform.Translate method, the approach depends on the translation space.
When called with Space.Self as its relativeTo argument (the default), Transform.Translate moves the transform along its own rotated axes, not the world axes. To match this behavior, apply the entity's LocalTransform.Rotation to the translation argument before adding it to LocalTransform.Position. Applying only the entity's own Rotation produces the correct self-space translation whether or not the entity has a parent. If the entity has a parent, the parent entity's rotation appears both when converting the translation argument from local space to world space and when converting back to the local space of the parent entity, and those two contributions cancel out:
Main-thread implementation:
// For Space.Self.
void TranslateSelf(ref SystemState state, Entity e, float3 translation)
{
// Applying LocalTransform.Rotation to the translation argument
// produces the correct local-space translation without a parent
// entity lookup.
LocalTransform transform = SystemAPI.GetComponent<LocalTransform>(e);
SystemAPI.SetComponent(e,
transform.Translate(math.mul(transform.Rotation, translation)));
}
When Transform.Translate is called with Space.World as its relativeTo argument, and the entity might have a parent, convert the translation argument into the local space of the parent entity first:
// For Space.World when the entity might have a parent.
void TranslateWorld(ref SystemState state, Entity e, float3 translation)
{
// The `translation` argument is in world space. If the entity has
// a parent, convert it into the local space of the parent entity
// before applying it.
float3 localTranslation = translation;
if (SystemAPI.HasComponent<Parent>(e))
{
Entity parent = SystemAPI.GetComponent<Parent>(e).Value;
float4x4 parentL2W = SystemAPI.GetComponent<LocalToWorld>(parent).Value;
localTranslation = parentL2W.InverseTransformDirection(translation);
}
LocalTransform transform = SystemAPI.GetComponent<LocalTransform>(e);
SystemAPI.SetComponent(e, transform.Translate(localTranslation));
}
Inside a parallel job, modify the entity's LocalTransform component as an Execute method parameter, then apply LocalTransform.Rotation to the job's Translation field and add the result to LocalTransform.Position, the same way as in the main-thread version. The parallel scheduler guarantees that no two workers process the same entity at the same time, so the job doesn't need a ComponentLookup<LocalTransform> when it only modifies the entity that it processes:
// Multithreaded equivalent of TranslateSelf: rotate the job's Translation
// field by the entity's own LocalTransform.Rotation before adding it to
// LocalTransform.Position. The job doesn't need a ComponentLookup<LocalTransform>
// because it only modifies the entity that it processes.
[BurstCompile]
public partial struct TranslateJob : IJobEntity
{
public float3 Translation;
void Execute(ref LocalTransform transform, in ExampleTag tag)
{
transform.Position += math.mul(transform.Rotation, Translation);
}
}
Methods with no equivalent
The following methods have no equivalent in the Entities API:
FindGetSiblingIndex. The order of child entities is arbitrary.SetAsFirstSibling. The order of child entities is arbitrary.SetAsLastSibling. The order of child entities is arbitrary.SetSiblingIndex. The order of child entities is arbitrary.