Section 1: Non-hierarchical Transforms(Basic)
LocalToWorld(float4X4)表示从本地到世界空间的变换。这是一种经典的表示方式,也是系统中唯一用来访问本地空间的组件。
- 一些DOTS特性,只有存在LocalToWorld时才能执行。
- 例如,RenderMesh组件依赖LocalToWorld来渲染实例。
- 如果只有一个LocalToWorld组件,那么没有transform system会写或影响LocalToWorld数据(Translation,Rotation,Scale组件配合)。
- 如果没有其它的变换组件添加到该实体,则用户可以直接修改LocalToWorld的数据来为实例定义方位变换。
所有的transform systems和其它的transform components都是用来提供接口来更新LocalToWorld的数据。
LocalToWorld = Translation * Rotation * Scale。
如果有Translation(float3),Rotation(quaternion),Scale(float)和LocalToWorld同时存在,那么transform system会把这些组件组合并写到LocalToWorld中。
下面列出具体的组合:
- [TRSToLocalToWorldSystem] LocalToWorld <= Translation
- [TRSToLocalToWorldSystem] LocalToWorld <= Translation * Rotaion
- [TRSToLocalToWorldSystem] LocalToWorld <= Translation * Rotation * Scale
- [TRSToLocalToWorldSystem] LocalToWorld <= Rotation
- [TRSToLocalToWorldSystem] LocalToWorld <= Rotation * Scale
- [TRSToLocalToWorldSystem] LocalToWorld <= Scale
Section 2: Hierarchical Transforms (Basic)
transform system 利用 LocalToParent 和 Parent 组件来为 LocalToWorld 计算基于层级的 transform。
- LocalToParent(float4X4)表示本地空间到父节点本地空间的变换。
- Parent(实体) 引用了父节点的 LocalToWorld。
- 如果实体没有其它的transform system定义对LocalToParent的更新,那么可以直接用代码更新 LocalToParent。
例如下面的实体及其组件:
Parent(Entity) | Child(Entity) |
---|---|
LocalToWorld | LocalToWorld |
Translation | LocalToParent |
Rotation | Parent |
Scale |
那么transform system会:
- [TRSToLocalToWorldSystem] Parent: LocalToWorld = Translation * Rotation * Scale
- [TRSToParentSystem] Child: LocalToWorld = LocalToWorld[Parent] * LocalToParent
系统保证父节点实体IDs对应的 LocalToWorld会在子节点实体ID对应的LocalToParent与它相乘前计算储结果。
注:循环的父子关系是无效的,结果未定义。
当拓扑层级改变(例如,任一父节点添加,删除或改变组件)时,SystemStateComponentState内部状态会被添加:
- 子节点组件(ISystemStateBufferElementData of Entity)关联到父节点实体ID
- PreviousParent组件(ISystemStateComponentData of Entity)关联到子节点实体ID
Parent(Entity) | Child(Entity) |
---|---|
LocalToWorld | LocalToWorld |
Translation | LocalToParent |
Rotation | Parent |
Scale | PreviousParent* |
Child* |
添加,删除,更新这些组件,都是由[ParentSystem]完成的。transform systems之外的系统不能读写这些组件。
LocalToParent = Translation * Rotation * Scale
如果任一Translation(float3),Rotation(Quaternion),Scale(float)组件与LocalToParent组件同时出现,transform system 会将这些组件的值组合并写到 LocalToParent中
- [TRSToLocalToParentSystem] LocalToWorldParent <= Translation
- [TRSToLocalToParentSystem] LocalToWorldParent <= Translation * Rotaion
- [TRSToLocalToParentSystem] LocalToWorldParent <= Translation * Rotation * Scale
- [TRSToLocalToParentSystem] LocalToWorldParent <= Rotation
- [TRSToLocalToParentSystem] LocalToWorldParent <= Rotation * Scale
- [TRSToLocalToParentSystem] LocalToWorldParent <= Scale
对于如下父子实体:
Parent(Entity) | Child(Entity) |
---|---|
LocalToWorld | LocalToWorld |
Translation | LocalToParent |
Rotation | Parent |
Scale | PreviousParent* |
Child* | Translation |
Rotation | |
Scale |
那么变换系统会分别执行:
- [TRSToLocalToWorldSystem] Parent: LocalToWorld = Translation * Rotation * Scale
- [TRSToLocalToParentSystem] Child: LocalToParent = Translation * Rotation * Scale
- [LocalToParentSystem] Child: LocalToWorld = LocalToWorld[Parent] * LocalToParent
如果父节点也有父节点:
Parent(Entity) | Child(Entity) |
---|---|
LocalToWorld | LocalToWorld |
LocalToParent | LocalToParent |
Parent | Parent |
PreviousParent* | PreviousParent* |
Child* | Translation |
Translation | Rotation |
Rotation | Scale |
Scale |
那么变换系统会执行:
- [TRSToLocalToParentSystem] Parent: LocalToParent = Translation * Rotation * Scale
- [TRSToLocalToParentSystem] Child: LocalToParent = Translation * Rotation * Scale
- [LocalToParentSystem] Parent: LocalToWorld = LocalToWorld[Parent] * LocalToParent
- [LocalToParentSystem] Child : LocalToWorld = LocalToWorld[Parent] * LocalToParent
也就是,TRSToLocalToParentSystem先执行,再执行LocalToParentSystem。
Section 3: Default Conversion (Basic)
Hybrid Conversion:
带有UnityEngine.Transform MonoBehaviour 组件的GameObjects,并且有 Covert To Entity MonoBehaviour组件,或者该GameObject在Sub Scenes内,则该GameObject 有默认的转换,将UnityEngine.Transform转换为Transform 系统的组件。该转换可以在 Unity.Transforms.Hybrid 的程序集中的 TransformConversion 系统找到。
要被转换成Enttities的 有静态component的GameObject,仅会为entity添加 LocalToWorld 组件。因此对于静态实例,运行时transform systems不会被更新。
对于非静态的实体,
a. 添加Translation组件并将Transform.position的值赋给它。
b. 添加Rotation组件并将Transform.rotation的值赋给它。
c. 如果Transform.parent == null ,对于非均匀缩放(x,y,z都相等为均匀缩放)的Transform.localScale,添加NonUniformScale组件,并将Transform.localScale的值赋给它。
d. 如果Transform.parent != null,在层级(局部的)开始转换时,对非均匀的Transform.lossyScale,添加NonUniformScale组件,并将Transform.lossyScale的值赋给它。
e. 对于其它Transform.parent != null的情况,添加Parent组件,并引用Transform.parent转换的Entity。添加LocalToParent组件。
Section 4: Non-hierarchical Transforms (Advanced)
NonUniformScale(float3)是一种在每个轴上分别缩放的方式。需要注意的是,不是所有的DOTS特性都完全支持非均匀缩放。确保核实它们的文档来确定它们的限制。
- [TRSToLocalToWorldSystem] LocalToWorld <= Translation
- [TRSToLocalToWorldSystem] LocalToWorld <= Translation * Rotation
- [TRSToLocalToWorldSystem] LocalToWorld <= Translation * Rotation * NonUniformScale
- [TRSToLocalToWorldSystem] LocalToWorld <= Rotation
- [TRSToLocalToWorldSystem] LocalToWorld <= Rotation * NonUniformScale
- [TRSToLocalToWorldSystem] LocalToWorld <= NonUniformScale
Scale和NonUniformScale同时存在是无效的情况,但是结果是确定的,Scale将会被应用,NonUniformScale会被忽略。
例如下面的实体组件:
(Entity) |
---|
LocalToWorld |
Translation |
Rotation |
NonUniformScale |
transform system 将:
[TRSToLocalToWorldSystem] Write LocalToWorld <= Translation * Rotation * NonUniformScale
用户可以直接以四元数的形式写入 Rotation 组件数据。然而如果用的是欧拉角,对应旋转顺序的组件将被添加,并向Rotation组件写入数据。
- [RotationEulerSystem] Rotation <= RotationEulerXYZ
- [RotationEulerSystem] Rotation <= RotationEulerXZY
- [RotationEulerSystem] Rotation <= RotationEulerYXZ
- [RotationEulerSystem] Rotation <= RotationEulerYZX
- [RotationEulerSystem] Rotation <= RotationEulerZXY
- [RotationEulerSystem] Rotation <= RotationEulerZYX
如果是下面的组件布局:
(Entity) |
---|
LocalToWorld |
Translation |
Rotation |
RotationEulerXYZ |
则 transform system 会:
- [RotationEulerSystem] Write Rotation <= RotationEulerXYZ
- [TRSToLocalToWorldSystem] Write LocalToWorld <= Translation * Rotation * Scale
如果一个Entity拥有多个RotationEular***的组件,说明该实体创建出错了,但是结果也被定义了。第一优先级顺序的将被应用。优先级顺序为:
- RotationEulerXYZ
- RotationEulerXZY
- RotationEulerYXZ
- RotationEulerYZX
- RotationEulerZXY
- RotationEulerZYX
对于更加复杂的旋转需要,可以使用CompositeRotation(float4x4)。
- [TRSToLocalToWorldSystem] LocalToWorld <= Translation * CompositeRotation
- [TRSToLocalToWorldSystem] LocalToWorld <= Translation * CompositeRotation * Scale
- [TRSToLocalToWorldSystem] LocalToWorld <= CompositeRotation
- [TRSToLocalToWorldSystem] LocalToWorld <= CompositeRotation * Scale
- [TRSToLocalToWorldSystem] LocalToWorld <= Translation * CompositeRotation
- [TRSToLocalToWorldSystem] LocalToWorld <= Translation * CompositeRotation * NonUniformScale
- [TRSToLocalToWorldSystem] LocalToWorld <= CompositeRotation
- [TRSToLocalToWorldSystem] LocalToWorld <= CompositeRotation * NonUniformScale
可以在代码中直接对CompositeRotation组件赋float4x4的值。然而如果用的是Maya/FBX数据接口,将会添加相关组件并向CompositeRotation中写数据:
CompositeRotation = RotationPivotTranslation * RotationPivot * Rotation * PostRotation * RotationPivot^-1
如果RotationPivotTranslation(float3),RotationPivot(float3),Rotation(quaternion),或PostRotatin(quaternion)和CompositeRotation一起出现,则transform system会将他们组合并写到CompositeRotation中去。
CompositeRotation通常用不到,包括后面的CompositeScale,就不说了,读者自己查阅吧。
Section 5: Hierarchical Transforms (Advanced)
注:高级的层级 transform 组件规则跟non-hierarchical非常类似,除了层级的是写入到LocalToParent。主要增加的专用的组件是ParentScaleInverse。
- [TRSToLocalToParentSystem] LocalToParent <= Translation
- [TRSToLocalToParentSystem] LocalToParent <= Translation * Rotation
- [TRSToLocalToParentSystem] LocalToParent <= Translation * Rotation * NonUniformScale
- [TRSToLocalToParentSystem] LocalToParent <= Rotation
- [TRSToLocalToParentSystem] LocalToParent <= Rotation * NonUniformScale
- [TRSToLocalToParentSystem] LocalToParent <= NonUniformScale
如下父子实体的组件布局:
Parent(Entity) | Child(Entity) |
---|---|
LocalToWorld | LocalToWorld |
Translation | LocalToParent |
Rotation | Parent |
Scale | PreviousParent* |
Child* | Translation |
Rotation | |
NonUniformScale |
transform系统会执行:
- [TRSToLocalToWorldSystem] Parent: Write LocalToWorld as defined above in “Non-hierarchical Transforms (Basic)”
- [TRSToLocalToParentSystem] Child: Write LocalToParent <= Translation * Rotation * NonUniformScale
- [LocalToParentSystem] Child: Write LocalToWorld <= LocalToWorld[Parent] * LocalToParent
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QdastLLj-1576920475766)(https://docs.unity3d.com/Packages/com.unity.entities@0.1/manual/images/sec5-1.png)]
更高级的不翻了
Section 6: Custom Transforms (Advanced)
有2种方法来写用户定义的transforms来与transform system兼容:
- 覆写transform components
- 扩展transform components
Overriding transform components
添加一个用户自定义的组件,并添加LocalToWorld WriteGroup:
[Serializable]
[WriteGroup(typeof(LocalToWorld))]
struct UserComponent : IComponentData
{
}
覆写意味着添加了UserComponent后,由用户来完成完成转换。
在UserTransformSystem中,使用默认的query方法来访问LocalToWorld,例如:
public class UserTransformSystem : Jobcomponent
{
[BurstCompile]
struct UserTransform : IJobForEach<LocalToWorld, UserComponent>
{
public void Execute(ref LocalToWorld localToWorld, [ReadOnly] ref UserComponent userComponent)
{
localToWorld.Value = ... // 根据需要对localToWorld进行赋值
}
}
protected override JobHandle OnUpdate(JobHandle inputDependencies)
{
var job = new UserTransform()
{
};
return job.Schedule(this, inputDependencies);
}
}
对添加了UserComponent的实体,所有其它写LocalToWorld 的transform 组件都将被忽略。
例如以下实体组件布局:
(Entity) |
---|
LocalToWorld |
Translation |
Rotation |
Scale |
UserComponent |
那么:
[TRSToLocalToWorldSystem] 不执行
[UserTransformSystem] 执行
如果有2个覆写LocalToWorld的组件存在:
UserComponent2
UserTransformSystem2
两个系统都会写LocalToWorld,将导致未知结果。
Extending transform components
为了保证多个组件正确地转换,需要定义WriteGroup过滤:
[Serializable]
[WriteGroup(typeof(LocalToWorld))]
struct UserComponent : IComponentData
{
}
基于WriteGroup的过滤:
public class UserTransformSystem : JobComponentSystem
{
private EntityQuery m_Query;
protected override void OnCreate()
{
m_Query = GetEntityQuery(new EntityQueryDesc()
{
All = new ComponentType[]
{
ComponentType.ReadWrite<LocalToWorld>(),
ComponentType.ReadOnly<userComponent>(),
},
Options = EntityQueryDescOption.FilterWriteGroup
}
);
}
[BurstCompile]
struct UserTransform : IJobForEach<LocalToWorld, UserComponent>
{
public void Execute(ref LocalToWorld localToWorld, [ReadOnly] ref UserComponent userComponent)
{
localToWorld.Value = ... // Assign localToWorld as needed for UserTransform
}
}
protected override JobHandle OnUpdate(JobHandle inputDependencies)
{
var job = new UserTransform()
{
};
return job.ScheduleGroup(m_Query, inputDependencies);
}
}
UserTransformSystem的m_Query只处理明确提到的组件。
例如,下面的实体将被处理:
(Entity) |
---|
LocalToWorld |
UserComponent |
下面的不会被处理:
(Entity) |
---|
LocalToWorld |
Translation |
Rotation |
Scale |
UserComponent |
包含了UserComponent的实体,如果包含其它在同一个WriteGroup的组件,并且没有明确声明的组件,实体将不处理
上面的实体也可以定义EntityQuery来处理:
public class UserTransformExtensionSystem : JobComponentSystem
{
private EntityQuery m_Query;
protected override void OnCreate()
{
m_Query = GetEntityQuery(new EntityQueryDesc()
{
All = new ComponentType[]
{
ComponentType.ReadWrite<LocalToWorld>(),
ComponentType.ReadOnly<UserComponent>(),
ComponentType.ReadOnly<Translation>(),
ComponentType.ReadOnly<Rotation>(),
ComponentType.ReadOnly<Scale>(),
},
Options = EntityQueryDescOptions.FilterWriteGroup
});
}
[BurstCompile]
struct UserTransform : IJobForEach<LocalToWorld, UserComponent>
{
public void Execute(ref LocalToWorld localToWorld, [ReadOnly] ref UserComponent userComponent,
[ReadOnly] ref Translation translation,
[ReadOnly] ref Rotation rotation,
[ReadOnly] ref Scale scale)
{
localToWorld.Value = ... // Assign localToWorld as needed for UserTransform
}
}
protected override JobHandle OnUpdate(JobHandle inputDependencies)
{
var job = new UserTransform()
{
};
return job.ScheduleGroup(m_Query, inputDependencies);
}
}
同样,我们在加一个组件:
[Serializable]
[WriteGroup(typeof(LocalToWorld))]
struct UserComponent2 : IComponentData
{
}
实体布局:
(Entity) |
---|
LocalToWorld |
UserComponent |
UserComponent2 |
我们可以定义相应的EntityQuery来处理:
public class UserTransformComboSystem : JobComponentSystem
{
private EntityQuery m_Query;
protected override void OnCreate()
{
m_Query = GetEntityQuery(new EntityQueryDesc()
{
All = new ComponentType[]
{
ComponentType.ReadWrite<LocalToWorld>(),
ComponentType.ReadOnly<UserComponent>(),
ComponentType.ReadOnly<UserComponent2>(),
},
Options = EntityQueryDescOptions.FilterWriteGroup
});
}
[BurstCompile]
struct UserTransform : IJobForEach<LocalToWorld, UserComponent>
{
public void Execute(ref LocalToWorld localToWorld,
[ReadOnly] ref UserComponent userComponent,
[ReadOnly] ref UserComponent2 userComponent2
{
localToWorld.Value = ... // Assign localToWorld as needed for UserTransform
}
}
protected override JobHandle OnUpdate(JobHandle inputDependencies)
{
var job = new UserTransform()
{
};
return job.ScheduleGroup(m_Query, inputDependencies);
}
}
翻译到此结束,历时将近2个月,中间经历了Word文档文件损坏,又改用Markdown,。。。