Post

[ STUDY/유니티 ] 2023. 1. 22. 15:57

null 체크를 하기위해서 다양한 방법들을 알아보았습니다.

그러던 중 신기한 것을 발견하였죠.

 

바로 같은역할을 하는 친구들이 모두 속도가 다르단 것입니다!

is, Equals ,==

(ReferenceEquals의 경우에는 is와 속도가 거의 같아서 is로 대체하였습니다.)

 

테스트코드

테스트 코드

 void Start()
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        Test test1= TestClass1;

        for (int i=0;i<100000000;i++)
        {
            if ( test1 == null);
        }
        sw.Stop();

        UnityEngine.Debug.Log("Object == null : " + sw.Elapsed);
    }

일단 앞선 게시물에서 is가 ==보다는 왜 빠른지 알았습니다.

근데 Equals(object,object).. 이 친구는 ==보다는 빠른데, is보다는 느립니다..

어떤 원리일까요? 좀 더 안전하게 속도를 빨리 할 수 있는걸까요?

 


 

일단 FakeNull을 체크할 수 있는 지 확인해봅시다.

IEnumerator Start()
    {
        GameObject test = new GameObject();
        yield return null;


        Destroy(test);
        yield return null;

        Debug.Log("UnityEngine Null check : " + NullCheckUnityEngine(test));
        Debug.Log("System Null check : " + NullCheckSystem(test));
    }

    string NullCheckUnityEngine(Object obj)
    {
        if (obj == null)
            return "true";
        else
            return "false";            
    }
    string NullCheckSystem(object obj)
    {
        if (obj == null)
            return "true";
        else
            return "false";
    }

일단, ==은 fake null체크를 진행합니다. 그 결과 true, false로 나오죠.

 

 

Equals(,)의 경우에는 어떨까요?

IEnumerator Start()
    {
        GameObject test = new GameObject();
        yield return null;


        Destroy(test);
        yield return null;

        Debug.Log("UnityEngine Null check : " + NullCheckUnityEngine(test));
        Debug.Log("System Null check : " + NullCheckSystem(test));
    }

    string NullCheckUnityEngine(Object obj)
    {
        if (Equals(obj, null))
            return "true";
        else
            return "false";            
    }
    string NullCheckSystem(object obj)
    {
        if (Equals(obj, null))
            return "true";
        else
            return "false";
    }

이런.. fake null까지 체크하진 않군요.

 

실제 찾아보니 ==의 경우에는 UnityEngine.Object와 정의가 연결되어 있고

Equals나 ReferenceEquals 등 fakenull 을 체크하지 않는 친구들은 System.Object에 정의되어있습니다.

 

Equals(object, null)같은 경우는 fakenull을 체크하지 않아 연산에서 좀 더 시간이 빠르단걸 알게되었습니다.

그럼 왜 is나 ReferenceEquals보다는 느린걸까요??

 


둘다 시스템 상 null만 체크하는건 동일하지만

 Reference.Equals(object, object)의 경우에는 두 개체의 참조만을 비교합니다.

참조만을 비교하니 서로 가리키는 것이 무엇인지만 파악하면 되기에 속도가 빠른것같습니다.

 

object is null의 경우에는 상수 또는 Null만을 비교함으로써

두가지의 경우만 있기에 좀 더 속도가 빠르게 진행되는것같습니다.

 

Equals(object,null) 의 경우에는 두개의 값 모두 null인지를 먼저 체크 한 다음에 값의 비교를 진행합니다

null인지도 체크하고, 그 다음에 값이 같은지까지 체크하니 좀 더 속도가 걸리는것같습니다.

 

그래서 결론적으로 속도차이는

object.Reference.Equals(object,null) = object is null  >  Equals(object,null)  >  object == null

이렇게 결론이 나게됩니다.

 

 

자세한 함수의 코드는 잘 모르지만 나와있는 문서의 정보라던가 사람들의 이야기를 들어보고 정리한 결과,

