Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- java
- linux
- Camera Movement
- css framework
- --watch
- Unity IAP
- react
- express
- unity
- Unity Editor
- critical rendering path
- SDK upgrade
- OverTheWire
- spread 연산자
- docker
- Git
- draganddrop
- Digital Ocean
- server
- Camera Zoom
- Google Developer API
- nodejs
- mongoDB
- screencapture
- Spring Boot
- rpg server
- MySQL
- springboot
- Google Refund
- Packet Network
Archives
- Today
- Total
우당탕탕 개발일지
[Unity] CustomEditor - Dictionary 사용하기 본문
enum 인 SkillElement 와, Class인 ElementBonus에 대해 다음과같이 dictionary로 선언된 변수를 커스텀에디터에서 설정하고싶었다. 그러나 딕셔너리는 커스텀에디터에서 자동으로 직렬화를 해주지 않으므로, 직접 직렬화를 해줘야 한다.
private Dictionary<SkillElement, List<ElementBonus>> skillBonuses = new Dictionary<SkillElement, List<ElementBonus>>();
[System.Serializable]
public class ElementBonus
{
public int skillCount;
public float bonus;
}
오늘은 이 dictionary를 커스텀에디터에서 수정하고, 직렬화/역직렬화 함수를 통해 데이터가 손실되지 않는 방법을 알아보겠다.
1. 두 개의 List 생성하기
에디터는 dictionary를 직렬화하지 못하므로, key와 value를 나누어서 List로 변환한다.
[SerializeField] private List<SkillElement> keys = new List<SkillElement>();
[SerializeField] private List<ElementBonusList> values = new List<ElementBonusList>();
Value부분이 그냥 값이면 상관없지만, 나의 경우 Value부분이 List이다. 그러면 values부분이 List<List<ElementBonus>>인데, 이러면 또 직렬화가 안된다고 한다. 따라서 아래처럼 List를 감싸는 새로운 WrappingClass를 하나 만들어준다음, values에 넣어주었다. 만약 Value부분이 List로 이루어져있지 않은 경우는 그냥 원래Class로 진행하면된다.
// ✅ 리스트를 감싸는 직렬화 가능한 클래스 추가 (List<List<T>> 직렬화 문제 해결)
[System.Serializable]
public class ElementBonusList
{
public List<ElementBonus> list = new List<ElementBonus>();
public ElementBonusList(List<ElementBonus> elements)
{
list = new List<ElementBonus>(elements);
}
}
2. ISerializationCallbackReceiver상속받고 함수 구현하기
우선 ISerializationCallbackReceiver 를 상속받아서 OnBeforeSerialize() 와 OnDeserialize() 함수를 구현해준다.
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "NewSkillDamageBonusPreset", menuName = "Skill/SkillDamageBonusPreset")]
public class SkillDamageBonusPresetData : ScriptableObject, ISerializationCallbackReceiver
{
// ✅ Dictionary 대신 List 사용 (Unity에서 직렬화 가능하도록 변경)
[SerializeField] private List<SkillElement> keys = new List<SkillElement>();
[SerializeField] private List<ElementBonusList> values = new List<ElementBonusList>();
private Dictionary<SkillElement, List<ElementBonus>> skillBonuses = new Dictionary<SkillElement, List<ElementBonus>>();
public Dictionary<SkillElement, List<ElementBonus>> GetSkillBonuses()
{
return skillBonuses;
}
public void OnBeforeSerialize()
{
keys.Clear();
values.Clear();
foreach (var pair in skillBonuses)
{
keys.Add(pair.Key);
values.Add(new ElementBonusList(pair.Value)); // ✅ 리스트를 감싸는 클래스 사용
}
}
public void OnAfterDeserialize()
{
skillBonuses = new Dictionary<SkillElement, List<ElementBonus>>();
if (keys.Count != values.Count)
{
//Debug.LogError($"❌ 데이터 불일치 발생! keys({keys.Count}) / values({values.Count}) → 자동 복구");
return;
}
for (int i = 0; i < keys.Count; i++)
{
skillBonuses[keys[i]] = new List<ElementBonus>(values[i].list);
}
}
public float GetBonusDamage(SkillElement type, int count)
{
if (!skillBonuses.TryGetValue(type, out var list) || list == null)
{
return 0;
}
float value = 0f;
foreach (var item in list)
{
if (item.skillCount <= count && value < item.bonus) value = item.bonus;
if (item.skillCount == count) break;
}
return value;
}
}
3. 커스텀 에디터 구현하기
커스텀에디터에서 값을 수정/삭제/추가할때는 원래의 Dictionary에 값을 수정한다.
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.LabelField("🔥 스킬 속성별 데미지 보너스 설정 🔥", EditorStyles.boldLabel);
EditorGUILayout.Space();
var skillBonuses = preset.GetSkillBonuses();
foreach (SkillElement element in skillBonuses.Keys)
{
foldouts[element] = EditorGUILayout.Foldout(foldouts[element], GetElementIcon(element) + $" {element}", true, EditorStyles.boldLabel);
if (foldouts[element])
{
DrawElementSection(element, skillBonuses);
}
}
if (GUI.changed)
{
EditorUtility.SetDirty(preset);
}
serializedObject.ApplyModifiedProperties();
}
private void DrawElementSection(SkillElement element, Dictionary<SkillElement, List<ElementBonus>> skillBonuses)
{
EditorGUILayout.BeginVertical(GUI.skin.box);
List<ElementBonus> bonuses = skillBonuses[element];
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("보유 개수", GUILayout.Width(80));
EditorGUILayout.LabelField("데미지 증가 (%)", GUILayout.ExpandWidth(true));
EditorGUILayout.LabelField("", GUILayout.Width(50));
EditorGUILayout.EndHorizontal();
for (int i = 0; i < bonuses.Count; i++)
{
EditorGUILayout.BeginHorizontal();
bonuses[i].skillCount = EditorGUILayout.IntField(bonuses[i].skillCount, GUILayout.Width(80));
bonuses[i].bonus = EditorGUILayout.FloatField(bonuses[i].bonus, GUILayout.ExpandWidth(true));
if (GUILayout.Button("❌", GUILayout.Width(50)))
{
bonuses.RemoveAt(i);
EditorUtility.SetDirty(preset);
break;
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.Space(5);
if (GUILayout.Button("➕ 세트 추가", GUILayout.Height(25)))
{
int nextCount = bonuses.Count > 0 ? bonuses[bonuses.Count - 1].skillCount + 1 : 1;
bonuses.Add(new ElementBonus() { skillCount = nextCount, bonus = 0f });
EditorUtility.SetDirty(preset);
}
EditorGUILayout.Space(5);
EditorGUILayout.EndVertical();
}
'Unity' 카테고리의 다른 글
[커스텀 에디터] 파일 이름에서 ID추출 & 자동 ID부여하기 (0) | 2025.04.08 |
---|---|
[Unity] Custom Editor - enum값을 dropdown에서 선택하기 + 원하는값만 나타내기 (0) | 2025.03.17 |
[Unity] CustomEditor - 자동으로 addressable 활성화하기 (0) | 2025.03.17 |
[Unity][Build Err] ClassNotFoundException: com.google.android.gms.ads.initialization (0) | 2024.11.14 |
[Unity] ScreenShot 촬영 및 저장하기 (0) | 2024.08.11 |