using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
#if UNITY_EDITOR
using UnityEditor;
[CustomEditor(typeof(RoomGoto))]
public class RoomGoto_Inspector : Editor
{
public override void OnInspectorGUI()
{
EditorGUILayout.LabelField("버튼조작을 위한 컴포넌트");
}
}
#endif
public class RoomGoto : MonoBehaviour
{
public void room_goto(string room_name)
{
SceneManager.LoadScene(room_name);
}
public void game_quit()
{
Application.Quit();
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
기존 인스펙터 호출
public override void OnInspectorGUI()
{
DrawDefaultInspector();
}
혹은
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
[CustomEditor(typeof(SampleClass))]
public class SampleClassInspecter : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();//기본 인스펙터를 받아올때
serializedObject.Update();
{
if (GUILayout.Button("실행"))
{
((SampleClass)target).PressInspecterButton();
}
EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(SampleClass.notice)), GUIContent.none);
}
serializedObject.ApplyModifiedProperties();
}
}
#endif
public class SampleClass : MonoBehaviour
{
[HideInInspector]
[Header("주석")]
public string notice;
public void PressInspecterButton()
{
//버튼을 누를시 활성화될 코드
if (Application.isPlaying)
{
//게임중일때 실행될 코드
}
else
{
//인스펙터에서 수정중일때 실행될 코드
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
[CustomEditor(typeof(SampleClass))]
public class SampleClassInspecter : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();//기본 인스펙터를 받아올때
serializedObject.Update();
{
EditorGUI.BeginChangeCheck();
{
EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(SampleClass.border)));
}
if (EditorGUI.EndChangeCheck())
{
((AccountSelect)target).SelectInspecterSlider();
}
EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(SampleClass.border)), GUIContent.none);
}
serializedObject.ApplyModifiedProperties();
}
}
#endif
public class SampleClass : MonoBehaviour
{
[HideInInspector]
[Header("주석")]
public string notice;
[HideInInspector]
[Range(0,6)]
public float border;
public void SelectInspecterSlider()
{
//값변경시 활성화될 코드
if (Application.isPlaying)
{
게임중일때 실행될 코드
}
else
{
인스펙터에서 수정중일때 실행될 코드
}
}
}
라벨필드
public override void OnInspectorGUI()
{
EditorGUILayout.LabelField("일반라벨");
EditorGUILayout.LabelField("굵은글씨", EditorStyles.boldLabel);
}
메뉴를 만들어야 할때
#if UNITY_EDITOR
using UnityEditor;
public class CustomMenuEditor : Editor
{
[MenuItem("MyMenu/Run")]
static void Run()
{
//실행되는 코드
}
}
#endif
커스텀 윈도우 프로퍼티 필드(수동 serializedObject)
public Object folder;
SerializedObject serializedObject;
void OnGUI()
{
if (serializedObject == null)
{
serializedObject = new SerializedObject(this);
}
serializedObject.Update();
{
EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(folder)));
}
serializedObject.ApplyModifiedProperties();
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditorInternal;
[CustomEditor(typeof(SampleClass))]
public class SampleClassInspecter : Editor
{
Hashtable reorderableListTable = new Hashtable();
public void DrawStageDatas(string propertyPath)
{
var reorderableListProperty = serializedObject.FindProperty(propertyPath);
if (reorderableListTable[propertyPath] == null)
{
reorderableListTable[propertyPath] = new ReorderableList(serializedObject, reorderableListProperty);
}
var reorderableList = (ReorderableList)reorderableListTable[propertyPath];
serializedObject.Update();
{
//헤더명
reorderableList.drawHeaderCallback = (rect) => EditorGUI.LabelField(rect, $"{propertyPath} ({reorderableListProperty.arraySize})");
//요소
var elementName = nameof(SampleClass.StageData.stageName);
reorderableList.drawElementCallback =
(Rect rect, int index, bool isActive, bool isFocused) =>
{
EditorGUI.PropertyField(rect, reorderableListProperty.GetArrayElementAtIndex(index).FindPropertyRelative(elementName));
};
reorderableList.DoLayoutList();
//선택된 필드
if (reorderableList.index >= 0)
{
var index = reorderableList.index;
if ((reorderableListProperty != null) && (reorderableListProperty.arraySize > 0) && (index < reorderableListProperty.arraySize))
{
var elementAtIndex = reorderableListProperty.GetArrayElementAtIndex(index);
if (elementAtIndex.FindPropertyRelative(nameof(SampleClass.StageData.isTutorial)).boolValue)
{
//튜토리얼 모드라면 이름과 제한시간만 표시
EditorGUILayout.PropertyField(elementAtIndex.FindPropertyRelative(nameof(SampleClass.StageData.isTutorial)));
EditorGUILayout.PropertyField(elementAtIndex.FindPropertyRelative(nameof(SampleClass.StageData.timeLimit)));
}
else
{
//기본표시
foreach (var field in typeof(SampleClass.StageData).GetFields())
{
if ((field.IsStatic) || (field.IsNotSerialized))
{
}
else
{
if (field.Name != elementName)
{
EditorGUILayout.PropertyField(elementAtIndex.FindPropertyRelative(field.Name));
}
}
}
//life값은 0으로 초기화
{
var sampleClass = (SampleClass)target;
var field = sampleClass.GetType().GetField(propertyPath);
var datas = (SampleClass.StageData)field.GetValue(sampleClass);
datas[index].life = 0;
//구조체라면 field.SetValue(sampleClass, datas)를 사용한다
}
}
}
}
}
serializedObject.ApplyModifiedProperties();
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
DrawStageDatas(nameof(SampleClass.stageDatas));
DrawStageDatas(nameof(SampleClass.stageDatas2));
}
}
#endif
public class SampleClass : MonoBehaviour
{
[HideInInspector]
public StageData[] stageDatas;
[HideInInspector]
public StageData[] stageDatas2;
[System.Serializable]
public class StageData
{
public string stageName;
public bool isTutorial;
public int timeLimit;
public int life;
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditorInternal;
[CustomEditor(typeof(SampleClass))]
public class SampleClassEditor : Editor
{
Hashtable reorderableListTable = new Hashtable();
public void DrawStageDatas(string propertyPath)
{
var reorderableListProperty = serializedObject.FindProperty(propertyPath);
if (reorderableListTable[propertyPath] == null)
{
reorderableListTable[propertyPath] = new ReorderableList(serializedObject, reorderableListProperty);
}
var reorderableList = (ReorderableList)reorderableListTable[propertyPath];
serializedObject.Update();
{
//헤더명
reorderableList.drawHeaderCallback = (rect) => EditorGUI.LabelField(rect, $"{propertyPath} ({reorderableListProperty.arraySize})");
//요소크기
reorderableList.elementHeight = EditorGUIUtility.singleLineHeight * 4;
reorderableList.drawElementCallback =
(Rect rect, int index, bool isActive, bool isFocused) =>
{
var elementProperty = reorderableListProperty.GetArrayElementAtIndex(index);
var fieldRect = rect;
fieldRect.height = EditorGUIUtility.singleLineHeight;
EditorGUI.PropertyField(fieldRect, elementProperty.FindPropertyRelative(nameof(SampleClass.StageData.stageName)));
fieldRect.y += EditorGUIUtility.singleLineHeight;
EditorGUI.PropertyField(fieldRect, elementProperty.FindPropertyRelative(nameof(SampleClass.StageData.isTutorial)));
fieldRect.y += EditorGUIUtility.singleLineHeight;
EditorGUI.PropertyField(fieldRect, elementProperty.FindPropertyRelative(nameof(SampleClass.StageData.timeLimit)));
fieldRect.y += EditorGUIUtility.singleLineHeight;
EditorGUI.PropertyField(fieldRect, elementProperty.FindPropertyRelative(nameof(SampleClass.StageData.life)));
};
reorderableList.DoLayoutList();
}
serializedObject.ApplyModifiedProperties();
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
DrawStageDatas(nameof(SampleClass.stageDatas));
DrawStageDatas(nameof(SampleClass.stageDatas2));
}
}
#endif
public class SampleClass : MonoBehaviour
{
[HideInInspector]
public StageData[] stageDatas;
[HideInInspector]
public StageData[] stageDatas2;
[System.Serializable]
public class StageData
{
public string stageName;
public bool isTutorial;
public int timeLimit;
public int life;
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
참고용 단순 예제
#if UNITY_EDITOR
using UnityEditor;
using UnityEditorInternal;
[CustomEditor(typeof(SampleClass))]
public class SampleClassEditor : Editor
{
Hashtable reorderableListTable = new Hashtable();
public static System.Type GetType(SerializedProperty property)
{
var parentType = property.serializedObject.targetObject.GetType();
var fieldInfo = parentType.GetField(property.propertyPath);
return fieldInfo.FieldType;
}
public void DrawReorderableList(string propertyPath)
{
var reorderableListProperty = serializedObject.FindProperty(propertyPath);
if (reorderableListTable[propertyPath] == null)
{
reorderableListTable[propertyPath] = new ReorderableList(serializedObject, reorderableListProperty);
}
var reorderableList = (ReorderableList)reorderableListTable[propertyPath];
serializedObject.Update();
{
//헤더명
reorderableList.drawHeaderCallback = (rect) => EditorGUI.LabelField(rect, $"{propertyPath} ({reorderableListProperty.arraySize})");
//요소
var targetType = GetType(reorderableListProperty).GetElementType();
reorderableList.elementHeight = EditorGUIUtility.singleLineHeight * Mathf.Max(1, targetType.GetFields().Length);
reorderableList.drawElementCallback =
(Rect rect, int index, bool isActive, bool isFocused) =>
{
var elementProperty = reorderableListProperty.GetArrayElementAtIndex(index);
var fieldRect = rect;
fieldRect.height = EditorGUIUtility.singleLineHeight;
fieldRect.y -= EditorGUIUtility.singleLineHeight;
foreach (var fields in targetType.GetFields())
{
fieldRect.y += EditorGUIUtility.singleLineHeight;
EditorGUI.PropertyField(fieldRect, elementProperty.FindPropertyRelative(fields.Name));
}
};
reorderableList.DoLayoutList();
}
serializedObject.ApplyModifiedProperties();
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
DrawReorderableList(nameof(SampleClass.stageDatas));
DrawReorderableList(nameof(SampleClass.stageDatas2));
}
}
#endif
public class SampleClass : MonoBehaviour
{
[HideInInspector]
public StageData[] stageDatas;
[HideInInspector]
public StageData[] stageDatas2;
[System.Serializable]
public class StageData
{
public string stageName;
public bool isTutorial;
public int timeLimit;
public int life;
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
자동화 버전
배열길이 임의로 조정
var property=serializedObject.FindProperty(nameof(SampleClass.folders));
if(property.arraySize > 3)
{
property.arraySize = 3;
}
다중배열 표시
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
//Production Date 2022-10-10 from Ahzkwid
using UnityEditor;
[CustomEditor(typeof(SampleClass))]
public class SampleClassInspecter : Editor
{
public void Draw2DArray(string targetFieldName)
{
bool IsArrayCustom(System.Type type)
{
return (type.IsArray) || ((type.IsGenericType) && type.GetGenericTypeDefinition() == typeof(List<>));
}
void DrawElement(string label,object ilistObject)
{
var ilist = (System.Collections.IList)ilistObject;
EditorGUI.indentLevel--;
{
EditorGUILayout.LabelField(label);
}
EditorGUI.indentLevel++;
EditorGUI.indentLevel++;
{
if (ilist == null)
{
EditorGUILayout.LabelField("null");
}
else
{
EditorGUILayout.TextField($"Size", ilist.Count.ToString());
for (int i = 0; i < ilist.Count; i++)
{
if (ilist[i] == null)
{
EditorGUI.indentLevel++;
EditorGUILayout.LabelField("null");
EditorGUI.indentLevel--;
continue;
}
var elementType = ilist[i].GetType();
if (IsArrayCustom(elementType))
{
DrawElement($"▼Element {i}", ilist[i]);
}
else
{
EditorGUILayout.TextField($"Element {i}", ilist[i].ToString());
}
}
}
}
EditorGUI.indentLevel--;
}
serializedObject.Update();
{
var fields = target.GetType().GetFields();
GUI.enabled = false;
{
foreach (var field in fields)
{
var fieldName = field.Name;
if (fieldName != targetFieldName)
{
continue;
}
if (field.IsStatic)
{
fieldName += " (static)";
}
var fieldType = field.FieldType;
if (IsArrayCustom(fieldType))
{
DrawElement($"▼{field.Name}", field.GetValue(target));
}
}
}
GUI.enabled = true;
}
serializedObject.ApplyModifiedProperties();
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
Draw2DArray(nameof(SampleClass.sampleArray2));
Draw2DArray(nameof(SampleClass.sampleArray3));
Draw2DArray(nameof(SampleClass.sampleArray4));
}
}
#endif
public class SampleClass : MonoBehaviour
{
public string[] sampleArray;
public string[][] sampleArray2;
public string[][] sampleArray3 = new string[1][];
public string[][][] sampleArray4 = new string[2][][] { new string[1][], new string[1][] };
// Start is called before the first frame update
void Start()
{
sampleArray2 = new string[8][];
for (int i = 0; i < sampleArray.Length; i++)
{
sampleArray2[i] = new string[8];
}
sampleArray3 = new string[2][];
for (int i = 0; i < sampleArray3.Length; i++)
{
sampleArray3[i] = new string[3];
for (int j = 0; j < sampleArray3[i].Length; j++)
{
sampleArray3[i][j] = $"text[{i}][{j}]";
}
}
sampleArray4 = new string[2][][];
for (int i = 0; i < sampleArray4.Length; i++)
{
sampleArray4[i] = new string[2][];
for (int j = 0; j < sampleArray4[i].Length; j++)
{
sampleArray4[i][j] = new string[2];
for (int k = 0; k < sampleArray4[i][j].Length; k++)
{
sampleArray4[i][j][k] = $"text[{i}][{j}][{k}]";
}
}
}
}
// Update is called once per frame
void Update()
{
}
}
오른쪽으로 한칸 들여쓰기
EditorGUI.indentLevel++;
{
//들어갈 내용
}
EditorGUI.indentLevel--;
#if UNITY_EDITOR
using UnityEditor;
[CustomEditor(typeof(Crosshair))]
public class CrosshairEditor : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
var targetClass = (Crosshair)target;
serializedObject.Update();
{
GUILayout.BeginHorizontal();
{
GUILayout.Label("Layer Mask");
var layerMask = targetClass.layerMask;
var layerNames = new List<string>();
for (int i = 0; i < 32; i++)
{
if ((layerMask & (1<<i))==0) //해당 레이어마스크가 미체크면 스킵
{
continue;
}
var layerName = LayerMask.LayerToName(i);
if (string.IsNullOrWhiteSpace(layerName))
{
layerName = i.ToString();
}
layerNames.Add(layerName);
}
var name = "";
switch (layerMask)
{
case 0: //Nothing
name = "Nothing";
break;
case -1: //Everything (-1,~0)
name = "Everything";
break;
default:
if (layerNames.Count>3)
{
name = "Mixed...";
}
else
{
name = string.Join(", ", layerNames);
}
break;
}
if (EditorGUILayout.DropdownButton(new GUIContent(name), FocusType.Passive))
{
var menu = new GenericMenu();
menu.AddItem(new GUIContent($"Nothing"), layerMask == 0, Callback, 0); //Nothing
menu.AddItem(new GUIContent($"Everything"), layerMask == -1, Callback, -1); //Everything
for (int i = 0; i < 32; i++)
{
var layerName = LayerMask.LayerToName(i);
var on = (layerMask & (1 << i)) != 0;
if (string.IsNullOrWhiteSpace(layerName))
{
continue;
}
menu.AddItem(new GUIContent($"{i}: {layerName}"), on, Callback, 1 << i);
}
menu.ShowAsContext();
void Callback(object obj)
{
switch ((int)obj)
{
case 0: //Nothing
case -1: //Everything (-1,~0)
targetClass.layerMask = (int)obj;
break;
default:
targetClass.layerMask ^= (int)obj;
break;
}
}
}
}
GUILayout.EndHorizontal();
}
serializedObject.ApplyModifiedProperties();
}
}
#endif
enum to dropdown 예제
아주 가끔 쓰인다
#if UNITY_EDITOR
using UnityEditor;
[CustomEditor(typeof(PickupSystem))]
public class PickupSystemInspecter : Editor
{
public enum Piece
{
None, Pawn, Knight, Bishop, Rook, Queen, King
}
public override void OnInspectorGUI()
{
GUI.enabled = false;
{
var script = MonoScript.FromMonoBehaviour((MonoBehaviour)target);
EditorGUILayout.ObjectField("Script", script, typeof(MonoScript), false);
}
GUI.enabled = true;
serializedObject.Update();
{
DrawPropertiesExcluding(serializedObject, "m_Script", nameof(PickupSystem.piece));
GUILayout.BeginHorizontal();
{
GUILayout.Label(nameof(PickupSystem.piece));
var pickupSystem = target as PickupSystem;
var names = System.Enum.GetNames(typeof(Piece));
if (EditorGUILayout.DropdownButton(new GUIContent(names[pickupSystem.piece]), FocusType.Passive))
{
var menu = new GenericMenu();
for (int i = 0; i < names.Length; i++)
{
if (string.IsNullOrWhiteSpace(names[i]))
{
continue;
}
menu.AddItem(new GUIContent($"{i}: {names[i]}"), false, Callback, i);
}
menu.ShowAsContext();
void Callback(object obj)
{
pickupSystem.piece = (int)obj;
}
}
}
GUILayout.EndHorizontal();
}
serializedObject.ApplyModifiedProperties();
}
}
#endif
텍스처 표시
{
var preview = AssetPreview.GetAssetPreview(loadComputeShader.renderTexture);
GUILayout.Label(preview);
EditorGUILayout.LabelField(nameof(LoadComputeShader.renderTexture));
}
MinMax슬라이더
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
#if UNITY_EDITOR
using UnityEditor;
[CustomEditor(typeof(SampleClass))]
public class SampleClassEditor : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();//기본 인스펙터
serializedObject.Update();
{
var sampleClass = (SampleClass)target;
var minLimit = sampleClass.minLimit;
var maxLimit = sampleClass.maxLimit;
EditorGUILayout.LabelField($"MinMaxSlider ({minLimit},{sampleClass.minVal},{sampleClass.maxVal},{maxLimit})");
EditorGUILayout.MinMaxSlider(ref sampleClass.minVal, ref sampleClass.maxVal, minLimit, maxLimit);
}
serializedObject.ApplyModifiedProperties();
}
}
#endif
public class SampleClass : MonoBehaviour
{
public float minVal = -10;
public float minLimit = -20;
public float maxVal = 10;
public float maxLimit = 20;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
//해당 변수가 어떤 역할인지 위에 굵은 글씨로 표시해줌
[Header("헤더이름")]
//슬라이더로 표시해줌
[Range(0.5f,1.5f)]
//슬라이더바가 있는 멀티라인
[TextArea(1,30)]
public string csvData;
//슬라이더바가 없는 고정된 멀티라인
[Mutiline]
public string text;
변수계열 어트리뷰트
//인스펙터의 변수1에 할당되어있던걸 변수2로 할당함
[UnityEngine.Serialization.FormerlySerializedAs("변수1")]
public int 변수2;
클래스계열 어트리뷰트
//해당 클래스의 인스펙터를 여러개 동시에 수정가능하도록 함
[CanEditMultipleObjects]
//항상 작동함
[ExecuteAlways]
//해당 클래스가 에디터상태에서도 동작함
[ExecuteInEditMode]
//에디터가 시작될때 호출됨
[UnityEditor.InitializeOnLoad]
//에디터가 시작될때 해당 메소드가 호출됨 static 메소드만 가능
[UnityEditor.InitializeOnLoadMethod]
//특정 컴포넌트를 강제로 추가시킴,클래스 위에 삽입함
[RequireComponent(typeof(Rigidbody))]
//없어질 함수라고 선언함, 클래스계열에도 사용가능하다
[System.Obsolete("미사용되는 함수입니다")]
//동일한 컴포넌트는 2개이상 적용 불가
[DisallowMultipleComponent]
함수계열 어트리뷰트
//이건 좀 특이하니 하단 참고
[ContextMenu("버튼명")]
-ContextMenu
public int motionCount;
#if UNITY_EDITOR
[ContextMenu("함수명")]
public void UpdateMotionCount()
{
motionCount = GetNumberMotionsInBlendTree(GetAnimator(),0,"Blend Tree");
}
#endif
인스펙터의 컴포넌트 상단에 대고 우클릭하면 함수를 실행할수있는 메뉴가 나옴 프로퍼티 드로워 버튼 찾지 말고 이거 쓰자
미분류
//인스펙터에서 숨기고 지정된 값도 지움, 그리고 디버그모드에서는 보임
//json등에서 public인데 저장안되게 할때도 사용
[System.NonSerialized]
//인스펙터에서 숨기고 지정된 값은 지우지 않음, 디버그모드에서도 안보임
[HideInInspector]
//원래는 보이지 않아야할 클래스나 구조체등을 보이게 함
//클래스나 구조체형을 인스펙터에 표시할때 사용
[System.Serializable]
//private 필드를 보이게 함
//인스펙터에서 수정은 가능한데 스크립트로는 못하게 하는것
[SerializeField]
//선언된 Define이 있을때만 작동, #if와 조금 다른점은 define이 없을때 에러가 나지 않는다
[System.Diagnostics.Conditional("UNITY_EDITOR")]