본문 바로가기

Unity/DOTS

DOTS 학습을 위한 기초 정리 #16 샘플 학습 - 프리팹 등록과 생성

반응형

참고 : 본 문서는 아래 URL의 Unity-Technologies/EntityComponentSystemSamples 을 포함하고 있다는 가정하에 작성되어 있다.

https://github.com/Unity-Technologies/EntityComponentSystemSamples

 

Unity-Technologies/EntityComponentSystemSamples

Contribute to Unity-Technologies/EntityComponentSystemSamples development by creating an account on GitHub.

github.com

첨부파일 : 6.SpawnFromEntity를 따라 작성하고 주석을 달아 분석한 파일

ECS-Labs.zip
0.01MB

 

코드 설명

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;
        }


    }
}

등록된 엔티티 데이터와 컴포넌트 데이터를 이용해 엔티티를 생성한다.

 

반응형