본문 바로가기

분류 전체보기

(18)
Unity의 fake null 문제 유니티 엔진의 구조 UnityEngine.Object를 == null로 비교해서 true가 떨어졌다고 진짜 null인 것은 아니다. 이 문제는 유니티 개발자들에게는 잘 알려져 있는 문제지만, 워낙 직관적이지 않은 문제이기 때문에 유니티를 처음 접하는 사람들은 백이면 백 빠지는 함정이기도 하다. 유니티 코어 레벨의 객체들은 모두 다 C++로 구현되어 있고, C++ 객체로서 존재한다. 유니티 스크립트 상에서 보이는 C# 객체는 C++ 객체를 참조하는 wrapper로 볼 수 있다. 이 C# wrapper 객체는 무조건 생성되는게 아니라, C++ 객체가 C#레벨에서 노출되어야 할 때만 생성되는데, 상황에 따라서는 C++ 객체만 생성되어있고 C# 객체는 생성되어 있지 않을 수도 있다. 하지만 반대로 C++ 객체는..
IEnumerable, IEnumerator를 반환타입으로 가지는 메서드 파고들기 코루틴과 yield의 마법 유니티를 접하면서 코루틴을 처음 써보면 대기하는 것 같아 신기하다. 분명히 코드는 한줄한줄 순서대로 실행되는데 어떻게 yield에서 대기해놓고 다른곳을 실행할 수 있을까? IEnumerator와 yield가 정확히 어떻게 동작하는지는 찾아보면, 대략 MoveNext()를 호출할 때마다 다음 yield를 만날 때까지 실행해서 값을 반환한다는 식으로 이해된다. 약간 더 깊이 파고들면, 프로그래밍 언어에서 함수 호출을 하는 방식을 생각해볼 수 있다. 함수를 호출하면 스택에 반환될 주소와 함수 내부의 지역변수들이 추가된다. 그런데 만약 함수에서 값을 반환해버리면 스택이 없어질테고, 다음에 "중간부터 다시 시작" 할 수는 없을 것 같다. 어떻게 이런 일이 가능한 것일까? IEnumera..
C#의 readonly 키워드와 불변성에 대해 readonly vs const 처음 readonly를 접하면, 자연스럽게 const와 비교하게 된다. 언뜻 보기에는 둘다 변하지 않는 값을 선언하는 문법으로, 큰 차이를 느낄 수 없다. 다른 점을 찾아봐도 주로 const는 컴파일 시간 상수고, readonly는 런타임 상수라는 정도, 혹은 readonly는 생성자에서 값을 변경할 수 있다 라는 정도로 이해하는 경우가 대다수이다. 그러나 이는 완전한 오해이다. const와 readonly는 의미적으로나 실제로나 완전히 다르다. 근본적인 차이점은 const는 상수이고 readonly는 변수라는 것이다. const는 일종의 별명과도 같다. const int a = 1; 이라는 문장이 있을 때, 이 것은 1의 별명을 a로 하겠다는 선언이다. 이는 재정의하거나..
MonoBehaviour의 초기화는 어떻게 해야 하는가? Awake로 충분하지 않나요? MonoBehaviour의 초기화 메소드로는 유니티에서 Awake, Start 등을 지원하고 있다. 일반적으로 초기화는 Awake에서 하게 될 것이다. using UnityEngine; public class Something : MonoBehaviour { private void Awake() { Debug.Log("awake."); } } 잘 동작한다. 그런데 문서에는 강조되지 않는부분이지만, 생성 당시 GameObject가 비활성화 상태일때는 Awake가 호출되지 않는다. 나중에 활성화되는 시점에 Awake가 호출된다. using UnityEngine; public class SomethingParent : MonoBehaviour { public Something s..
유니티에서 테이블 데이터 로드하기 #4 - Source Generator C#의 Source Generator를 사용하여 직렬화/역직렬화 코드를 생성하는 방법도 있다. 이론적으로 어떤 클래스를 byte array로 변환하려면 각 멤버를 모두 개별적으로 BitConverter 등을 이용해서 순서대로 byte array에 쓰면 되는데, 모든 데이터를 표현하는 클래스에 개별적으로 직렬화하는 코드를 작성하는 방법은 현실적이지 않다. 하지만 C#에는 이럴 때 사용할 수 있는 기능이 있는데, 바로 Source Generator이다. https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview Source Generators Source Generators is a C# compiler featu..
유니티에서 테이블 데이터 로드하기 #3 - Marshal System.Runtime.InterOpServices.Marshal을 사용하여 byte array로 변환하는 방법도 고려해볼 수 있다. 이 방법은 특히 관리되지 않는 형식, 즉 int, float 등으로만 구성된 클래스를 저장하고 읽을 때 빠른 속도를 보여준다. 데이터의 형식에 따라 상당히 달라질 수 있지만 json보다도 3~5배가량의 속도 향상을 기대해볼 수 있다. Marshal은 프로그램이 다른 프로그램에서 사용할 수 있도록 데이터를 전송하는 것을 의미하는데, Marshal을 하려면 직렬화를 하는 경우가 많다. 따라서 .NET에서는 Marshal.StructureToPtr과 Marshal.PtrToStructure를 이용해 간단히 클래스를 byte array로 직렬화하는 메소드를 제공한다. usin..
유니티에서 테이블 데이터 로드하기 #2 - CSV 두번째로 소개할 형식은 CSV이다. 과거에는 비교적 사용 빈도가 높았으나, 최근에는 게임 등 일부 분야를 빼고는 사용 빈도가 많이 줄어든 느낌이다. 장점으로는 사람이 읽고 비교하기가 가장 쉬운 형식이라는 점, 엑셀이나 구글 시트, 오픈오피스 등에서 CSV 익스포트를 모두 지원하기 때문에 간단한 경우 익스포터를 따로 만들 필요가 없다는 점 등이 있다. 단점으로는 CSV 파서는 유니티 내장 기능이 아니다보니 적당한 외부 라이브러리를 찾아와야 하는 점과, 속도가 비교적 느리다는 점 등이 있다. 또한 파서에 따라 빈 컬럼 (,,) 을 빈 문자열 ""로 읽거나 null로 읽는 등 동작이 조금씩 다른 점도 문제가 될 수 있다. C#용 CSV 파서는 여러가지가 있는데, 가장 널리 쓰이는 것은 CsvHelper(htt..
유니티에서 테이블 데이터 로드하기 #1 - JSON 게임 제작에는 아주 간단한 게임이 아닌 이상 데이터 테이블이 필수이다. 따라서 게임개발에는 필수적으로 테이블을 로드하는 코드가 들어가야 한다. 이 포스트들에서는 유니티에서 데이터 테이블을 다루는 여러가지 방법을 살펴볼 예정이다 가장 처음 소개할 내용은 JSON형식이다. 웹에서 널리 사용되어 비교적 친숙하며, 사람이 어느정도 눈으로 읽거나 고칠 수 있고, 유니티에 기본적으로 내장되어있다. using System; using System.Collections.Generic; using UnityEngine; [Serializable] public class Row { public int A; public float B; public string C; } [Serializable] public class Da..