서론
- 현재 기존 작업 유지보수, 사이드 프로젝트, 신규 프로젝트 준비를 동시에 진행하고 있다.
AI가 발전하면서 개발 속도는 빨라지고 있지만, 반복되는 작업에 들어가는 시간은 여전히 같다. 이 시간을 단축할 수 있다면 좋지 않을까? 싶고 또한 협업에도 도움이 될 것 같아 해당 툴을 제작하게 되었다.
제작하면서 새롭게 알게 된 기능들을 정리하고 기억하기 위해 글을 작성한다.
UnityEditor. AssetDatabase
- 에디터 안에서 프로젝트의 에셋(프리팹, 스크립트, 텍스처, 사운드 등)을 검색·생성·이동·삭제·가져오기(Import)·메타데이터 관리 등을 할 수 있게 해주는 에디터 전용(런타임 불가) 클래스다.
기능
- 검색
- FindAssets(filter) : 타입/라벨/이름 필터로 GUID 목록 검색
- GetDependencies(path, recursive) : 에셋이 참조하는 의존성 목록 조회
- 로드
- LoadAssetAtPath<T>(path) : 경로로 에셋 객체 로드
- LoadAllAssetsAtPath(path) : 서브에셋 포함 로드
- 생성/추가
- CreateAsset(obj, path) : 새 에셋 파일 생성
- AddObjectToAsset(obj, path) : 기존 에셋에 서브오브젝트로 추가
- CreateFolder(parent, newFolderName) : 폴더 생성
- 이동/복제/삭제/이름 변경
- MoveAsset(oldPath, newPath)
- DuplicateAsset(path, newPath)
- DeleteAsset(path)
- RenameAsset(path, newName)
- 메타/경로/식별자
- GUIDToAssetPath(guid), AssetPathToGUID(path)
- TryGetGUIDAndLocalFileIdentifier(obj, out guid, out localId)
- GetAssetPath(obj) / IsMainAsset(obj) / GetMainAssetTypeAtPath(path)
- 가져오기/리프레시/저장
- ImportAsset(path, ImportAssetOptions)
- Refresh() : 디스크 변경 사항 스캔
- SaveAssets() : 메타/프로젝트 저장
- 대량 작업 최적화: StartAssetEditing() / StopAssetEditing()
- 라벨 관리
- GetLabels(obj) / SetLabels(obj, labels)
- 검증
- IsValidFolder(path), Contains(obj)
에디터에서만 사용해야 하므로 전처리기를 통해 분기 처리한다.
AddressableAssetGroup
Addressables 시스템에서 에셋을 묶어 **빌드/배포 단위(AssetBundle 묶음 + 카탈로그 메타)**로 관리하는 컨테이너이다.
- 각 그룹은 스키마(Schema) 설정을 통해 빌드/로딩 경로, 압축, 해시, 캐싱, 콘텐츠 업데이트 전략 등을 제어합니다.
- 그룹 안에는 여러 **엔트리(Entry)**가 있으며, 각 엔트리는 특정 에셋(프리팹/사운드/텍스처 등)과 그 주소(Address), 라벨(Label) 정보를 가진다.
해당 클래스를 통해 그룹을 새로 만들거나 확장하는 메소드를 예시로 작성했다.
/// <summary>
/// Addressables.CreateGroup의 다양한 오버로드를 탐색하고 가장 적합한 것을 호출한다.
/// 지원 대상(우선순위 순서):
/// 1) CreateGroup(string, bool, bool, bool, params Type[] types)
/// 2) CreateGroup(string, bool, bool, bool, IEnumerable<AddressableAssetGroupSchema> schemasToCopy, Type[] types)
/// 3) CreateGroup(string, bool, bool, bool, List<AddressableAssetGroupSchema> schemasToCopy)
/// </summary>
private static AddressableAssetGroup CreateGroupCompat(AddressableAssetSettings settings, string groupName, Type[] schemaTypes)
{
var t = typeof(AddressableAssetSettings);
var methods = t.GetMethods(BindingFlags.Instance | BindingFlags.Public);
MethodInfo best = null;
// 1) params Type[] 단독
best = methods.FirstOrDefault(m =>
{
if (m.Name != "CreateGroup")
{
return false;
}
var ps = m.GetParameters();
if (ps.Length != 5)
{
return false;
}
return ps[0].ParameterType == typeof(string)
&& ps[1].ParameterType == typeof(bool)
&& ps[2].ParameterType == typeof(bool)
&& ps[3].ParameterType == typeof(bool)
&& ps[4].ParameterType.IsArray
&& ps[4].ParameterType.GetElementType() == typeof(Type);
});
if (best != null)
{
object[] args =
{
groupName,
false,
false,
false,
schemaTypes
};
return (AddressableAssetGroup)best.Invoke(settings, args);
}
// 2) IEnumerable<AddressableAssetGroupSchema>, Type[]
best = methods.FirstOrDefault(m =>
{
if (m.Name != "CreateGroup")
{
return false;
}
var ps = m.GetParameters();
if (ps.Length != 6)
{
return false;
}
bool fifthOk = typeof(System.Collections.Generic.IEnumerable<AddressableAssetGroupSchema>).IsAssignableFrom(ps[4].ParameterType);
bool sixthOk = ps[5].ParameterType.IsArray && ps[5].ParameterType.GetElementType() == typeof(Type);
return ps[0].ParameterType == typeof(string)
&& ps[1].ParameterType == typeof(bool)
&& ps[2].ParameterType == typeof(bool)
&& ps[3].ParameterType == typeof(bool)
&& fifthOk
&& sixthOk;
});
if (best != null)
{
object[] args =
{
groupName,
false,
false,
false,
null, // schemasToCopyFrom 없음
schemaTypes // 붙일 스키마 타입들
};
return (AddressableAssetGroup)best.Invoke(settings, args);
}
// 3) List<AddressableAssetGroupSchema> 단독
best = methods.FirstOrDefault(m =>
{
if (m.Name != "CreateGroup")
{
return false;
}
var ps = m.GetParameters();
if (ps.Length != 5)
{
return false;
}
bool fifthOk = typeof(List<AddressableAssetGroupSchema>).IsAssignableFrom(ps[4].ParameterType);
return ps[0].ParameterType == typeof(string)
&& ps[1].ParameterType == typeof(bool)
&& ps[2].ParameterType == typeof(bool)
&& ps[3].ParameterType == typeof(bool)
&& fifthOk;
});
if (best != null)
{
object[] args =
{
groupName,
false,
false,
false,
new List<AddressableAssetGroupSchema>() // 빈 목록 전달
};
var group = (AddressableAssetGroup)best.Invoke(settings, args);
// 스키마는 수동으로 Add
if (group != null)
{
EnsureDefaultSchemas(group);
}
return group;
}
// 찾지 못함
return null;
}
SystemIO
- 새로 글 작성 예정.
큰 타이틀로 나누면 이 정도로 정리할 수 있을 것 같다.
아래는 새로 만든 툴을 사용한 영상이다.
사운드가 포함돼 있습니다.
'유니티' 카테고리의 다른 글
| 오디오 믹서 (Aduio Mixer) (1) | 2025.09.14 |
|---|---|
| Custom Editor (0) | 2025.08.30 |
| 스크립팅 백엔드 (2) | 2025.03.30 |
| RectTransformUtility (3) | 2025.03.19 |
| RectTransformUntility에 대해서 (17) | 2025.01.07 |