Step 5 - 대포알 이동(Cannon ball movement)
병렬 job에 대한 소개
- "Scripts/Aspects" 폴더에 "CannonBallAspect.cs"이라는 이름의 새 C# 소스 파일을 만들고 다음 내용을 저장합니다.
-
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; } }
- "Scripts/Systems" 폴더에 "CannonBallSystem.cs"이라는 이름의 새 C# 소스 파일을 만들고 다음 내용을 저장합니다.
-
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(); } }
- 플레이 모드로 들어가면 대포알이 탱크에서 떨어져 땅 위로 튀어오릅니다.