참고 : https://learn.microsoft.com/ko-kr/dotnet/api/system.linq.enumerable.oftype

 

OfType 

실행 쿼리문 메소드
지연된 실행 없음 OfType

사용예시

필터결과 = 집합.OfType<클래스>()

- 집합내의 아이템들 중 클래스에 해당하는 타입의 아이템들을 뽑아낸다.

 

OfType은 메소드로만 존재하고, 쿼리문은 없습니다.

 

예제 및 활용

기본적인 예

    class Animal
    {

    }
    class Dog : Animal
    {

    }
    class Cat : Animal
    {

    }
    List<Animal> animals = new List<Animal>()
    {
        new Animal(),
        new Dog(),
        new Cat()
    };
    
    // 주인공 OfType
    var res = animals.OfType<Dog>();
    
    
    // 아래는 위 OfType과 동일한 로직을 Where과 일반 문법으로 표기한 겁니다.
    // where절일 때,
    var res = animals.Where(item => item.GetType() == typeof(Dog));

    // 일반적인 문법일 때,
    foreach(var item in animals)
    {
        if(item.GetType() == typeof(Dog))
        {
        }
    }

 

Animal을 상속받은 클래스 중에 Dog 클래스만을 필터링하는 코드입니다.

 

object가 최상위 클래스니까 object List에 여러 클래스를 담아서 원하는 클래스를 뽑아서 쓸 수도 있겠지만,, 그렇게 쓰는 곳이 있을런지 의문이네요.

 

특정 클래스를 상속받아 하나의 리스트에 담는 경우는 흔합니다.  같은 클래스를 상속받아 함수를 클래스별로 다르게 구현하고, list 루프를 돌면서 실행시키는 식으로 많이 쓰잖아요.

예를 들어, 모든 몬스터 (오크, 스켈레톤, 뱀파이어 등)를 이동시키고자 하는데, 다 이동범위가 제각각이라면, 최상이 클래스에 이동 함수를 구현하고, 각 몬스터마다 상속받아 이동범위를 다르게 구현하겠죠.

 이 때 오크들만 뽑아서 잠깐 스톱시키고 싶다! 이럴 때 OfType으로 오크 클래스만 필터링해서 Stop 시킬 수 있겠네요.

 

다만, OfType은 Where절로도 구현이 가능합니다.

성능을 한번 PS에서 비교해보겠습니다.

 

PS. 성능 비교해보기.

1. foreach 와 Where절, OfType  비교하기.

 코드 :

    private void Start()
    {
        for(int i = 0; i < 100000; i++)
        {
            animals.Add(new Animal());
            animals.Add(new Dog());
            animals.Add(new Dog());
            animals.Add(new Cat());
            animals.Add(new Cat());
            animals.Add(new Cat());
        }
        Debug.Log("List count " + animals.Count);
    }

    public void OnClick1()
    {
        System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
        sw.Start();

        foreach (var item in animals)
        {
            if (item.GetType() == typeof(Dog))
            {
            }
        }

        sw.Stop();
        Debug.Log($"foreach Time : {sw.ElapsedMilliseconds}ms");
    }
    public void OnClick2()
    {
        System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
        sw.Start();


        var res = animals.Where(item => item.GetType() == typeof(Dog));

        foreach(var item in res)
        {
        }

        sw.Stop();
        Debug.Log($"Where Time : {sw.ElapsedMilliseconds}ms");

    }

    public void OnClick3()
    {
        System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
        sw.Start();

        var res = animals.OfType<Dog>();

        foreach (var item in res)
        {
        }

        sw.Stop();
        Debug.Log($"OfType Time : {sw.ElapsedMilliseconds}ms");
    }

 

결과:

상세 프로파일링 결과:

Foreach:

 Where:

 OfType:

결론:

foeach-if보다 where이 1.5배 정도 느리고, OfType 이 where보다 1.5배 정도 느리네요.

당혹스럽네요... Where로도 할 수 있는 걸 따로 메소드를 뺏길래 더 특화되서 좋은 건가? 했는데, 더 안좋네요?

