ECS(Entity Component System) 基礎中の基礎
原点回りにCubeが回転するだけの単純なサンプルです。
Script
CubeDataで使用するデータを定義します。
using UnityEngine;
using Unity.Entities;
using Random = Unity.Mathematics.Random;
namespace EmptyCan
{
public struct CubeData : IComponentData
{
public float Speed;
public Vector3 Direction;
public Vector3 Axis;
public Quaternion Rotaiton;
public Quaternion InitRotation;
public float MaxRadius;
public static CubeData Set(uint seed, float max_radius)
{
float min_speed = 100f;
float max_speed = 10000f;
Random random = new Random(seed);
Quaternion rotation = Quaternion.Euler(random.NextFloat(0f, 360f), random.NextFloat(0f, 360f), random.NextFloat(0f, 360f));
float min_value = 1f / max_radius;
float t = random.NextFloat(min_value, 1f);
t = random.NextFloat(t, 1f); //原点付近で生成される数を減らすため
float radius = t * max_radius;
Vector3 direction = rotation * Vector3.up * radius;
Vector3 axis = rotation * Vector3.forward;
Quaternion init_rotation = Quaternion.FromToRotation(Quaternion.FromToRotation(Vector3.forward, axis) * Vector3.up, -direction);
return new CubeData()
{
Speed = random.NextFloat(min_speed, max_speed),
Direction = direction,
Axis = axis,
Rotaiton = rotation,
InitRotation = init_rotation,
MaxRadius = max_radius
};
}
}
}
CubeAuthoringではEntityを取得し、そのEntityにCubeData Componentを追加しています。よって、CubeAuthoringにはCubeを動かすための処理はありません。また、GetEntityでTransformUsageFlags.Dynamicを指定した場合、LocalToWorldとLocalTransform Componentが追加されます。
using Unity.Entities;
using UnityEngine;
using Unity.Mathematics;
namespace EmptyCan
{
public class CubeAuthoring : MonoBehaviour
{
public uint Seed;
public float MaxRadius;
class Baker : Baker<CubeAuthoring>
{
public override void Bake(CubeAuthoring authoring)
{
CubeData data = CubeData.Set(math.max(1, authoring.Seed), math.max(1f, authoring.MaxRadius));
Entity entity = GetEntity(TransformUsageFlags.Dynamic); //Entityを取得
AddComponent(entity, data);
}
}
}
}
Cubeを動かすための処理は以下のScriptで行います。このように、ECSではデータと処理を分けて記述する必要があります。また、このSystemは動かすために何かする必要はなく、自動的に実行されます。Cubeを動かすためにはCubeDataとLocalTransformの取得を行い、CubeDataをもとに計算を行いLocalTransformを変更する必要があります。よって、SystemAPI.QueryでこれらのComponentを取得します。SystemAPI.Query内のRefROは読み取り専用、RefRWは読み込みだけではなく書き込みができることを示します。そして、Componentから値を取得する場合はValueRO、書き込みを行う場合はValueRWを使用します。
using Unity.Burst;
using Unity.Entities;
using Unity.Transforms;
using UnityEngine;
namespace EmptyCan
{
public partial struct CubeSystem : ISystem
{
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
//CubeDataとLocalTransformをもつ場合にComponentを取得
foreach(var (cube, xform) in SystemAPI.Query<RefRO<CubeData>, RefRW<LocalTransform>>())
{
float radius = cube.ValueRO.MaxRadius;
float angle = cube.ValueRO.Speed / radius * (float)SystemAPI.Time.ElapsedTime;
Vector3 direction = cube.ValueRO.Direction;
Vector3 axis = cube.ValueRO.Axis;
Quaternion init_rotation = cube.ValueRO.InitRotation;
Vector3 position = Quaternion.AngleAxis(angle, axis) * direction;
xform.ValueRW.Position = position;
Quaternion quaternion = init_rotation * Quaternion.FromToRotation(Vector3.forward, axis);
quaternion = Quaternion.AngleAxis(angle, axis) * quaternion;
xform.ValueRW.Rotation = quaternion;
}
}
}
}
SystemAPI.Queryについて
SystemAPI.Queryでは指定したComponentを有するEntityのComponentを取得できます。複数のComponentを指定した場合、それらを有するEntityのComponentのみを取得します。
例としてEntityを作成する際にTransformUsageFlags.Dynamicを指定したCubeAとTransformUsageFlags.Noneを指定したCubeBを使用します。(Burstを利用する場合はstringが使用できないのでFixedStringを使っています。)
//CubeA
CubeData data = CubeData.Set(
math.max(1, authoring.Seed),
math.max(1f, authoring.MaxRadius),
new FixedString32Bytes(authoring.name));
Entity entity = GetEntity(TransformUsageFlags.Dynamic);
//CubeB
CubeData data = CubeData.Set(
math.max(1, authoring.Seed),
math.max(1f, authoring.MaxRadius),
new FixedString32Bytes(authoring.name));
Entity entity = GetEntity(TransformUsageFlags.None);
TransformUsageFlags.DynamicはLocalTransformが追加されますが、TransformUsageFlags.Noneは追加されません(Colliderがある場合は自動的にLocalTransformが追加されます)。この条件でSystemAPI.QueryへCubeDataのみ指定するとConsoleにはCubeAとCubeBが表示されます。
foreach (var cube in SystemAPI.Query<RefRO<CubeData>>())
{
Debug.Log(cube.ValueRO.Name);
}
次に、SystemAPI.QueryにLocalTransformを追加します。
foreach (var (cube, xform) in SystemAPI.Query<RefRO<CubeData>, RefRW<LocalTransform>>())
{
Debug.Log(cube.ValueRO.Name);
}
これを実行すると、LocalTransformを持たないCubeBは除外されるため、ConsoleにはCubeAのみ表示されます。
設定と実行結果
以上のScriptを利用してCubeを回転させます。始めに、SubSceneを作成します。作成したSubSceneの横に表示されているチェックボックスにチェックを入れます。

次に、Cubeを作成し、SubSceneへ移動させます。

最後に、作成したCubeへCubeAuthoringを追加します。

実行すると以下のように、原点回りにCubeが回転します。

参考ページ
-
前の記事
三次ベジェ曲線の最接近点(近似二次ベジェ曲線を利用) 2023.12.10
-
次の記事
PrefabからEntityを生成(Entity Component System) 2025.01.13
コメントを書く