2022.03.16 - [분류 전체보기] - Unity ECS - 일단 시작2
이전 글에서는 큐브가 이미 씬에 포함되어 있었고, 큐브에 붙어있는 ConvertToEntity 스크립트가 게임 오브젝트를 엔티티로 변환해주었다.
그렇다면 동적으로 큐브를 생성하는건 어떻게 해야할까? ConvertToEntity가 붙어있는 Cube를 GameObject.Instantiate로 생성한다면... 불필요하게 GameObject를 Entity로 변환하는 과정을 거쳐야 한다.
따라서 엔티티 전용의 Instantiate 함수를 쓰는 것이 바람직 할것이다.
Instantiate
단순하게 MonoBehaviour로 된 Spawner를 만들어보자.
Entity 전용의 Instantiate를 사용하기 위해선, 당연히 그 Prefab 또한 Entity여야 한다. 따라서 Start()에서 GameObject인 프리팹을 Entity인 프리팹으로 바꿔준다. (이제 GameObject 프리팹에는 ConvertToEntity 스크립트가 없어도 된다.)
public class Spawner : MonoBehaviour
{
public GameObject Prefab;
private Entity _prefab; //내부적으로 사용되는 Entity Prefab
private EntityManager _entityManager;
private void Start()
{
var settings = GameObjectConversionSettings.FromWorld(World.DefaultGameObjectInjectionWorld, null);
_prefab = GameObjectConversionUtility.ConvertGameObjectHierarchy(Prefab, settings);
_entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;
}
}
그다음 Cube를 Instantiate 하는 과정은 다음과 같다.
private void Spawn(int count)
{
for (int i = 0; i < count; ++i)
{
var instance = _entityManager.Instantiate(_prefab);
var radius = Random.Range(minRadius, maxRadius);
var (pos, velocity) = GetRandomPosAndVelocity(radius, CubeCenter.G);
_entityManager.SetComponentData(instance, new Translation { Value = pos });
_entityManager.SetComponentData(instance, new Velocity { Value = velocity });
}
}
GameObject와 마찬가지로 인스턴스를 생성후에 참조를 유지하다가 Translation, Velocity 등의 값들을 지정해준다.
잘 된다.
단, Spawner가 Entity이고 멀티쓰레딩을 활용한다면 아래 Destory에서 설명할 EntityCommnadBuffer를 사용해줘야 한다.
Destory
Destroy는 쉽다. 그냥 EntityManager.DestoryEntity를 호출하면 된다.
그!!러!!나!!
여러 개의 쓰레드가 엔티티들에 접근하는 와중에 DestroyEntity가 호출된다면 예기치 못한 문제가 발생할 것이다. 혹은 문제를 방지하기 위한 Race가 생겨 성능이 저하될 것이다.
다시 말해... 여러 쓰레드 작업중인 엔티티에 구조적인 변화가 일어나면 안 된다.
이를 해결하기 위해 CommandBuffer에 삭제 명령을 넣어두고 모든 job의 Update가 끝나면 쌓인 엔티티 삭제 명령들을 일괄 수행한다. 이를 위한 CommandBuffer가 EntityCommandBuffer이다.
public class LifeTimeDestroySystem : SystemBase
{
private EntityCommandBufferSystem _ecbs;
protected override void OnCreate()
{
_ecbs = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
}
protected override void OnUpdate()
{
float deltaTime = Time.DeltaTime;
var ecb = _ecbs.CreateCommandBuffer().AsParallelWriter();
Entities.ForEach((Entity entity, int nativeThreadIndex, ref LifeTime lifeTime) =>
{
lifeTime.Value -= deltaTime;
if (lifeTime.Value <= 0)
ecb.DestroyEntity(nativeThreadIndex, entity);
}).ScheduleParallel();
_ecbs.AddJobHandleForProducer(Dependency);
}
}
실제 엔티티를 삭제하는 코드인
if (lifeTime.Value <= 0)
ecb.DestroyEntity(nativeThreadIndex, entity);
이 코드에서 첫번째 인자는 sortKey이다. 이것은 삭제 명령을 어떤 순서로 처리할지를 결정하는 역할을 하기에 적절한 sortKey를 넘겨준다면 작업이 결정적(Deterministic)이 되게 만들수도 있다.
sortKey 참고..
아무튼 실행하면 위 화면처럼 된다.
그런데 Entity들이 자연스럽게 삭제되지 않고 뭔가 양자적으로 삭제되는데 이 부분은 ECB를 좀 더 파봐야 알 수 있을 듯.
'게임엔진 > DOTS' 카테고리의 다른 글
DOTS 1.0 - 2 (생성, MonoBehavour 연계) (0) | 2023.02.04 |
---|---|
DOTS 1.0 - 기본 (Component,System,Aspect,Job) (0) | 2023.01.29 |
DOTS 1.0 나온 기념 세팅법 (2) | 2023.01.01 |
DOTS - 일단 시작2 (2) | 2022.03.16 |
DOTS - 일단 시작 1 (1) | 2022.03.13 |