뭔가 별도의 사용 용도가 있는 걸까요? 모르겠네요.

 

이전 where 절에서 테스트 했을 때는 foreach-if가 20~40배 가량 압도적으로 빨랐는데, 이번엔 1.5배가 나왔네요. 상황에 따라 다르긴 하지만 foreach-if가 Where절보다 빠르단 건 변하지 않았네요

 

(제대로 Type  필터 로직이 돌았는지는 count()를 통해 각각 200000의 결과가 나온건 확인했습니다. 프로파일링에서도 확인 할 수 있습니다.)

 

최종 결론:

 - foreach-if (일반 문법) > Where > OfType 순으로 속도가 빠릅니다. ( OfType이 제일 느리고, 각각 1.5배 정도 이상의 차이가 난다.)

 - OfType 전 안쓸랍니다...

 

 

 

 

테스트 환경

  • cpu - i5-11400F
  • gpu - GeForce RTX 3060
  • Unity 2021.3.13f1
  • VisualStudio 2022
Posted by 검은거북

ECS 개요는 알겠는데, 대체 얼마나 빨라지길래 스크립팅 방식을 바꿔가면서까지 제공을 하는 걸까요?
그래서 한 번 유니티에서 제공해주는 샘플앱을 이용해 간단하게 벤치마크 테스트 해보았습니다.

 

* 아래 결과는 제 컴퓨터에서 18년 12월 28일 기준으로 수행된 결과입니다.

  * 사양 - i5-6600 / GTX 970 / Ram 15GB

* 실행 프로젝트는 유니티 ECS 샘플 프로젝트의 TwoStickShooter 입니다.

* job system과 burst compiler를 고려하지 않고, 단순히 오브젝트 리젠 갯수를 늘린 결과입니다.

 

* 오브젝트 무한리젠 ( 매 업데이트(0.02)마다 오브젝트 10개씩 생성)

* Classic

Profiler
Game Play Scene

 - 약 30FPS 아래로 내려갈 때의 프로파일링 결과입니다.

 - 총 오브젝트 갯수가 6800개를 넘어가자 어마어마하게 끊깁니다.

 

* hybrid ECS

- 약 30FPS 아래로 내려갈 때의 결과입니다.

- 총 오브젝트 갯수가 21000개 정도 이상되니 끊기네요

- 특이한 점은 classic은 끊기다가 다시 조금 회복되다 또 끊기는 반면에 Hybrid는 더더더 느려집니다.

 

*Pure ECS

- 30 FPS 이하로 떨어지질 않아요 하단 이미지가 실행중인 이미지인데 저 상태에서도 굉장히 부드럽습니다.

- 다만 특이한 점은 총 오브젝트 갯수가 균일하게 약 2000개 밖에 안 찍힙니다. 화면상으로만 봐도 몇 만개는 되보이는데 말이죠. ( 이 부분에 대해서는 좀 더 ECS 시스템에 대한 공부가 필요할 듯하네요)

 

 

 

 

번외로 Pure ECS가 30 FPS 이하로 떨어지는 구간이 대체 언제쯤일지 테스트를 해보았는데, 매 업데이트(0.02초)마다 적 오브젝트를 50개씩 만드니 아래와 같이 떨어지긴 하더군요 (참고로 적오브젝트는 계속 총알 오브젝트를 날립니다)

 

Result

- 거의 성능 차이가 classic을 1이라 했을 때, hybrid가 3, pure가 5~10 정도로 차이가 나는 듯 보이는 것 같습니다.

 

* 다음 포스팅부터는 hybrid ECS를 사용하는 방법 및 사용하면서 발생했던 오류 위주로 포스팅 시작하도록 하겠습니다.

 

 

* 유니티 샘플 프로젝트 경로를 같이 올리려했는데 반년 사이에 많은 변화가 있었는지 git 저장소에 TwoStickShooter를 비롯해 제가 가진 예전 샘플들이 없어졌더군요 ; (너무 바뀌어서 같은 프로젝트인지도 확실치 않지만 최근 샘플을 공유드립니다.)

 - git https://github.com/Unity-Technologies/EntityComponentSystemSamples

