원본: https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/master/DOTS_Guide/ecs_tutorial/README.md

Step 5 - 대포알 이동(Cannon ball movement)

병렬 job에 대한 소개

  1. "Scripts/Aspects" 폴더에 "CannonBallAspect.cs"이라는 이름의 새 C# 소스 파일을 만들고 다음 내용을 저장합니다.
  2. using Unity.Entities;
    using Unity.Mathematics;
    using Unity.Transforms;
    
    readonly partial struct CannonBallAspect : IAspect
    {
        // 외관(aspect)의 엔티티 필드는 엔티티 자체에 대한 액세스를 제공합니다.
        // 이것은 예를 들어 EntityCommandBuffer에 명령을 등록하는 데 필요합니다.
        public readonly Entity Self;
    
        // 외관(aspect)들은 다른 외관(aspect)을 격납할 수 있습니다.
        readonly TransformAspect Transform;
    
        // RefRW 필드는 컴퍼넌트에 대한 읽기 쓰기 액세스 권한을 제공합니다.
        // 외관(aspect)이 "in" 매개 변수로 사용되는 경우 필드는 RefRO인 것처럼 동작하며 쓰기 시도 시 예외를 발생시킵니다.
        readonly RefRW<CannonBall> CannonBall;
    
        // 이와 같은 프로퍼티는 필수가 아니고, 대신 트랜스폼 필드를 공개할 수도 있습니다.
        // 그러나 "aspect.aspect.aspect.component.value.value" 같은 체인을 피함으로써 가독성을 향상시킵니다.
        public float3 Position
        {
            get => Transform.Position;
            set => Transform.Position = value;
        }
    
        public float3 Speed
        {
            get => CannonBall.ValueRO.Speed;
            set => CannonBall.ValueRW.Speed = value;
        }
    }
  3. "Scripts/Systems" 폴더에 "CannonBallSystem.cs"이라는 이름의 새 C# 소스 파일을 만들고 다음 내용을 저장합니다.
  4. using Unity.Burst;
    using Unity.Entities;
    using Unity.Mathematics;
    
    [BurstCompile]
    // IJobEntity는 소스 생성에 의존하여 실행 함수의 서명 시에 암시적으로 쿼리를 정의합니다. 
    partial struct CannonBallJob : IJobEntity
    {
        // 일반 EntityCommandBuffer는 병렬로 사용할 수 없으며, ParallelWriter를 명시적으로 사용해야 합니다.
        public EntityCommandBuffer.ParallelWriter ECB;
        // job에서 직접 시간에 액세스할 수 없으므로 DeltaTime을 매개 변수로 전달해야 합니다.
        public float DeltaTime;
    
        // ChunkIndexInQuery 특성은 청크 인덱스를 int 매개 변수에 매핑합니다.
        // 각 chunk는 단일 스레드에서만 처리할 수 있으므로 이러한 인덱스는 각 스레드마다 고유합니다.
        // 또한 병렬 처리의 양에 관계없이 완전히 결정론적입니다.
        // 따라서 이러한 인덱스는 EntityCommandBuffer에서 명령을 기록할 때 정렬 키로 사용되므로,
        // 명령 재생이 항상 결정론적임을 보장합니다.
        void Execute([ChunkIndexInQuery] int chunkIndex, ref CannonBallAspect cannonBall)
        {
            var gravity = new float3(0.0f, -9.82f, 0.0f);
            var invertY = new float3(1.0f, -1.0f, 1.0f);
    
            cannonBall.Position += cannonBall.Speed * DeltaTime;
            if (cannonBall.Position.y < 0.0f)
            {
                cannonBall.Position *= invertY;
                cannonBall.Speed *= invertY * 0.8f;
            }
    
            cannonBall.Speed += gravity * DeltaTime;
    
            var speed = math.lengthsq(cannonBall.Speed);
            if (speed < 0.1f) ECB.DestroyEntity(chunkIndex, cannonBall.Self);
        }
    }
    
    [BurstCompile]
    partial struct CannonBallSystem : ISystem
    {
        [BurstCompile]
        public void OnCreate(ref SystemState state)
        {
        }
    
        [BurstCompile]
        public void OnDestroy(ref SystemState state)
        {
        }
    
        [BurstCompile]
        public void OnUpdate(ref SystemState state)
        {
            var ecbSingleton = SystemAPI.GetSingleton<EndSimulationEntityCommandBufferSystem.Singleton>();
            var ecb = ecbSingleton.CreateCommandBuffer(state.WorldUnmanaged);
            var cannonBallJob = new CannonBallJob
            {
                // EntityCommandBuffer의 병렬 기록기를 가져오는 데 필요한 함수 호출을 기록합니다.
                ECB = ecb.AsParallelWriter(),
                // job에서 직접 시간에 액세스할 수 없으므로 DeltaTime을 매개 변수로 전달해야 합니다.
                DeltaTime = SystemAPI.Time.DeltaTime
            };
            cannonBallJob.ScheduleParallel();
        }
    }
  5. 플레이 모드로 들어가면 대포알이 탱크에서 떨어져 땅 위로 튀어오릅니다.
블로그 이미지

RIsN

,