목록

커스텀에디트

유니티 작업 중 편의을 위해 몇몇 데이터 값들은 에디터부분으로 뺴는게 괜찮아서, 그에 따른 자료 정리


#선행 정보

Serialization(직렬화)

직렬화는 데이터 구조나 오브젝트 상태를 Unity 에디터가 저장하고 나중에 재구성할 수 있는 포맷으로 자동으로 변환하는 프로세스를 말합니다. Unity 에디터에서는 저장 및 로딩, 인스펙터 창, 인스턴스화, 프리팹과 같은 일부 내장 기능에 직렬화가 사용된다

바이너리 형태의 스트림으로 데이터를 변환하여 파일 등으로 저장한는 유니티에서 지원해주는 방.

객체를 연속적인 데이터 값을 일렬로 나열하여 값을 저장한는거 C에서는 변수 데이터만 입출력할 수 있었는데 그떄 당시에는 class형태로 저장이 안되었는데(배울떄) 직렬화를 통해 class단위로 데이터 저장 가능.(로드 시 역직렬화)

보통 에디터에서 play버튼 눌러 동작하고 stop시 play중에 입력했던 데이터들이 초기화되는데, 이유는 유니티는 Play모들르 실행/멈춤 또는 스크립트 수정을 핧 때마다 모노 어셈블리, 즉 유니티와 관련된 dll을 다시 로드한다. 이에 대해 이해를 위해 유니티의 플레이모드 방식을 이해할 필요가 있는데.. 크게 3단계를 거치게 된다.

1. 먼저 Managed메모리 영역에 있는 데이터를 다 뺴내고, 유니티 내부 C++에서 관리될 수 있는 데이터 형태로 생성

2. 유니티의 MAnaged메모리 영역에 관련된 정보, 할당된 메모리를 해제하고 어셈블리를 다시 로드한다.

3. C++에 임시로 저장되었던 데이터를 Managed메모리 영역에 다시 Serialize한다.

이러한 이유로 play상태의 데이터 구조/정보들이 살아남기 위해서는 유니티의 C++영역에서 Serialize가능해야 한다는의미.

SerializedObject

serializedObject는 직렬화 된 데이터를 Unity가 취급하기 쉽도록 가공 한 것

그러면 다양한 데이터에 액세스가 가능하게하거나 Undo 처리 나 게임 오브젝트에서 제조가 용이하게 작성할 수 있도록되어 있다고한다.

SerializedObject은 Unity에서 취급하는 모든 객체에 해당하며, 평소 다루고있는 자산 (재료 및 텍스처 애니메이션 클립 등)도 SerializedObject이 없이는 만들 수 없다.

핫 리로드 이해

핫 리로드는 에디터가 열려 있는 상태에서 스크립트를 생성하거나 편집한 후 스크립트 동작을 즉시 적용하는 프로세스.

변경 사항을 적용하기 위해 게임이나 에디터를 다시 시작할 필요가 없다는 장, 스크립트를 변경해서 저장하면 Unity 에디터가 현재 로드되어 있는 스크립트 데이터를 모두 핫 리로드한다.

우선 로드된 모든 스크립트에서 직렬화 가능한 변수를 저장하고 스크립트를 로드한 후 변수를 복원합니다. 핫 리로드 후에는 직렬화할 수 없는 모든 데이터를 잃게 됩니다.

직렬화 규칙

Unity 에디터의 직렬화는 실시간 게임 환경에서 실행되므로 성능에 상당한 영향을 미친다.

따라서 Unity 에디터의 직렬화는 기타 프로그래밍 환경의 직렬화와 다르게 동작한다.

Unity 에디터에서 직렬화를 사용하는 방법

에셋과 오브젝트의 내부 유니티에서 어떻게 데이터를 적절히 다루는지를 이해하기 위해, 유니티가 어떻게 데이터를 구별하고 직렬화하는지를 이해하는 것이 매우 중요.

첫번째 핵심은 Asset과 UnityEngine.Object를 구별하는 것이다.

에셋은 유니티 프로젝트의 Assets폴더에 저장되어디스크 상의 한 파일인데, 텍스쳐파일, 매터리얼, FBX등이 모두 에셋이다.

오브젝트(UnityEngine.Object) 는 리소스의 특정 인스턴스를 표현하는 직렬화된 데이터 들의 집합, 이는 유니티에서 사용하는 어떤 리소스 타입도 가능한데 예로 메쉬, 스프라이트 등이 있다.

모든 종류의 오브젝튼느 UnityEngine.Object를 상속받은 하위클래스이다.

에셋과 오브젝트 간에는 일대다(one-to-many)의 관계가 있으며, 이 말은 어떤 하나의 에셋 파일이 한 개 이상의 오브젝트를 가진다는 말입니다.


