Study Log

Unity ECS Study Log - 7

종잇장 2023. 1. 21. 22:22

 

1. Bullet, Character 충돌 처리

foreach ((var bullet, var bulletTransform, var bulletHitLayer) in SystemAPI.Query<RefRW<BulletComponent>, RefRO<LocalTransform>, RefRO<HitLayerComponent>>())
{
    foreach ((var character, var characterTransform, var characterHitLayer) in SystemAPI.Query<RefRW<CharacterComponent>, RefRO<LocalTransform>, RefRO<HitLayerComponent>>())
    {
        if (math.distancesq(new Vector2(characterTransform.ValueRO.Position.x, characterTransform.ValueRO.Position.z), new Vector2(bulletTransform.ValueRO.Position.x, bulletTransform.ValueRO.Position.z)) <= (characterHitLayer.ValueRO.hitboxRadius + bulletHitLayer.ValueRO.hitboxRadius) * (characterHitLayer.ValueRO.hitboxRadius + bulletHitLayer.ValueRO.hitboxRadius) &&
            bulletHitLayer.ValueRO.attackLayerMask.HasFlag(characterHitLayer.ValueRO.hitLayer) == true)
        {
            character.ValueRW.HP = math.max(character.ValueRO.HP - bullet.ValueRO.Damage, 0);
            bullet.ValueRW.isDestroyed = true;
        }
    }
}

간단하게 2중으로 쿼리를 해서 만들었는데 정상 작동합니다.

 

문제는 Character와 Bullet이 늘어나면 GC문제도 프레임드랍이 발생한다는 것.

오늘은 이걸 해결해보기 위해 여러 방법을 사용해 봤습니다.

 


 

2. JobSystem으로 시도

 일단 첫 번째 문제가 발생했는데 위 코드처럼 2중반복문 처럼 돌아가려면 bullet 루프 도는 Job 하나, character 루프 도는 Job 하나 이렇게 둘로 나뉘어서 Job에서 Job을 Schedule할 수 있도록 짤 예정이었다.

 하지만, character Job으로 BulletComponent를 넘겨주고 수정이 이루어져야 하는데 이 부분을 해결하지 못해서 Job System으로는 포기함.

 어떻게든 Job System으로 하고싶은데 혹시 아는 분 댓글 남겨주세요.

 


 

3. Physics로 시도

이전에 Physics 패키지를 등록하고 Trigger이벤트를 동작시켰을 때 처럼 해보려고 시도함.

 

물론 기존 유니티에서 Rigidbody처럼 사용하려고 Freeze Position, Freeze Rotation에 체크했는데, 이게 동작하지 않아서 사실상 다른 게 잘 동작하더라도 쓸 수가 없다.

 

 

4. 코드 개선

var characterQuery = state.EntityManager.CreateEntityQuery(typeof(CharacterComponent), typeof(LocalTransform), typeof(HitLayerComponent));
var characterEntities = characterQuery.ToEntityArray(Allocator.Temp);
var characters = characterQuery.ToComponentDataArray<CharacterComponent>(Allocator.Temp);
var characterTransforms = characterQuery.ToComponentDataArray<LocalTransform>(Allocator.Temp);
var characterHitLayers = characterQuery.ToComponentDataArray<HitLayerComponent>(Allocator.Temp);
foreach ((var bullet, var bulletTransform, var bulletHitLayer) in SystemAPI.Query<RefRW<BulletComponent>, RefRO<LocalTransform>, RefRO<HitLayerComponent>>())
{
    for (int i = 0; i < characterEntities.Length; i ++)
    {
        var characterTransform = characterTransforms[characterIndex];
        var characterHitLayer = characterHitLayers[characterIndex];
        var checkDistance = math.distancesq(new float2(characterTransform.Position.x, characterTransform.Position.z), new float2(bulletTransform.ValueRO.Position.x, bulletTransform.ValueRO.Position.z)) <= (characterHitLayer.hitboxRadius + bulletHitLayer.ValueRO.hitboxRadius) * (characterHitLayer.hitboxRadius + bulletHitLayer.ValueRO.hitboxRadius);
        var checkLayer = (bulletHitLayer.ValueRO.attackLayerMask & characterHitLayer.hitLayer) > 0;
        if (checkDistance && checkLayer)
        {
            var character = characters[i];
            character.HP = math.max(character.HP - bullet.ValueRO.Damage, 0);
            state.EntityManager.SetComponentData(characterEntities[i], character);

            bullet.ValueRW.isDestroyed = true;
        }
    }
}
characterEntities.Dispose();
characters.Dispose();
characterTransforms.Dispose();
characterHitLayers.Dispose();
characterQuery.Dispose();

일단은 쿼리를 두 번 하는 게 너무 부담이라 character 쿼리는 먼저 하기로 했다.

 

var characterTransform = characterTransforms[characterIndex];
var characterHitLayer = characterHitLayers[characterIndex];

Profiler를 계속 보면서 알게 된 내용인데, NativeArray에서 값을 참조해 올 때마다 GC가 꽤 발생한다.

한번 이렇게 변수에 담아두고 쓰는 게 좋다.

 

대충 시간 기준으로는 반토막 낸 것 같다.

오늘 삽질의 경험 잊지 않는다.