해당 함수가 어떤 기능을 수행하는지를 알게되어 속도가 왜 차이가 나는지에 대해서도 어느정도 이해가 되었습니다. 

 

하지만 무조건 제 말이 옳은건 아닙니다.

저도 단지 원리를 보고 파악하는것이기때문에 피드백이 있다면 언제든지 환영입니다!

 

이걸로 null에 대한 여정 끝!  

 

 

참고자료

https://stackoverflow.com/questions/34895925/difference-between-object-equalsobject-object-and-object-referenceequalsobjec

 

difference between Object.Equals(object,object) and Object.ReferenceEquals(object,object) in c#

I have asked by an Interviewer in an Interview that "difference between Object.Equals(object,object) and Object.ReferenceEquals(object,object)". I have tried in code snippet but the result is same.

stackoverflow.com

https://learn.microsoft.com/ko-kr/dotnet/csharp/language-reference/operators/is

 

'is' 연산자 - 식과 형식 또는 상수 패턴 일치

패턴에 대한 식과 일치하는 C# 'is' 연산자에 대해 알아봅니다. 식이 패턴과 일치하면 'is' 연산자는 true를 반환합니다.

learn.microsoft.com

 

Post

[ STUDY/유니티 ] 2023. 1. 21. 21:58

이전에 null 비교를 위한 연산으로 ==와 Equals를 보던 중 같은 기능을 수행하는  is라는 연산자를 발견하였습니다.

그럼 is란 무엇인가?

여기서 is란?

연산자로써 C#7 이전에는 객체의 타입을 체크하는 용도로 쓰였지만,

C#7 이후부터는 패턴으로써 사용할 수 있게되었습니다. 

 

패턴은 크게 상수패턴/ 타입패턴 / var 패턴으로 나뉘어져있고

여기서 이번 게시물과 밀접한 관련이 있는 패턴은 상수패턴입니다.

 

상수패턴은 특정 상수값을 사용해 패턴을 체크함으로써 ==과 같은 기능을 합니다.

(instance is null 과 instance==null은 같게 결과가 나옴.. 처럼 보이지만 내막이 있는데...)

 

하지만 기능이 같다고 아예 같다란 것은 아닙니다.

제가 본 글들은 is가 ==보다 속도면에서 우수하다고 하는데

그 이유를 제가 자세히 몰라  왜 그런걸까 한번 알아봤습니다.


먼저, 유니티에는 두가지 오브젝트가 있습니다.

 

하나는 System.Object이고, 다른 하나는 UnityEngine.Object입니다.

이 두개가 나뉘는 이유는 유니티 엔진은 C++로 만들어져있습니다.

다만, 유니티에서 .NET API를 노출하여 우리는 C++대신에 C#을 이용해 개발을 할 수 있는거죠.

 