#인스펙터 창 커스터마이즈(Custom Inspector)

인스펙터에 보여지는 정보를 커스터마이즈 하려면

Editor 클래스에서 상속을 받아야 한다.

에디터 커스터마이즈 스크립트들은 반드시 [Project Folder/Assets/Editor] 밑에 있어야 동작한다.

  • 카메라 view 인스펙터창에 그리기
public class MovementEditor : Editor
{
  // 목표 지점에서 게임 오브젝트를 바라보는 카메라
  static private Camera _camera = null;

  void OnEnable()
  {
      // ... 중간 생략 ...

      // 카메라를 새로 세팅하는 번거로움을 줄이기 위해 잠시 메인 카메라를 빌려서 사용합니다.
      if (_camera == null)
          _camera =GameObject.Find("Main Camera").GetComponent<camera>();
  }

  void OnDisable()
  {
      // 카메라 삭제
      if (_camera != null)
          _camera = null;
  }

  //_movement는 해당 에디터 객체.
  public override void OnInspectorGUI()
  {
      // ... 중간 생략 ...

      // 카메라를 렌더링 - 렌더링 영역, 위치, 회전값은 카메라 렌더링 후에 복구해야 하므로 임시로 저장.
      Rect org_pixel_rect = _camera.pixelRect;
      Vector3 org_position = _camera.transform.position;
      Quaternion org_rotation = _camera.transform.rotation;
      _camera.transform.position = _movement._targetPosition;
      _camera.transform.LookAt(_movement.transform.position);
      _camera.pixelRect = new Rect(10, 10, 100, 100);
      _camera.Render();
      _camera.pixelRect = org_pixel_rect;
      _camera.transform.position = org_position;
      _camera.transform.rotation = org_rotation;
  }
}

//예제 출처: http://mobilism.tistory.com/entry/Unity3D-에디터-커스터마이즈-2?category=322178

#씬 윈도우 커스터마이즈(Custom Scene Window)

씬 윈도우에 UI 혹은 여러 정보등을 표시해 줄 필요가 있을 것 이며

OnSceneGUI()함수를 통해 그릴 수 있으면 Handles객체를 통해 여러가지 함수 호출 가능.

public class MovementEditor : Editor
{
    // ... 중간 코드 생략 ...

    // 게임 오브젝트가 선택되었을 때, 씬 윈도우에 표시되는 UI나 여러 표식들은 이곳에서 그려줍니다.
    public void OnSceneGUI()
    {
        // 타겟 지점에 붉은색의 큐브 생성
        Handles.color = Color.red;
        Handles.CubeCap(0, _movement._targetPosition, Quaternion.identity, 1.0f);

        // 타겟 지점과 게임 오브젝트를 녹색 줄로 이어주겠습니다.
        Handles.color = Color.green;
        Handles.DrawLine(_movement.gameObject.transform.position, _movement._targetPosition);

        // UI
        Handles.BeginGUI();
        {
            // 타겟 지점으로 게임 오브젝트를 이동시켜 주는 버튼 표시
            if (_movement.gameObject.transform.position != _movement._targetPosition)
            {
                // 3D 좌표를 2D 좌표로 변경해서 큐브보다 약간 더 위에 출력시켜 준다.
                Vector2 button_positoin = HandleUtility.WorldToGUIPoint(_movement._targetPosition);
                Rect button_rect = new Rect(button_positoin.x, button_positoin.y - 40, 200, 20);
                if (GUI.Button(button_rect, "Move To Target Position"))
                    _movement.gameObject.transform.position = _movement._targetPosition;
            }
        }
        Handles.EndGUI();
    }
}

//예제 출처: http://mobilism.tistory.com/entry/Unity3D-에디터-커스터마이즈-2?category=322178

#메뉴 윈도우 커스터마이즈(Custom Scene menu)

마찬가지로 Assets/Editor폴더 내에 생성해야 한다.

using UnityEngine;
using UnityEditor;

public class Sample01 : EditorWindow
{
    [MenuItem("Sample Menu/Sample01")]
    static void ShowWindow()
    {
        EditorWindow.GetWindow<Sample01>();
    }
    void OnGUI()
    {
        GUI.Button(new Rect(0, 0, 100, 50), "Button");
    }
}
//출처 : http://blog.naver.com/cra2yboy/90120829270

#함수 설명

DrawDefaultInspector

OninspectorGUI를 통해 커스텀 할떄 안에 내용이 비어 있는 경우 해당 스크립트 관련 에디터정보가 안보이는데(기본적인) 그럴떄 이 함수를 OninspectorGUI내부에서 호출 시 원래 기본 상태 에디터로 보여지게됨.

현재 존재하는 항목을 변경하는 대신 스크립트의 새 항목 만 속성 관리자에 추가하려는 경우 매우 유용