어떤 인붕이가 string을 키값으로 쓰면서 에디터에서 오타나고 귀찮은 문제를 겪고있다는 글을 썼던데
나도 같은 문제를 겪다가 이 방법으로 해결했는데 아직까진 괜찮은 것 같아서 쓰고 있음.
나는 게임오브젝트의 컴포넌트에 어떤 딕셔너리의 키값을 에디터에서 직접 설정해서 프리팹을 만들거나 하는일이 자주 있었음.
가령 어떤 몬스터의 스탯 데이터를 연결하는 따위의 상황이다.
그런데 이 키값을 직접 입력하다보면 상당히 귀찮고 오류도 많이 생긴다.
그렇다고 하드코딩을 하기는 싫고 해서 대안으로 생각한것이 있다.
기본적인 아이디어는
1. 데이터테이블에서 키값을 가지는 에디터에서만 유효한 static 클래스 코드를 자동생성
2. 컴포넌트의 string 필드에 1에서 생성한 클래스의 스트링들을 드랍다운으로 선택할 수 있게하는 어트리뷰트 사용
이렇게 하면 런타임에서는 일반 string을 사용하되
에디터에서만 드랍다운의 형태로 할당을 좀 더 편하게 할 수 있게 된다.
내가 생각하는 SO에 비해 가지는 장점이라고 해야할지 모르겠지만
SO는 파일이라고 생각되서 그런지 갯수가 많아지면 1인개발인 입장에서 조금 관리하기가 까다로운 것 같음.
내가 이상하게 사용해서 그런지, 본 용도대로 제대로 사용을 못해서 그런지도 모르겠음.
SO파일의 네이밍 규칙정하기나 어느 폴더에 정리해야할지 이런것을 전부 생각하게되고
데이터 테이블의 키값을 바꾸면 또 파일 이름도 바꿔주고 여러모로 번거로웠다.
{
public static string KnightHelm = "Knight Helm";
public static string WinterTravelersHood = "Winter Travelers Hood";
}
아무튼 데이터테이블을 스프레드시트에서 변경하면
일단 이런 static class에 static string이나 const string을 사용하는 형태의 코드가 자동생성된다.
엄밀히 말하면 반자동생성이다. 매번 테이블이 변경될 때마다 코드 자동생성할 필요는 없으니. 필요한때만 재생성한다.
그리고 이 코드는 에디터 전용으로 #if UNITY_EDITOR를 사용해서 쓴다. 다른 코드에서는 절대 사용하지 않는다.
그리고 나는 아래의 코드를 아주 좋아하게 되었음.
public class StaticClassDropdownAttribute : PropertyAttribute
{
public Type TargetType { get; }
public StaticClassDropdownAttribute(Type targetType)
{
if (!targetType.IsClass || !targetType.IsAbstract)
{
throw new ArgumentException("TargetType must be a static class");
}
TargetType = targetType;
}
}
[CustomPropertyDrawer(typeof(StaticClassDropdownAttribute))]
public class StaticClassDropdownDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
var dropdownAttribute = (StaticClassDropdownAttribute)attribute;
// 정적 클래스에서 필드 이름과 값을 가져옴
var options = new List<string> { "---" }; // 기본값
var fields = dropdownAttribute.TargetType.GetFields(BindingFlags.Public | BindingFlags.Static);
foreach (var field in fields)
{
if (field.FieldType == typeof(string) && field.GetValue(null) is string value)
{
if (!options.Contains(value)) // 중복 방지
{
options.Add(value);
}
}
}
// 현재 값
string currentValue = property.stringValue;
// 현재 값 인덱스 확인
int currentIndex = string.IsNullOrEmpty(currentValue) ? 0 : options.IndexOf(currentValue);
// 값이 드롭다운에서 찾을 수 없는 경우
bool isInvalidValue = currentIndex == -1 && !string.IsNullOrEmpty(currentValue);
if (isInvalidValue)
{
currentIndex = 0; // 기본값(---)으로 설정
}
// 드롭다운 표시
int selectedIndex = EditorGUI.Popup(position, label.text, currentIndex, options.ToArray());
// 선택된 값에 따라 동작
if (selectedIndex > 0) // 유효한 값을 선택한 경우에만 값 변경
{
property.stringValue = options[selectedIndex];
}
// 유효하지 않은 값 빨간색으로 표시
if (isInvalidValue)
{
Rect warningRect = new Rect(position.x, position.y + position.height + 2, position.width,
position.height);
GUIStyle warningStyle = new GUIStyle(EditorStyles.label) { normal = { textColor = Color.red } };
EditorGUI.LabelField(warningRect, $"Invalid Value: {currentValue}", warningStyle);
}
}
}
아까 선언했던 스태틱 클래스를 이용해 드랍박스로 만들어주는건데 클래스에 상관없이 사용할 수 있다.
코드에 대한 자세한 설명은 생략한다. 궁금한 사람은 챗지피티한테 물어보면 되겠다.
그럼 이것을 어떻게 사용하는가
[StaticClassDropdown(typeof(EntityDataKey))]
#endif
[SerializeField] protected string initialEntityDataKey;
이렇게 사용하면 된다. 원래 필드 앞에 저렇게 써주면 된다 (에디터에서만 작동하는거니 #if도 써줬다).
내가 드랍다운에서 선택할 때만 새로운 값이 할당되는것이다.
만약 키값이 바뀌어서 실제 할당되어있던 값이 유효하지 않으면 경고를 주는 기능도 넣었다.
가령 skeleton_D라는 데이터가 있었는데 테이블에서 날려버리고 난 경우임.
나중에는 이렇게 유효하지 않은 값이 있는지 프로젝트를 검사하는 기능을 넣어볼까 하는데. 그게 나을지 다른 방법을 모색해야할지는 아직 모르겠음.
4년째 깡통차고 개발하고 있는데 갈길이 멀다. 좋은 조언있으면 언제든 부탁함.
흠.. 이미 너무 먼길을 와버렸지만... 감사합니다 에셋레퍼런스 써보려고용...
해보고 좋은거 있음 알려주
와 왕고수맨 ㄷㄷ - dc App
내가 아니고 챗지피티가 고수임
그래도 스스로 문제를 해결해내는 방법을 찾아내는거 부터 왕고수맨 - dc App
칭찬 감사감사 게임 대박나자!
StaticClassDropdownDrawer 클래스만 Editor폴더에 넣거나 전처리기로 감싸면 굳이 사용할때 마다 UNITY_EDITOR로 감쌀 필요 없을거임
오 좋은정보 감사감사 코드 꼴배기 싫었는데 잘됐다
이거보고 바로 다 지웠다
유니티는 FName같은 거 없나?
c++이나 언리얼 잘 몰라서 뭔지 모르것다. 여기서는 그냥 단순한 컴포넌트 클래스의 스트링 멤버변수(필드) 값 설정 방법만 에디터에서 커스텀에디터로 통제하는것뿐임.
스트링을 키로 쓸려면 해시값을 만들어서 쓰는게 빠르지 않을까 생각해. Lua가 이렇게 해서 성능을 향상시켰어.