NullReferenceException: Object reference not set to an instance of an object.
at System.Linq.Expressions.Interpreter.LightLambda.MakeRunDelegateCtor (System.Type delegateType) [0x00000] in <00000000000000000000000000000000>:0
at System.Linq.Expressions.Interpreter.LightLambda.GetRunDelegateCtor (System.Type delegateType) [0x00000] in <00000000000000000000000000000000>:0
at System.Linq.Expressions.Interpreter.LightDelegateCreator.CreateDelegate () [0x00000] in <00000000000000000000000000000000>:0
at System.Linq.Expressions.Expression`1[TDelegate].Compile (System.Boolean preferInterpretation) [0x00000] in <00000000000000000000000000000000>:0
at System.Runtime.CompilerServices.CallSite`1[T].MakeUpdateDelegate () [0x00000] in <00000000000000000000000000000000>:0
at System.Runtime.CompilerServices.CallSite`1[T].GetUpdateDelegate (T& addr) [0x00000] in <00000000000000000000000000000000>:0
at System.Runtime.CompilerServices.CallSite`1[T]..ctor (System.Runtime.CompilerServices.CallSiteBinder binder) [0x00000] in <00000000000000000000000000000000>:0
at System.Runtime.CompilerServices.CallSite`1[T].Create (System.Runtime.CompilerServices.CallSiteBinder binder) [0x00000] in <00000000000000000000000000000000>:0
유니티에서 지원하는 Dynamic Font는 상당히 편리하다. 기존에 한글 폰트를 화면에 표시하려면, 한글 유니코드에 해당하는 모든 글자를 폰트 텍스쳐에 담아놓고 써야해서, 메모리 낭비가 컸고, 글자 크기도 크게 하기가 힘들었다.
반면 Dynamic Font는 그때 그때 사용하는 글자들만 폰트 텍스쳐에 그려두고 사용하기 때문에 메모리 낭비가 적고, 폰트를 크게 표시할 수 있다는 장점이 있다.
하지만, 유니티 자체의 버그인지 특정 폰의 문제인지 몇몇 폰에서 플레이를 하다보면, 글자들이 깨지거나, 일부 글자가 아예 표시되지 않는 버그가 발견되고 있고, 현재까지 배포된 유니티 버전에서는 아직 해결되지 않은 것으로 보인다.
해결 방법을 구글링 해보았지만, 아직 완벽한 해결책은 없는 것 같아서 직접 여러가지 실험을 해보고 대응 방법을 찾아보았다.
1. Dynamic Font의 텍스쳐 관리 방식
맨 처음 게임이 실행되면 기본 폰트 텍스쳐 크기는 256*256이다. 여기에 글자들이 추가되면서 글자를 추가할 공간이 없을 때 256*256이 256*512, 512*512, 그 다음에는 512*1024 이런식으로 텍스쳐 크기가 증가한다.
폰트 텍스쳐에 새로운 글자가 추가되었는데 더이상 추가할 수 있는 공간이 없을 때, 폰트 텍스쳐가 재정렬되는데, 이 경우 크기가 변경될 수도 있고, 변하지 않을 때도 있다. 더 이상 글자를 추가할 공간이 없을 때, 사용하지 않는 글자들을 정리하기 때문에 정리를 하고 나서 공간이 남는 경우에는 크기가 그대로 유지 된다. 나름 합리적으로 폰트 텍스쳐를 관리하고 있는 것인데, 문제는 특정 폰에서 이 순간에 글자가 사라지는 것이다.
실험을 해본 결과 문제가 되는 폰에서는 폰트 텍스쳐가 리셋되는 순간 크기의 변화가 있으면 정상적으로 표시가 되는데, 사이즈의 변화가 없으면 일부 글자가 표시되지 않는 문제가 발생하고 있었다.
2. 대응 방법
대응방법은 아름답지는 않지만, 단순한 편이다. 폰트 텍스쳐가 리셋되는 순간 만약 폰트 텍스쳐의 크기가 변경되지 않았다면, 변경될 때까지 폰트 텍스쳐에 존재하지 않는 글자를 계속 추가해주는 것이다. 코딩은 대략 다음과 같다.
1) 유니티의 Font에는 Font.textureRebuildCallback이라는 콜백함수를 등록할 수 있다. 게임 시작 시점에 콜백 함수에 특정 함수를 지정해두면, 폰트 텍스쳐의 리셋 시점에 해당 함수가 호출된다. ex: this.font.textureRebuildCallback = this.SizeChanged;
4) 기존 사이즈와 똑같으면, 한글을 맨처음부터 한글자씩 집어넣는다. 맨 처음 '가'를 font.GetCharacterInfo를 이용해서, 폰트 텍스쳐에 '가' 글자가 들어있는지 확인해서, 들어있지 않다면 font.RequestCharactersInTexture함수를 이용해서, '가'를 추가해준다. 처음에는 5글자 정도를 추가하고, Invoke함수 등을 이용해서 잠시 기다린다. 만약, 추가한 5글자로 인해서, 크기가 커진다면 다시 콜백함수가 호출될 것이고, 그렇다면 글자들이 제대로 보일 것이므로, 해당 루틴을 끝낸다. 하지만, 커지지 않았다면, 다시 10글자 정도를 추가하고, 그래도 안되면 15글자로 늘리는 방식이다. 이런 루틴을 반복하다보면 어느 순간 폰트 텍스쳐의 크기가 커진다. (폰트 사이즈가 너무 작고, 폰트 텍스쳐 크기가 굉장히 커서 모든 한글 글자를 포함할 경우라면 무한루프가 될 수도 있을거 같다.)
한글을 자동으로 하나씩 추가하려면, 아래와 같은 코드를 이용해서, 초중종성의 값을 증가시켜가면 된다.