Notice
Recent Posts
Recent Comments
Link
12-25 13:16
«   2024/12   »
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
Tags
more
Archives
Today
Total
관리 메뉴

킹머핀의 제작 일지

UNT) 커스텀 에디터를 만들 때 Undo가 안 된다? 본문

Unity

UNT) 커스텀 에디터를 만들 때 Undo가 안 된다?

KingMUffin 2021. 5. 14. 22:41

내가 만든 커스텀 에디터는 유니티를 실행할 때마다 초기화된다. 런타임을 실행할 때마다 초기화된다. 런타임을 정지해도 초기화된다.

그리고 커스텀 에디터를 편집해도 씬에 수정사항이 반영되지 않기 때문에 종료할 때 저장할 거냐고 묻지도 않는다. 혹시 이 두 증상은 관련이 있을까?

 

[에디터 확장 입문] 번역 9장 CustomEditor

http://anchan828.github.io/editor-manual/web/customeditor.html9장 CustomEditor 커스텀 에디터는, 인...

blog.naver.com

위 글에서, Editor 클래스에서 SerializedObject를 통해 메인 스크립트와 커스텀 에디터 스크립트 간 데이터를 교환하면 Undo 처리를 해 준다고 한다. (모든 오브젝트는 직렬화되어 다루어진다. 에셋도 오브젝트다.) 그래서 target을 이용한 데이터 교환은 Undo 처리를 직접 해야 한다.

이미 이전에도 두 방식을 알고 있었고, 뭔 차인지 모르겠고 편한 방법으로 안 할 이유가 없어서 target을 이용해 코드를 작성했다. 근데 이제 뭔 차인지 알겠네. ㅎㅎㅎ 역시 제공하는 기능에는 이유가 있다. 괜히 있는 게 아니라고 생각할 땐 주는 걸 써야 한다.

자, 그럼 이제 모든 필드를 커스텀 에디터 스크립트에 똑같이 선언하고, serializedObject.FindProperty 함수로 전부 가져와야 하나?AnimationCurve나 Enum 등은 직렬화가 가능한지 모르겠다.

그럼 그 전에, 위 글에서 보여주는 코드를 참고해서 target으로 가져온 클래스를 Undo.RecordObject로 통째로 Undo 처리해보자. 방법은? 그냥 이 함수를 OnInspectorGUI 함수 최상단에 넣는 것뿐.

오? 잘 작동한다! Undo가 가능하고, 씬이 변경사항을 감지한다. 이렇게 겁나 쉽게 해결된다고? 에이 설마. 분명 어딘가에 또 문제가 있을 것이다. 음, 그리고 역시 있었다. Undo 한 번으로 처음으로 돌아가버린다. Redo가 안 된다. 런타임 실행 후 정지해도 초기화된다.


GUI를 변경하는 모든 코드 앞에 저 함수를 넣어봐도 마찬가지. 대신 메뉴에서 Undo 처리는 되고 있는 것을 확인할 수 있다. 그렇다면 한 번에 여러 Undo를 처리해도, 여러 번 Undo를 처리해도, Undo 한 번으로 전부 초기화되고 있나보다.

예전에 쓸일이 있지 않을까 해서 넣어둔 serializedObject.Update 그리고 serializedObject.ApplyModifiedProperties 함수와 충돌이 생겼을까 해서 주석 처리해봐도 마찬가지.

만약 모든 Undo가 동시에 실행되는 문제라면 Undo.IncrementCurrentGroup 함수로 순서를 나눌 수 있지만, 애초에 OnInspectorGUI 함수에서 Undo.RecordObject를 한 번씩만 실행하고 있는 상황이라 상관이 없다. GUI를 변경하는 모든 코드 앞에 Undo.RecordObject와 Undo.IncrementCurrentGroup을 함께 넣어봐도 마찬가지다.

그럼 어떻게 하지?

또다시 찾아온 위기. 두 가지 선택지가 있다.
1. 오기가 생겨서라도 해결하고 성취감을 얻거나 2. 알아서 다 처리해주는 방법으로 수정한다.
예전의 나라면 1번으로 갔겠지만, 그건 아주 미련한 길이란 걸 깨달은 지금의 난 예전의 내가 아니다. 실마리가 안 잡히고 있기도 했고..


자, 그럼 이제 모든 필드를 커스텀 에디터 스크립트에 SerializedProperty로 똑같이 선언하고, serializedObject.FindProperty 함수로 전부 가져오자.

오오.. 생각보다 수월한데? 기존에는 조절한 값을 다시 대입하는 코드 때문에 보기 좋지 않았는데, 지금은 조절하는 함수에 인수로 SerializedProperty를 넣기만 하면 바로 적용된다.

그리고 문제는 그 다음에 일어난다. 나는 값을 조절하면 타 컴포넌트에 바로 적용되도록 모든 필드를 속성으로 다시 생성해서 사용하는데, 아무래도 속성은 SerializedProperty로 가져올 수 없나보다. 차암나 겁나 깐깐하네.. [NullReferenceException: Object reference not set to an instance of an object]가 출력된다. [SerializeField]로도 가져올 수 없다. 왜지? 디버그 중에 오류가 안 뜨면 직렬화가 가능하단 뜻 아닌가? 그럼 직렬화는 되는데 가져올 수는 없다? 에라이 코딩 망해라

찾아보니 유니티에서 모종의 이유로 막아놨댄다. 이 메뉴얼을 봐도 못 하는 걸 알 수 있다. 그래서 이제 어떡하나? 만약 변경사항을 감지하고 직접 속성에 다시 대입한다면 Undo 후 Redo를 했을 때 결과가 달라지지 않을까? 아, Redo를 실행했다는 걸 알면 그 함수를 다시 호출할 수 있을 텐데. 찾아보니 가능하다.

이 와중에, 스크립트 클래스에 [ExecuteInEditMode]가 주석 처리되어 있는 걸 발견하고 주석을 해제했다. 위에서 Undo.RecordObject가 제대로 작동하지 않는 문제와 관련이 있는지 확인해봤다. 의도하진 않았지만 백업 파일이 있어서 다행이다. 결과는 마찬가지였다. 그래도 [ExecuteInEditMode]는 그대로 두기로 했다.

serializedObject.ApplyModifiedProperties() ||
(Event.current.type == EventType.ValidateCommand &&
Event.current.commandName == "UndoRedoPerformed")

위의 찾아본 링크에서 가져온 이 조건문을 이용해서 Undo or Redo 수행 시 필드를 속성에 다시 대입해보았다. 잘 작동한다! 하지만 그렇기만 할까? 어림도 없지!

항상 잘 작동하지 않는다. 모든 GUI의 변경사항을 감지하는 것도 아니고, 감지하더라도 두 번째 변경사항부터 되돌린다. 왜 때에 따라 다른 거지? 그리고 위 조건문이 Undo or Redo 수행 시 참이 되지 않는다. 흠.. 그러고보니 저 EventType.ValidateCommand는 무슨 의미야?

흠.. 아무래도 나한텐 필요 없다. 그래서 지워봤더니, 이제 Undo에 Redo까지도 모두 잘 작동한다! 유후~ 근데 이걸 수행할 때마다 변경사항 감지를 두 번 이상씩 하던데, 원인이 뭐지? 문제 없나? 모르겠다~ &