using UnityEditor;
using UnityEditor.Animations;
using UnityEditor.SceneManagement;
using UnityEngine;
///
/// 애니메이션 배선을 씬에 넣고, 상태 이름이 맞춰진 빈 Animator Controller 를 만들어 준다.
///
/// 컨트롤러를 손으로 만들면 상태 이름을 CharacterMotionDirector 의 필드와 일일이
/// 맞춰야 하는데, 오타 하나면 조용히 아무 일도 안 일어난다. 그 자리를 없앤다.
///
public static class CharacterMotionSetupMenu
{
const string ObjectName = "CharacterMotion";
const string ControllerPath = "Assets/99_Settings/CharacterMotion.controller";
const string OccluderShaderPath = "Assets/03_Shaders/PlatformOccluder.shader";
[MenuItem("Tools/Desktop Overlay/10. Add Character Motion To Scene")]
public static void AddMotionDirector()
{
// 이미 있어도 그냥 돌아가지 않는다. 나중에 추가된 컴포넌트(가림, 포즈 메뉴)가
// 빠져 있을 수 있으므로, 있는 것은 두고 없는 것만 채워 넣는다.
var existing = Object.FindFirstObjectByType();
GameObject go;
if (existing != null)
{
go = existing.gameObject;
}
else
{
go = new GameObject(ObjectName);
Undo.RegisterCreatedObjectUndo(go, "Add Character Motion");
go.AddComponent();
}
// 발판 뒤로 숨기기용 사각형. 셰이더는 직접 참조해야 빌드에서 빠지지 않는다.
if (Object.FindFirstObjectByType() == null)
{
var occluder = Undo.AddComponent(go);
var shader = AssetDatabase.LoadAssetAtPath(OccluderShaderPath);
if (shader != null)
{
var so = new SerializedObject(occluder);
so.FindProperty("occluderShader").objectReferenceValue = shader;
so.ApplyModifiedProperties();
}
else
{
Debug.LogWarning($"[CharacterMotionSetupMenu] 셰이더를 찾지 못했습니다: {OccluderShaderPath}");
}
}
// 우클릭 포즈 목록.
if (Object.FindFirstObjectByType() == null)
{
Undo.AddComponent(go);
}
Selection.activeGameObject = go;
EditorSceneManager.MarkSceneDirty(go.scene);
Debug.Log(
"[CharacterMotionSetupMenu] CharacterMotion 을 추가했습니다. 씬을 저장(Ctrl+S)하세요.\n" +
"Motion Controller 가 비어 있으면 지금처럼 ProceduralIdle 로 동작합니다.\n" +
"메뉴 11 로 빈 컨트롤러를 만들고 클립을 채워 넣으세요.");
}
[MenuItem("Tools/Desktop Overlay/12. Add Frame Rate Limiter To Scene")]
public static void AddFrameRateLimiter()
{
var existing = Object.FindFirstObjectByType();
if (existing != null)
{
Selection.activeGameObject = existing.gameObject;
EditorGUIUtility.PingObject(existing.gameObject);
Debug.Log($"[CharacterMotionSetupMenu] 이미 있습니다: {existing.gameObject.name}");
return;
}
// 카메라(TransparentWindow) 옆에 두는 게 자연스럽다. 없으면 새 오브젝트.
var window = Object.FindFirstObjectByType();
GameObject host;
if (window != null)
{
host = window.gameObject;
Undo.AddComponent(host);
}
else
{
host = new GameObject("FrameRateLimiter");
Undo.RegisterCreatedObjectUndo(host, "Add Frame Rate Limiter");
host.AddComponent();
}
Selection.activeGameObject = host;
EditorSceneManager.MarkSceneDirty(host.scene);
Debug.Log(
"[CharacterMotionSetupMenu] FrameRateLimiter 를 추가했습니다. 씬을 저장(Ctrl+S)하세요.\n" +
"이게 없으면 상시 구동 중에 낼 수 있는 최대 FPS 로 계속 렌더링합니다.");
}
[MenuItem("Tools/Desktop Overlay/11. Create Motion Controller")]
public static void CreateController()
{
if (AssetDatabase.LoadAssetAtPath(ControllerPath) != null)
{
Debug.LogWarning($"[CharacterMotionSetupMenu] 이미 있습니다: {ControllerPath}\n" +
"덮어쓰지 않습니다. 지우고 다시 실행하세요.");
Selection.activeObject = AssetDatabase.LoadAssetAtPath