* 글을 쓰는 날(19.5.19)을 기준으로는 ECS 벤치마크 데이터가 빈번하게 검색되어 나오는 것 같네요. 더 궁금하시면 영문으로 찾아보시면 만족할만한 자료가 나올 듯 합니다.

 

* 개인이 공부하며 하는 포스팅이기에 잘못된 부분이 있을 수 있습니다.

 혹시 잘못되거나 문제시 되는 부분이 있으면 지적 부탁드립니다.

Posted by 검은거북

* 작년부터 개인 프로젝트를 진행하며 공부한 내용을 정리하는 포스팅입니다.

 

ECS란 무엇인가

Entity - Component - System 의 줄임말로, 각 용어에 대해 간단히 설명하자면 아래와 같습니다.

 

  • Component  - 데이터 집합
    • 요소를 이루는 데이터 집합
    • ex
      • position - 위치의 데이터만 가짐 (x,y,z)
      • rotation - 방향의 데이터만 가짐 (x,y,z)
  • Entity  - 컴포넌트 집합
    • 하나의 오브젝트를 이루는 데이터 집합의 집합 
    • ex
      • character - position, rotation 컴포넌트를 가짐.
  • System - 데이터들을 가공하는 작업 ( method 개념)
    • ex
      • MoveSystem - position, rotation 컴포넌트를 가진 엔티티들을 대상으로 키 입력시 position의 데이터를 변경하기만 한다.

 

기존의 객체 지향 개념(OOP) 이 아닌 데이터 지향 방식(DOD)의 개념으로, 데이터 지향은 저장되는 데이터의 위치를 집적시켜 캐시 히트율을 높여 속도를 끌어올리는 목적의 개념으로 알고있습니다.

 

 * 데이터 지향 부가 설명 및 운영체제 지식

  - 프로그램에서 데이터 필요시 CPU 캐시공간을 먼저 검사하고, 거기 없으면 메모리(램)에서 해당 데이터를 가져와서 사용합니다. (메모리에서 가져올 때는 필요 데이터 주변의 데이터를 뭉태기로 캐시에 가져옵니다. 그 주변 데이터를 다음에 사용할 확률이 크기때문이죠) 이때 CPU 캐시에서 바로 가져오느냐, 메모리에서 가져와서 사용하느냐의 속도차이는 아마 몇 십배일거에요. 즉, 캐시에 미리 사용할 데이터가 없을 수록 속도는 몇십배씩 느려진다는 거죠.  그래서 데이터 지향 방식은 데이터들을 한 곳에 몰아 넣어서 캐시 적중률을 늘려 속도를 올릴 수 있는거죠. (객체 지향은 객체에 따라 데이터가 위치가 분산될겁니다.) 

 

Unity Scripting 방법

* 현재 unity에서 제공하는 방식은 3가지 입니다.

  1. Classic ( 현재 일반적으로 사용하는 컴포넌트 방식입니다.)
  2. Hybrid ECS 
    • Component + ECS 방식입니다.
    • 에디터상의 작업은 기존의 Classic 방식을 따르고, 코딩은 ECS 방식으로 하는 형식입니다. 아마 ECS의 진입장벽을 낮추고, ECS의 속도면 이점도 어느정도 갖추기 위해 만들어졌을거 같네요.
  3. Pure ECS
    • 완전한 ECS 방식입니다.
    • 기존의 Classic 방식과는 객체 생성및 등록 방식이 확 바뀝니다. 거의 모든 걸 스크립트상에서 처리하기에 진입장벽이 많이 높습니다.

 

