ECS(Entity Component System) 튜토리얼: Step 7 - 색이 입혀진 탱크들과 대포알들(Colored tanks and cannon balls)
Unity 2022. 12. 3. 16:54Step 7 - 색이 입혀진 탱크들과 대포알들(Colored tanks and cannon balls)
고급 베이킹, 베이킹 시스템들에 대해 소개
ECS 컴퍼넌트들은 렌더링에 사용되는 쉐이더들에 대한 입력들을 제어할 수 있습니다. 자체 쉐이더를 만들고(쉐이더 그래프를 통해) 사용자 지정 ECS 컴퍼넌트를 입력에 맵핑하는 것은 이 튜토리얼의 범위를 벗어나지만, URPMaterialPropertyBaseColor라는 기존 컴퍼넌트를 사용할 예정입니니다. 이름에서 알 수 있듯이 표준 URP 머티리얼의 기본 색상을 제어할 수 있습니다.
우리의 탱크는 3개의 원시 요소(탱크, 터렛, 대포)로 구성되어 있으며, 각 엔티티에는 URPMaterialPropertyBaseColorcomponent가 추가되어야 합니다.
그렇게 하려면 Tank 프리팹을 열고 Hierarchy(탱크, 포탑, 대포)에서 SpawnPoint 트랜스폼을 제외하고 세 가지 기본 요소를 모두 선택하합니다. 렌더러가 없기 때문에 색상은 필요하지 않습니다.
세 가지 기본 요소를 선택한 상태에서 인스펙터의 "컴퍼넌트 추가" 단추를 사용하여 URPMaterialPropertyBaseColorAuthoringcomponent를 추가합니다.
- 플레이 모드로 전환하면 탱크가 완전히 검은색이 됩니다(방금 추가한 컴퍼넌트 요소의 기본값이 0,0,0,0입니다.).
- 플레이 모드에서 나가세요.
- 📝 NOTE
아래 시스템에서 사용하는 EntityCommandBuffer에는 SetComponentForLinkedEntityGroup에서 대상으로 지정할 엔티티를 지정하는 쿼리가 필요합니다.
엔티티 쿼리의 핵심은 컴퍼넌트 타입 집합들로 구성되며, 쿼리는 해당 집합과 일치하는 엔티티에 대한 필터링된 보기만 제공합니다.
엔티티 쿼리에 대한 자세한 내용은 package documentation를 참조하세요. -
using Unity.Burst; using Unity.Collections; using Unity.Entities; using Unity.Mathematics; using Unity.Rendering; [BurstCompile] partial struct TankSpawningSystem : ISystem { + // 쿼리는 OnUpdate의 즉석에서 만들어져서는 안 되므로 필드에 캐시됩니다. + EntityQuery m_BaseColorQuery; [BurstCompile] public void OnCreate(ref SystemState state) { + // Config(구성) 싱글톤이 로드되기 전에는 이 시스템을 실행하면 안 됩니다. + state.RequireForUpdate<Config>(); + m_BaseColorQuery = state.GetEntityQuery(ComponentType.ReadOnly<URPMaterialPropertyBaseColor>()); } [BurstCompile] public void OnDestroy(ref SystemState state) { } [BurstCompile] public void OnUpdate(ref SystemState state) { var config = SystemAPI.GetSingleton<Config>(); + // 이 시스템은 한 번만 실행되므로 랜덤 시드를 하드 코딩할 수 있습니다. + // 임의 상수 시드를 사용하면 동작이 결정론적으로 진행됩니다. + var random = Random.CreateFromIndex(1234); + var hue = random.NextFloat(); + // 가능한 한 서로 다른 색상을 만듭니다. + // 이 접근법의 논리는 다음 주소에서 자세히 설명합니다: + // https://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/ + URPMaterialPropertyBaseColor RandomColor() + { + // Note: 이 개념에 익숙하지 않은 경우, 이것은 "로컬 함수"입니다. + // 더 많은 정보를 얻기 위해 인터넷에서 그 용어를 검색할 수 있습니다. + // 0.618034005f == 2 / (math.sqrt(5) + 1) == 황금비율의 반대 + hue = (hue + 0.618034005f) % 1; + var color = UnityEngine.Color.HSVToRGB(hue, 1.0f, 1.0f); + return new URPMaterialPropertyBaseColor { Value = (UnityEngine.Vector4)color }; + } var ecbSingleton = SystemAPI.GetSingleton<BeginSimulationEntityCommandBufferSystem.Singleton>(); var ecb = ecbSingleton.CreateCommandBuffer(state.WorldUnmanaged); var vehicles = CollectionHelper.CreateNativeArray<Entity>(config.TankCount, Allocator.Temp); ecb.Instantiate(config.TankPrefab, vehicles); + // EntityQueryMask는 EntityQuery에 의해 특정 엔티티가 선택 되었는지 여부를 효율적인 테스트를 제공합니다. + var queryMask = m_BaseColorQuery.GetEntityQueryMask(); + foreach (var vehicle in vehicles) + { + // 모든 프리팹의 근본에는 해당 엔티티의 모든 목록인 LinkedEntityGroup이 포함됩니다. + ecb.SetComponentForLinkedEntityGroup(vehicle, queryMask, RandomColor()); + } state.Enabled = false; } }
- 플레이 모드로 들어가면 탱크들이 무작위 색상으로 지정됩니다.
- 플레이 모드에서 나가세요.
- "Scripts/Authoring" 폴더에 있는 "CannonBallAuthoring.cs" 파일의 내용을 다음과 같이 수정합니다:
-
using Unity.Entities; using Unity.Rendering; class CannonBallAuthoring : UnityEngine.MonoBehaviour { } class CannonBallBaker : Baker<CannonBallAuthoring> { public override void Bake(CannonBallAuthoring authoring) { AddComponent<CannonBall>(); + AddComponent<URPMaterialPropertyBaseColor>(); } }
- 다음과 같이 "Scripts/Aspects" 폴더에 있는 "TurretAspect.cs" 파일의 내용을 수정합니다:
-
using Unity.Entities; using Unity.Mathematics; using Unity.Rendering; readonly partial struct TurretAspect : IAspect { readonly RefRO<Turret> m_Turret; + readonly RefRO<URPMaterialPropertyBaseColor> m_BaseColor; public Entity CannonBallSpawn => m_Turret.ValueRO.CannonBallSpawn; public Entity CannonBallPrefab => m_Turret.ValueRO.CannonBallPrefab; + public float4 Color => m_BaseColor.ValueRO.Value; }
- 다음과 같이 "Scripts/Systems" 폴더에 있는 "TurretShootingSystem.cs" 파일의 내용을 수정합니다:
-
[BurstCompile] partial struct TurretShoot : IJobEntity { [ReadOnly] public ComponentLookup<LocalToWorldTransform> LocalToWorldTransformFromEntity; public EntityCommandBuffer ECB; void Execute(in TurretAspect turret) { var instance = ECB.Instantiate(turret.CannonBallPrefab); var spawnLocalToWorld = LocalToWorldTransformFromEntity[turret.CannonBallSpawn]; var cannonBallTransform = UniformScaleTransform.FromPosition(spawnLocalToWorld.Value.Position); cannonBallTransform.Scale = LocalToWorldTransformFromEntity[turret.CannonBallPrefab].Value.Scale; ECB.SetComponent(instance, new LocalToWorldTransform { Value = cannonBallTransform }); ECB.SetComponent(instance, new CannonBall { Speed = spawnLocalToWorld.Value.Forward() * 20.0f }); + // 아래의 줄은 포탑에서 대포알까지 색을 전파합니다. + ECB.SetComponent(instance, new URPMaterialPropertyBaseColor { Value = turret.Color }); } }
- 플레이 모드로 들어가면 대포알이 생성된 탱크와 같은 색을 갖게 됩니다.
- 플레이 모드에서 나가세요.
Reference
- 맵핑에 관해서
- https://www.ibm.com/docs/ko/developer-for-zos/14.2?topic=pli-introduction-mapping-concepts