반응형
참고 : 본 문서는 아래 URL의 Unity-Technologies/EntityComponentSystemSamples 을 포함하고 있다는 가정하에 작성되어 있다.
https://github.com/Unity-Technologies/EntityComponentSystemSamples
첨부파일 : 6.SpawnFromEntity를 따라 작성하고 주석을 달아 분석한 파일
코드 설명
using Unity.Entities;
//컴포넌트 데이터를 작성합니다.
namespace ECS.Labs.IJobForEachSample {
public struct Spawner : IComponentData
{
public int CountX;
public int CountY;
public Entity Prefab;
}
}
프리펩을 등록하면서 엔티티에 등록할 정보다. 2차원 배열을 돌며 엔티티를 생성해줄 것이므로 X,Y로 Count 변수를 두었다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine; //기존 모노비헤비어가 포함된 네임스페이스
using Unity.Entities; //ECS가 포함된 네임스페이스
//에디터 상에서 인스펙터에 등록된 프리펩을 엔티티로 등록, 변환해준다.
namespace ECS.Labs.IJobForEachSample
{
[RequiresEntityConversion]
public class SpawnerAuthoring : MonoBehaviour, IDeclareReferencedPrefabs, IConvertGameObjectToEntity
{
public GameObject Prefab;
public int CountX, CountY; //Spawner.cs에 있는 컴포넌트 데이터와 같은 변수입니다.
//참조된 프리펩은 변환 시스템이 미리 알 수 있도록 선언되어야 합니다.
void IDeclareReferencedPrefabs.DeclareReferencedPrefabs(List<GameObject> referencedPrefabs)
{
referencedPrefabs.Add(Prefab);
}
//에디터 데이터 표시를 엔티티 최적의 런타임 표현으로 변환할 수 있습니다.
void IConvertGameObjectToEntity.Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
{
//컴포넌트 데이터를 생성합니다.
var spawnerData = new Spawner
{
//DeclareReferencedPrefabs로 인해 참조된 프리팹이 변환됩니다.
//여기서는 게임 오브젝트를 프리팹을 참조하는 엔티티에 매핑하기만 하면 됩니다.
Prefab = conversionSystem.GetPrimaryEntity(Prefab),
CountX = CountX,
CountY = CountY
};
//해당 매니저에 컴포넌트 데이터를 등록해줍니다.
dstManager.AddComponentData(entity, spawnerData);
}
}
}
게임오브젝트 (모노비헤비어) 프리펩을 엔티티 데이터로 전환하여 등록해준다.
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
//실질적으로 엔티티 데이터에 접근하여 조작하는 ECS의 S 부분을 담당하는 스크립트
//JobComponentSystem는 작업자 스레드에서 실행할 수 있습니다.
//그러나 Entity들을 만들고 제거하는 작업은 경쟁 조건을 방지하기 위해 메인 스레드에서만 수행할 수 있음을 알아야합니다.
//시스템은 EntityCommandBuffer를 사용하여 작업 내에서 수행할 수 없는 작업을 연기합니다.
namespace ECS.Labs.IJobForEachSample
{
//업데이트 그룹을 지정한다. 해당 부분이 생략되면 SimulationSystemGroup으로 암묵적으로 등록된다.
[UpdateInGroup(typeof(SimulationSystemGroup))]
public class SpawnerSystem : JobComponentSystem
{
//BeginInitializationEntityCommandBufferSystem을 사용하여 명령 버퍼를 생성하는데 사용되며,
//이 버퍼는 차단 시스템이 실행도리 때 재생됩니다.
//인스턴스화 명령은 SpawnJob에 기록되지만 실제로 처리되거나 "재생"되지 않습니다.
//해당 EntityCommandBufferSystem이 업데이트될 때 까지 기다립니다. 변환 시스템에 기회가 있는지 확인합니다.
//새로 저장된 엔티티가 처음으로 렌더링되기 전에 SpawnerSystem에서 실행됩니다.
//BeginInitializationEntityCommandBufferSystem을 사용하여 명령을 재생합니다. 이렇게 하면 1프레임 지연이 발생합니다.
//명령을 기록하는 것과 엔티티를 인스턴스화 하는 것 사이에는 차이가 있지만, 실제로는 일반적으로 이 점이 눈에 띄지 않습니다.
BeginInitializationEntityCommandBufferSystem m_EntityCommandBufferSystem;
protected override void OnCreate()
{
//필드에 BeginInitializationEntityCommandBufferSystem을 캐시하므로 모든 프레임을 생성할 필요가 없습니다.
m_EntityCommandBufferSystem = World.GetOrCreateSystem<BeginInitializationEntityCommandBufferSystem>();
}
//직접 작성한 Spawner 컴포넌트 데이터와 LocalToWorld 컴포넌트 데이터를 불러옵니다.
struct SpawnJob : IJobForEachWithEntity<Spawner, LocalToWorld>
{
//JobHand OnUpdate()를 통해서 명령버퍼를 받아옵니다.
public EntityCommandBuffer.Concurrent CommandBuffer;
//주의점, public을 명시하지 않으면 IJobForEach.cs 139번 라인에서의 GetMethod로의 접근이 불가능합니다. 따라서 생성과 파괴가 불가능 해지니 반드시 public을 명시해줘야합니다.
public void Execute(Entity entity, int index, [ReadOnly] ref Spawner spawner, [ReadOnly] ref LocalToWorld location)
{
for (var x = 0; x < spawner.CountX; x++)
{
for (var y = 0; y < spawner.CountY; y++)
{
//var을 써도 되지만 명확성을 위해 명시적으로 작성했습니다.
//작성된 컴포넌트 데이터를 기반으로 엔티티를 생성합니다.
Entity instance = CommandBuffer.Instantiate(index, spawner.Prefab);
//순서대로 위치정보를 지정합니다. float3이 헷갈린다면 Vector3라고 보시면됩니다.
float3 position = math.transform(location.Value, new float3(x * 1.3F, noise.cnoise(new float2(x, y) * 0.21F) * 2, y * 1.3F));
//모노비헤비어의 Transform유사한 Translation을 세팅해주고 생성한 엔티티에게 적용합니다.
CommandBuffer.SetComponent(index, instance, new Translation { Value = position });
}
CommandBuffer.DestroyEntity(index, entity);
}
}
}
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
//직접 구조 변경을 수행하는 대신, Job은 EntityCommandBuffer에 작업을 완료한 후 기본 스레드에서 이러한 변경을 수행하는 명령을 추가할 수 있습니다.
//명령 버퍼를 사용하면 나중에 실제 삽입 및 삭제를 대기하는 동안 작업자 스레드에서 비용이 많이 들 수 잇는 계산을 수행할 수 잇습니다.
JobHandle job = new SpawnJob
{
CommandBuffer = m_EntityCommandBufferSystem.CreateCommandBuffer().ToConcurrent()
}.Schedule(this, inputDeps);
//SpawnJob은 Barrier System이 실행될 때까지 동기화 지점 없이 병렬로 실행되빈다.
//Barrier System이 실행되면 SpawnJob을 완료한 다음 명령을 재생합니다. (실제 생성 및 배치)
//명령을 재생하려면 어떤 작업을 완료해야 하는지 Barrier System에 알려야 합니다.
m_EntityCommandBufferSystem.AddJobHandleForProducer(job);
//현재 작업을 리턴합니다.
return job;
}
}
}
등록된 엔티티 데이터와 컴포넌트 데이터를 이용해 엔티티를 생성한다.
반응형
'Unity > DOTS' 카테고리의 다른 글
Unity에서 소개하는 DOTS에 대해 주관적 감상 (0) | 2020.07.29 |
---|---|
DOTS 학습을 위한 기초 정리 #17 사용자의 입력을 받아 오브젝트 움직이기(IJobForEach) (0) | 2019.05.31 |
DOTS 학습을 위한 기초 정리 #15 엔티티 데이터 접근 - IJobForEach (0) | 2019.05.29 |
DOTS 학습을 위한 기초 정리 #14 프로젝트에 패키지 다운로드 받기 (0) | 2019.05.29 |