장점 & 단점

  • 장점

    • 재사용성이 높다. / 상호 의존도가 낮다
      • 위 부분은 객체지향의 장점에도 포함되지만 개인적으로 사용 경험상, 데이터 지향 방식이 더 활용도가 높다고 느껴집니다.
      • 데이터 지향의 시스템은 자기가 필요한 엔티티 집합을 검색해 로직을 돌리는 방식이고, 엔티티는 시스템의 검색에 찾아지도록 필요 컴포넌트들을 포함시켜 만들어진 집합입니다. 즉, 특정 엔티티가 특정 시스템에 돌아가게 하고싶다면, 시스템이 필요로하는 컴포넌트만 엔티티에 추가만 하면 끝인거죠.
        • 이런 방식으로 시스템과 컴포넌트를 구성해놓으면 엔티티는 자기가 원하는 것만 골라 재사용하기 용이하게 느껴집니다.
        •  그리고 실제 설계 / 코딩을 할 때도 필요한 컴포넌트를 개별로 시스템에서 가져다 사용하기때문에 다른 시스템/ 엔티티에 영향도가 적습니다. (상호 의존성 낮음)
    • 유지보수가 용이하다.
      • 위의 장점과 일맥상통합니다.
        • 시스템 별로 필요 엔티티만 집약되어있기에 시스템별로 찾고 고치기가 쉽습니다. 
    • 실행 속도가 빠르다
      • 네, 엄청 빠릅니다. (이건 다음 포스팅에서 검증하도록 하겠습니다)
  • 단점

    •  진입장벽이 많이 크다.
      • Pure가 특히 높습니다. Hybrid는 기존의 방식을 어느정도 쓰기때문에 사실 ECS 개념만 좀 숙달되면 금방하는데, Pure는 스크립트로 거의 모든 걸 진행하여 에디터 UI상의 이점을 많이 뺏깁니다.
    •  디버깅이 힘들다.
      • Pure의 단점으로, 위의 단점과 일맥상통합니다.
      • Pure는 에디터상의 하이러키창에 오브젝트가 안나옵니다. 그래서 하이러키창을 자주 활용하여 디버깅하시는 분은 꽤 당황스러울 수 있습니다.
    • 베타 버전 (개인적으로 최고 단점으로 뽑습니다.)
      • Unity에서 기존에 제공하던 컴포넌트들을 직접 구현해야 할 수 있습니다. 
        • 일부 컴포넌트가 ECS형태로 아직 제공이 안됩니다 ㅠㅠ  자주 사용하는 Physics, animation관련 컴포넌트도 없는게 꽤 있습니다.

 

저는 아직 Pure ECS는 많이 어색하고 어렵더군요. 그래서 앞으로의 포스팅은 Hybrid ECS 위주로 진행하겠습니다. 애초에 개인 프로젝트도 Hybrid ECS를 이용해 만들기도 했거든요.

 

다음 포스팅은 유니티에서 제공해주는 샘플 프로젝트를 이용해 실험해본 간단한 성능 벤치마크를 하도록 하겠습니다.

대체 얼마나 기존에 비해 성능이 빨라질지 간단하게 실험한 결과입니다.

 

* 개인이 공부하며 하는 포스팅이기에 잘못된 부분이 있을 수 있습니다.

 혹시 잘못되거나 문제시 되는 부분이 있으면 지적 부탁드립니다.

Posted by 검은거북

폭발시 카메라 흔들림 효과 스크립트


흔들림 효과를 넣고 싶은 카메라에 스크립트 추가하여 사용


    
    Vector3 originPos;
    
    void Start () {
        originPos = transform.localPosition;
    }
	
    public IEnumerator Shake(float _amount,float _duration)
    {
        float timer=0;
        while(timer <= _duration)
        {
            transform.localPosition = (Vector3)Random.insideUnitCircle * _amount + originPos;

            timer += Time.deltaTime;
            yield return null;
        }
        transform.localPosition = originPos;

    }



참고 링크 : https://gist.github.com/ftvs/5822103


Random.insideUnitCircle - 반경 1의 원 내부의 랜덤한 값을 vector로 반환하는 함수

 https://docs.unity3d.com/kr/530/ScriptReference/Random-insideUnitCircle.html



ShakeCamera.cs

Posted by 검은거북
이전버튼 1 이전버튼

블로그 이미지
프로그래밍 공부 요약 및 공부하며 발생한 궁금증과 해결과정을 포스팅합니다.
검은거북

공지사항

Yesterday
Today
Total

달력

 « |  » 2025.1
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

최근에 올라온 글

최근에 달린 댓글

글 보관함