System.Object(=object)를 통해 시스템상 오브젝트(C#)를 확인하고,

UnityEngine.Object(=Object)를 통해 유니티 엔진상 오브젝트(C++)을 확인할 수 있습니다.

 

이 오브젝트들은 평소에는 신경을 쓰지 않고 개발을해도 크게 문제는 안되지만

Destroy를 할 경우 문제가 발생하게 됩니다. 

 

C++에서는 메모리를 동적할당/해제가 가능하지만, C#에서는 메모리를 사용자가 할당하고 해제할 수 없죠.
그런 상황에서 Destroy를 했을 경우에 속에 C++로 이루어진 시스템상 오브젝트는 NULL 이 되지만
C#으로 겉모습을 유지하고있는 오브젝트는 GC가 호출되어 메모리를 정리할때까지 NULL이 아니게됩니다.
유니티 엔징 상(C++)에서는  NULL이지만 시스템 상(C#)으론 NULL이 아닌 이 현상을 Fake Null이라고 합니다.


이런 Fake Null 현상을 잡아내기 위해

UnityEngine.Object 클래스에서는 같음연산자(==)를 오버로딩하여

네이티브 객체가 존재하는지 여부까지 판단하여 비교 후 결과를 돌려줍니다. 

 

하지만  is의 경우에는 (ReferenceEquals함수도 마찬가지)

실재여부 판단까지 하지않고 값만 판단하기 위해서
원시 오브젝트 자체로 비교하여 null check 비교과정을 생략하여 속도를 향상시킵니다.

 


그럼 속도가 빠른 is가 마냥 좋은걸까요?

그건 아닙니다.

 오브젝트가 Destroy가 되는 상황이 아예 없을 경우에는 is 나 ReferenceEquals가 속도면에서 좋을 수도 있지만
 그런 보장된 상황이 아니라면 좀 더 안정적으로 추가검증을 해주는 == 로 체크하는것이 좋을 것입니다.

 

무조건 빠르다고 쓰는것이 아닌

적재적소에 맞게 사용하면되는것이 결론!

 

 

 


 근데.. 솔직히..NULL체크 자주 쓰이는것도 아니라면..굳이?

 

 

 

 

 

 

Post

[ STUDY/유니티 ] 2022. 9. 10. 18:00

메지카복셀프로그램에서 오른쪽 하단에 Export를 눌러줍니다

 

 

그런다음에 obj를 눌러줍니다

 

obj파일을 저장한 경로로 가면 이렇게 3개의 파일들이 존재합니다.

 

이 친구들(3파일 모두)을 유니티 프로젝트 폴더안, Assets밑에 원하는경로에다가 넣어줍니다. 

 

씬에다가 넣어줍니다. 그럼 캐릭터가 나옵니다!

 

저는 position에서 y=-0.3 정도를 하니까 바닥에 착 달라붙더라구여 참고참고~.~

 

Post

[ STUDY/유니티 ] 2022. 8. 2. 18:38

UniRx는 유니티에서 캐릭터가 목표지점에 도착했을때를 알리기위해서 어떻게 해야하는지를 검색해보다가 접하게된 라이브러리입니다.

 

UniRx는 .NET Reactive Extensions를 유니티에 맞게 다시 구현한 것으로,

여기서 RX는 비동기 프로그래밍과 Observable 시퀀스를 이용해 이벤트를 처리하기위한 라이브러리입니다. 

 

RX(ReactiveX)

: observer 패턴 + iterator 패턴 + functional 프로그래밍 의 조합이라고하는데

ㅎㅎㅎㅎ 정말 말만 봐도 어렵네요! 더 자세하게 봅시다!

 

observer패턴은 

- 관찰차패턴으로써 특정 이벤트가 일어나는것을 감시

 

iterator 패턴은

- 집합체안에 들어있는 모든 항목에 접근할 수 있게 해 주는 방법을 제공

- 집합체 내에서 어떤 식으로 일이 처리되는지 몰라도 그 안에 들어있는 항목들에 대해서 반복작업을 수행

 

functional 프로그래밍

보통 우리가 하는 프로그래밍은 "명령형"프로그래밍으로써
함수를 너는 이렇게하고, 너는 저거하고 이거랑 저거 섞어서 이런 결과물을 내놓아라!라고 한다면..

<기계에 카카오 100kg을 넣고 그 다음 분쇄기계에서 갈고 그 다음에는 .... 마지막에 초콜릿을 예쁜모양으로 만들어낸다>

함수형 프로그래밍은 "선언형"프로그래밍이라서

함수를 이것은 이거다 라는  "행위가 아닌"  "값"으로 보고 프로그래밍을 하는느낌입니다.

<기계에 카카오 100kg양을넣고 그만큼의 초콜릿을 만들어낸다(방법을 적지않고 목표만 명시)>

 

이렇게 얘기하면 잘 감이 안올 수 있는데...코드를 통해 비교해보면 좀 더 쉽게 이해가 될 수 있습니다.

//명령형으로 쓸 경우(비함수형 코드)
var filtered = [];
for (var i=0; i<students.length; i++)
	{
		if(stduents[i].division == "이과")
				filtered.push(students[i]);
	}
filtered = filtered.splice(0,3);
var result =""
for(var i=0;i<filtered.length;i++)
	{
		result += '${filtered[i].name}(${filtered[i].major})';
	}

console.log(result);
//선언형으로 쓸 경우(함수형 코드)
consol.log(
	_(students)
	.filter((i) => i.division == '이과')
	.take(3)
	.map((i) => '${i.name}(${i.major})')
	.reduce((sum,i) => sum+" " +i)
)

위 두 코드는 모두 학생들 중에서 이과인 3명의 학생의 이름과 전공을 출력하는 형태를 띄고있습니다.

함수형 언어나 프로그래밍을 위한 라이브러리들에는 컬렉션 내 요소들을 다양하게, 연속적으로 처리할 수 있는 다양한 도구들이 존재하고, for문을 돌려가며 직접 변수를 선언하고 하나하나 설계했던것들을 이제는 도구들의 작동과 사용법을 익혀놓았다가 적재적소에 갖다붙이기만하면 되는거죠!

 

이러한 프로그래밍을 한다면 장점으로써 아래와 같은것들이 있습니다.

- 우리가 흔히 쓰던 방식이 아닌 새로운 방식이기때문에 다양한 사고방식을 얻을 수 있음 (사고의 전환)

- 사용하는 모든 데이터가 변경 불가능(immutable)하고 함수는 부수 효과를 가지고 있지 않기 때문에 외부환경으로부터 독립적이므로 동시성과 관련된 문제를 원천적으로 차단

- 함수는 입력과 그에 대한 출력만을 책임지기 때문에(순수함수) 테스트가 쉽고 가독성이 좋아 깔끔하고 유지보수가 용이

(부수효과 : 한 변수의 값이 변할 시, 다른 변수의 값도 변하게되는 등의 의도하지않은 상태가 나타나는것)

 

 

함수형 프로그래밍에서 말이 조금 길어졌는데,이러한 코드문법을 이용해서 Async Event(비동기 이벤트) 와  Observer 디자인패턴의 통지 처리를 이용해 미리 이런 조건이 발생하면 이런 처리를 하라고 지시를 내려놓고 그 지시가 통과된 시점에서만 통지를 받는 형태인 Rx라이브러리를 이용하여서 위와같은 함수형프로그래밍을 거의 모든언어에서 적용할 수 있습니다. 문법도 다들 비슷하구요!

 

그리고 UniRx는 이런 Rx라이브러리를 유니티 C#에 최적화되어있습니다.

 

UniRx 사용법

유니티 에셋스토어 or 깃허브에서 다운이 가능합니다

 

UniRx가 쓰이면 좋은점 

- 시간을 간단하게 취급한다

- GUI구현을 간단히 쓸 수 있다

  UI 반응이나 로직 상에 특정 변수 값이 변경되는 순간의 처리

- event의 상위호환으로써 event에 비해 유연한기술을 사용할 수 있다

  더블 클릭 판정이나 유저의 입력 시간 제한 등의 일정 시간동안 대기 및 체크 해야 하는 이벤트

  기존 유니티 event의  준비단계나 sendMessage등의 통지구조보다 간단하다

- update문을 없앨 수 있다

  작업별로 병렬로 늘어서 작성가능하다. (처리의도를 알기쉽다, 기능 추가/변경/제거가 용이)

  복잡한 로직을 operator 조합으로 구현가능하다

  자원관리가 탁월하다. ( Observe하는 Stream을 열어두기만하면 된다)

 

==> 종합적으로 보았을때, 작성을 간단하게 해주고 봤을때 이해하기쉽게 한다는 프로그래머가 개발코드를 짤때 편리함이 매우 높다라는 장점이 있습니다. 어떤 새로운기능! 신박한기능! 보다는 기존의 코드를 간결하게! 좀 더 쉽게! 구현이 가능!

 

 

자아 좋은점도 다 보고 어떤건지 간단하게 알아봤는데, 과연 나쁜점은 없을까요???

UniRx단점

- 학습 코스트가 높고 개념적으로도 어렵다

  데이터 질의기능(연산자)인 링큐(LINQ)를 사용하기때문에 난이도가 높아진다.

  흔히 쓰던 명령형프로그래밍이 아니기때문에 선언형 프로그래밍관점으로 보아야한다

- 도입하는 경우에는 프로그램의 설계부터 다시 생각할 필요가 생긴다

  라이브러리가 아닌 언어확장이라고 생각해야한다.

- 스트림의 수명관리가 중요하다

  스트림이 생성되면 스트림에 설정한 종료 조건을 모두 충족하기 전까지는 끝나지 않기때문에 퍼포먼스 저하나 에러에 의한 오동작이 일어날 수 있다

 

 

 

알아보자마자 끝난것같긴한데ㅎㅎ 글을 쓰면서 이해가 안가는 부분들, 적용할지말지 고민하면서 찾아본 좋은 사이트들을 밑에 링크걸어두겠습니다. :D 다들 너무 글을 잘쓰셨어요!!! 

 

 

 

 

 

 

 

----------------------------------------참고-----------------------------------------

https://www.slideshare.net/agebreak/160402-unirx

 

[160402_데브루키_박민근] UniRx 소개

2016.04.02 차세대 프로그래밍 패러다임을 유니티에서 Reactive Programming in Unity 초중급 게임 개발자 스터디 데브루키 발표 자료.

www.slideshare.net

위 링크는 개념적으로 이해하기가 정말 좋았습니다 :D

되게 제 눈높이에 맞춰서 잘 알려주셔서 가독성이 너무 좋고 술술 넘어갑니당

 

https://skuld2000.tistory.com/31

 

[UniRx 입문 강좌 1] 개념 및 기본 사용법 소개

[UniRx 입문 강좌 2] UniRx 의 핵심, Subject 와 Observable 사용 방법 [UniRx 입문 강좌 3] IObserver 메세지 종류와 스트림의 수명 관리 [UniRx 입문 강좌 4] Operator 활용(1) - Where & Select & SelectMany..

skuld2000.tistory.com

https://tech.lonpeach.com/2019/11/02/UniRx-Getting-Started-1/

 

UniRx 입문 1 - Lonpeach 기술 블로그 | Lonpeach Tech

환경

tech.lonpeach.com

https://rito15.github.io/posts/unity-study-unirx/

 

유니티 - UniRx (Reactive Extensions for Unity)

개요

rito15.github.io

간단하게 필요한 설명만 딱딱 적어주셔서 읽기 좋았습니다

 

https://nidelva.tistory.com/27

 

Unity :: UniRx 라이브러리 사용기

 사실 나는 새로운 시스템 혹은 라이브러리의 도입/사용을 굉장히 꺼려하는 타입이다. NGUI보다 UGUI를 선호하며, IDE는 확장기능 없이 순정만을 선호하고, 단축키를 바꾸는 일도 드물다. 코딩에

nidelva.tistory.com

이 분의 블로그는 당시에 직접 코드를 짤때 이해를 쉽게 해주었습니다!

 

https://www.youtube.com/watch?v=jVG5jvOzu9Y 

함수형 프로그래밍에 대해서 이해를 정말 쉽고 간단하게 해주었습니다 ㅎㅎ 위 예시들도 영상에서 많이 참고했습니다

 

https://tistory.jeon.sh/42

 

Unity: UniRx로 NavMeshAgent의 도착 콜백 생성하기

neuecc/UniRx: Reactive Extensions for Unity (github.com) neuecc/UniRx Reactive Extensions for Unity. Contribute to neuecc/UniRx development by creating an account on GitHub. github.com 내장된 NavMe..

tistory.jeon.sh

참고로, 저는 위 블로그에서 처음 발견을 하게되어 어떤것인지 알아보고 또 적용을 하게되었습니다. 코드에 대해서 설명도 적어주셨기에 당장 쓰는것에도 문제없을만큼 간단명료해보였는데 아무래도 제가 더 공부를 하고나서 적용시키는게 맞을것같습니다!

 

 

 

 

 

 

 

 

 

 

▲ top