처음에 어떤 이벤트가 들어있는 클래스들을 넣으면 유니티에 인스펙터 뷰에 있는 프로퍼티
값들이 변경 되었으면 좋는데~ 로 시작해서 제작 하게 되었다.
출처:
https://www.linkedin.com/pulse/unity-hack-dynamic-property-field-inspector-zhen-stephen-gou?trk=portfolio_article-card_title
위의 링크를 참고하여 제작하였습니다.
총 3개의 모듈이 필요하다.
1. 인스펙터에 출력할 클래스
- 기능에 따른 클래스들을 나눔
2. 인스펙터에 정말로 그려줄 에디터
- 실질적인 출력 부분을 담당 할 클래스
3. 실질적으로 오브젝트에 달라붙을 클래스.
- MonoBehaviour 이 녀석을 상속 받아야 오브젝트에 추가 가능
거두절미하고 코드부터 확인하자.
using System; using UnityEngine; [Serializable] public abstract class ObjectEvent { public GameObject m_EventObject; public float m_fTime; public abstract bool Execute(); } [Serializable] public class CreateObj : ObjectEvent { public Transform m_Position; public override bool Execute() { Debug.Log("Create Object!!"); return true; } } [Serializable] public class CreateVecPosObj : ObjectEvent { public Vector3 m_Position; public override bool Execute() { Debug.Log("Create Object!!"); return true; } } [Serializable] public class DestroyObj : ObjectEvent { public override bool Execute() { Debug.Log("Destroy Object!!"); return true; } } [Serializable] public class AnimPlayObj : ObjectEvent { public override bool Execute() { Debug.Log("Play Animation!!"); return true; } } [Serializable] public class AudioPlayObj : ObjectEvent { public float m_fValue; public override bool Execute() { Debug.Log("Audio Play!!"); return true; } }
- 위의 코드는 이벤트에서 중복되는 변수를 둔 부모 클래스를 정의하고
- 아래 각 세부 기능들을 담당하는 자식클래스를 두면서 작업한다.
- 위의 장점은 기능들을 세분화 하기에 용의하고, 코드 재활용성이 올라감에 따른 사람눈이 편-안 하다.
using System; [Serializable] public class EventFactory { public enum EventType { CreateEvent, CreateVectorObj, DestroyEvent, AnimPlayEvent, AudioPlayEvent, } public EventType Type = EventType.CreateEvent; public CreateObj CreateObj = new CreateObj(); public CreateVecPosObj CreateVecPosObj = new CreateVecPosObj(); public DestroyObj DestroyObj = new DestroyObj(); public AnimPlayObj AnimPlayObj = new AnimPlayObj(); public AudioPlayObj AudioPlayObj = new AudioPlayObj(); public ObjectEvent GetEvent() { return GetEventFromType(Type); } public System.Type GetClassType(EventType evnetType) { return GetEventFromType(evnetType).GetType(); } private ObjectEvent GetEventFromType(EventType type) { switch (type) { case EventType.CreateEvent: return CreateObj; case EventType.CreateVectorObj: return CreateVecPosObj; case EventType.DestroyEvent: return DestroyObj; case EventType.AnimPlayEvent: return AnimPlayObj; case EventType.AudioPlayEvent: return AudioPlayObj; default: return CreateObj; } } }
- 위의 각 자식객체들을 동적 생성 해놓은 후에 선택 되는 값에 따른 리턴 벨류를 두도록 한다.
- 유저는 enum값으로 값을 선택 할 것이기 때문에 간편하고 빠른 스위치 케이스문으로 대체 한다.
- 여기서 클래스 들의 이름은 변수의 명과 같아야 하는데 Editor class 에서 그 이유를 설명 할 것이다.
using UnityEditor; [CustomEditor(typeof(ActiveEvent), true), CanEditMultipleObjects] public class ActiveEventEditor : Editor { protected static string EVENT_FACTORY_NAME = "PrimaryEvent"; protected ActiveEvent ActiveEventobject; protected SerializedObject serializedEvent; protected SerializedProperty SerializedEventProp; void OnEnable() { ActiveEventobject = (ActiveEvent)target; serializedEvent = new SerializedObject(ActiveEventobject); SerializedEventProp = serializedEvent.FindProperty(EVENT_FACTORY_NAME); } public override void OnInspectorGUI() { serializedEvent.Update(); DrawPropertiesExcluding(serializedEvent, new string[] { EVENT_FACTORY_NAME }); DrawPrimaryEvent(); serializedEvent.ApplyModifiedProperties(); } protected void DrawPrimaryEvent() { EditorGUILayout.LabelField(EVENT_FACTORY_NAME, EditorStyles.boldLabel); EditorGUILayout.PropertyField(SerializedEventProp.FindPropertyRelative("Type")); //display relevant ability properties based on ability type EventFactory EventFactory = ActiveEventobject.PrimaryEvent; System.Type typeOfAbility = EventFactory.GetClassType(EventFactory.Type); SerializedProperty specificEvent = (SerializedEventProp.FindPropertyRelative(typeOfAbility.ToString())).Copy(); string parentPath = specificEvent.propertyPath; while (specificEvent.NextVisible(true) && specificEvent.propertyPath.StartsWith(parentPath)) { EditorGUILayout.PropertyField(specificEvent); } } }
- WindowEditor, Editor 다 비슷한 기능들을 지원하니까 레퍼런스 검색을 통해 입맛에 맞는 기능들을 사용하면 될것 같다.
- OnEnable() 이 함수안에서 초기화를 시작한다.
- OnInspectorGUI() 이 함수에서 그려준다.
- DrawPrimaryEvent() 그려줄 기능들을 분할한 함수이다.
- 왜 1-1 글에서 클래스 이름과 동일한 변수명을 써주어야 하는 이유는 바로 여기에 있다. SerializedProperty specificEvent 변수에 typeOfAblility type변수를 가져와서 ToString()해주기 때문이다. 기본적으로 FindPropertyRelative 인자값으로 클래스 이름을 string값으로 넣어 주어야 하기 때문이다.
https://docs.unity3d.com/Manual/script-Serialization.html
- Serialization 에 대한 설명 글
https://docs.unity3d.com/ScriptReference/SerializedObject.html
- SerializationObject에 대한 설명 글
https://docs.unity3d.com/ScriptReference/EditorGUILayout.html
- EditorGUILayout 클래스 기능 설명 글
* 그래픽디자이너와 같이 작업 하기엔 프로그래머가 편하려면 에디터를 사용해서 기능들을 추가 해주는것이 더 효육적인것 같다.
더 이상 public 을 남발하는 그런 상황은 다메요..
물론, serializable serializefield를 사용 해도 괜찮지만, 상황에 따라서 맞춰 써야할 것 같다.
댓글 없음:
댓글 쓰기
아이디어 및 질문 외에 댓글은 사양합니다.