I attached the script CubeMover.cs to the parent GameObject. In the CubeMover we collect all cubes are parented by referencing to the class CubeSingle . In #if true, we rotate all the cubes by the common way and count the performance. The result is about 0.64ms
Now toggle both #if statements, so the Hybrid ECS comes into action. The result is about 0.66ms.
How to collect several Gameobject in an array and run Hybrid ECS correctly?
Thanks!
using UnityEngine;
using Unity.Entities;
public class CubeMover : MonoBehaviour {
// Container for all single Cubes
public CubeSingle[] cubeSingles;
// Collect all single Cubes
private void Awake() {
cubeSingles = this.GetComponentsInChildren<CubeSingle>();
}
#if true
System.Diagnostics.Stopwatch ti = new System.Diagnostics.Stopwatch();
private void Update() {
ti.Reset();
ti.Start();
foreach (var c in cubeSingles) {
c.transform.Rotate(0, 1, 0);
}
Debug.Log("common " + ti.ElapsedTicks / 10000f);
}
#endif
}
class CubeMoverSystem : ComponentSystem {
struct Components {
public CubeMover cubes;
}
System.Diagnostics.Stopwatch ti = new System.Diagnostics.Stopwatch();
protected override void OnUpdate() {
#if false
ti.Reset();
ti.Start();
// Collect all single Cubes
foreach (var e in GetEntities<CubeMoverSystem.Components>()) {
foreach(var t in e.cubes.cubeSingles) {
t.transform.Rotate(0, 1, 0);
}
}
Debug.Log("hybrid " + ti.ElapsedTicks / 10000f);
#endif
}
}
The question was not to should I âsit and waitâ.
Anyway, Iâm not sure if this is the correct way to access the game objects transform, because the collected game objects are spread all over the place in memory. Further, the compiler produce errors while using an array in
struct Components { public Transform[ ] ArrayOfCubeTransforms; }
Does anyone know how to collect several game objects and use the hybrid ECS correctly?
Thank you.
The example you gave wonât be getting you any real performance increase as youâre pretty much performing the exact same operation in each case: loop over GameObjects stored in the array of your monobehavior and call Rotate on their transform component.
There might be a bit of a performance increase if you used injection, such as this (untested and off the top of my inexperienced head):
public struct Data {
public readonly int Length;
public ComponentData<CubeSingle> Cubes;
public ComponentData<Transform> Transforms;
}
[Inject] private Data cubeData;
protected override void OnUpdate()
{
for(int i = 0; i < cubeData.Length; i++)
{
cubeData.Transforms[i].Rotate(0, 1, 0);
}
}
I would also say applying a constant rotation via a transform method on 1000 cubes isnât exactly the best example to explore the benefits of ECS, especially if youâre not using Jobs. Thereâs not much room for improvement given how little itâs actually doing. Profiling using a stopwatch probably isnât the best way to accurately measure it either, you should be using Unityâs Profiler tool to accurately measure the performance.
Thanks. Currently I check how to render the complete game world in pure or hybrid ECS on PC. Rendering common ~8.000-10.000 static game objects cause the cpu load to about 15ms. Based on the info that pure ECS is able to render 100.000 in about the the same time, Iâm wondering why pure ECS is not used internally to render common static scene game objects.
In other words, we are able to move and render >100.000 entity objects nice and cool, but stuck at static scene rendering > 10.000 cooking the cpu.
A beside question that is nowhere answered, would it possible to prepare different meshes with different materials by ONE entity manager? So my idea is to write a script that read all scene static game objects at Start() and prepare to render it completely with the hybrid or pure ECS system. While loading an additional scene at runtime, positions and rotations can be serialized previously.
Unfortunately your code example gives me an error in that small example.(all required packages are loaded 2018.2.0f2)
using UnityEngine;
using Unity.Entities;
public class CubeMover : MonoBehaviour {
// Container for all single Cubes
public CubeSingle[] cubeSingles;
// Collect all single Cubes
private void Awake() {
cubeSingles = this.GetComponentsInChildren<CubeSingle>();
}
}
public class CubeMoverSystem : ComponentSystem {
public struct Data {
public readonly int Length;
public ComponentData<CubeSingle> Cubes;
public ComponentData<Transform> Transforms;
}
System.Diagnostics.Stopwatch ti = new System.Diagnostics.Stopwatch();
[Inject] private Data cubeData;
#if true
protected override void OnUpdate() {
for (int i = 0; i < cubeData.Length; i++) {
cubeData.Transforms[i].Rotate(0, 1, 0);
}
}
#endif
}
public ComponentData Cubes;
public ComponentData Transforms;
Error CS0246 The type or namespace name âComponentData<>â could not be found (are you missing a using directive or an assembly reference?)
public ComponentDataArray Cubes; doesnât work as well because of the none null-able
Any ideas?
Cleared up a little and added a âpurishâ ECS mode + burst:
No ECS (not defining UseEcs or UseBetterEcs): 0.70ms
Define UseEcs: 0.47ms
Define UseBetterEcs with burst: 0.05ms + 0.05ms for CopyTransformToGameObjectSystem
The actual rotation code takes around 0.015ms and the system overhead of 0.035ms is probably mostly gone in standalone as is CopyTransformToGameObjectSystem.
Additionally UseBetterEcs runs in a job and could even use a IJobParallelFor to use additional CPUs, which I did not do to be able to better compare the performance.
//#define UseEcs
#define UseBetterEcs
using UnityEngine;
using Unity.Entities;
using Unity.Transforms;
using Unity.Mathematics;
using Unity.Jobs;
using Unity.Burst;
public class CubeMover : MonoBehaviour
{
// Container for all single Cubes
public CubeSingle[] cubeSingles;
// Collect all single Cubes
private void Awake()
{
cubeSingles = this.GetComponentsInChildren<CubeSingle>();
#if UseEcs || UseBetterEcs
foreach (var cube in cubeSingles)
{
cube.gameObject.AddComponent<GameObjectEntity>();
#if UseBetterEcs
var goe = cube.gameObject.GetComponent<GameObjectEntity>();
goe.EntityManager.AddComponentData<CopyInitialTransformFromGameObject>(goe.Entity, default(CopyInitialTransformFromGameObject));
goe.EntityManager.AddComponentData<CopyTransformToGameObject>(goe.Entity, default(CopyTransformToGameObject));
goe.EntityManager.AddComponentData<Rotation>(goe.Entity, default(Rotation));
#endif
}
#endif
}
#if !UseEcs && !UseBetterEcs
System.Diagnostics.Stopwatch ti = new System.Diagnostics.Stopwatch();
private void Update()
{
ti.Reset();
ti.Start();
foreach (var c in cubeSingles)
{
c.transform.Rotate(0, 1, 0);
}
Debug.Log("common " + ti.ElapsedTicks / 10000f);
}
#endif
}
#if UseEcs
class CubeMoverSystem : ComponentSystem
{
struct Cubes
{
public readonly int Length;
public ComponentArray<Transform> transform;
public ComponentArray<CubeSingle> cubes;
}
[Inject] Cubes group;
System.Diagnostics.Stopwatch ti = new System.Diagnostics.Stopwatch();
protected override void OnUpdate()
{
ti.Reset();
ti.Start();
// Collect all single Cubes
for (int i = 0; i < group.Length; i++)
{
group.transform[i].Rotate(0, 1, 0);
}
Debug.Log("hybrid " + ti.ElapsedTicks / 10000f);
}
}
#endif
#if UseBetterEcs
class CubeMoverSystem : JobComponentSystem
{
[BurstCompile]
struct RotateJob : IJobProcessComponentData<Rotation>
{
public quaternion rotateBy;
public void Execute(ref Rotation rotation)
{
rotation = new Rotation { Value = math.mul(rotation.Value, rotateBy) };
}
}
protected override JobHandle OnUpdate(JobHandle dependsOn)
{
var job = new RotateJob
{
rotateBy = quaternion.eulerXYZ(0.0f, 3.14f / 180.0f, 0.0f),
}.Schedule(this, dependsOn);
return job;
}
}
#endif