292 lines
12 KiB
C#
292 lines
12 KiB
C#
|
|
#if UNITY_EDITOR
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEditor;
|
|
using UnityEditor.Animations;
|
|
|
|
namespace Bitd
|
|
{
|
|
public class FunctionAdder : EditorWindow
|
|
{
|
|
AnimatorController animatorController;
|
|
string layerName = string.Empty;
|
|
string parameterName = string.Empty;
|
|
List<AnimationClip> animationClips = new List<AnimationClip>(); // List로 변경하여 동적으로 관리
|
|
AnimationClip onAnimationClip = null; // On 애니메이션
|
|
AnimationClip offAnimationClip = null; // Off 애니메이션
|
|
bool isOnOffMode = false; // 온오프 모드 여부를 결정하는 변수
|
|
private bool isHair = false;
|
|
private bool isClothes = false;
|
|
|
|
[MenuItem("Bitd/애니메이터 기능 등록", false, 51)]
|
|
public static void ShowWindow()
|
|
{
|
|
GetWindow<FunctionAdder>("뜌땨뜌~");
|
|
}
|
|
|
|
void OnGUI()
|
|
{
|
|
GUILayout.Label("기능 등록 툴", EditorStyles.boldLabel);
|
|
|
|
// 애니메이터 선택 필드
|
|
animatorController = (AnimatorController)EditorGUILayout.ObjectField("타겟 애니메이터", animatorController, typeof(AnimatorController), false);
|
|
|
|
isHair = EditorGUILayout.Toggle("헤어인가요?", isHair);
|
|
isClothes = EditorGUILayout.Toggle("옷인가요?", isClothes);
|
|
GUILayout.Space(5);
|
|
layerName = EditorGUILayout.TextField("레이어 이름", layerName);
|
|
parameterName = EditorGUILayout.TextField("파라미터 이름", parameterName);
|
|
GUILayout.Space(5);
|
|
if (isHair)
|
|
{
|
|
layerName = "Hair Change";
|
|
parameterName = "HairIndex";
|
|
}
|
|
else if (isClothes)
|
|
{
|
|
layerName = "Clothes Change";
|
|
parameterName = "ClothesIndex";
|
|
}
|
|
|
|
isOnOffMode = EditorGUILayout.Toggle("온오프인가요?", isOnOffMode);
|
|
|
|
// 온오프 모드에 따른 UI 처리
|
|
if (isOnOffMode)
|
|
{
|
|
// 온오프 모드일 때 On, Off 애니메이션 선택 영역
|
|
onAnimationClip = (AnimationClip)EditorGUILayout.ObjectField("On 애니메이션", onAnimationClip, typeof(AnimationClip), false);
|
|
offAnimationClip = (AnimationClip)EditorGUILayout.ObjectField("Off 애니메이션", offAnimationClip, typeof(AnimationClip), false);
|
|
}
|
|
else
|
|
{
|
|
// 드래그 앤 드롭 구역 생성
|
|
GUILayout.Space(10);
|
|
EditorGUILayout.HelpBox("애니메이션 클립을 드래그하여 추가하세요.", MessageType.Info);
|
|
Rect dropArea = GUILayoutUtility.GetRect(0, 50, GUILayout.ExpandWidth(true));
|
|
GUI.Box(dropArea, "여기에 넣어주세요", EditorStyles.helpBox);
|
|
HandleDragAndDrop(dropArea);
|
|
|
|
// 드래그된 애니메이션 클립 표시
|
|
GUILayout.Label("추가된 애니메이션 클립:", EditorStyles.boldLabel);
|
|
for (int i = 0; i < animationClips.Count; i++)
|
|
{
|
|
EditorGUILayout.ObjectField(animationClips[i], typeof(AnimationClip), false);
|
|
}
|
|
}
|
|
|
|
if (GUILayout.Button("적용"))
|
|
{
|
|
if (animatorController != null)
|
|
{
|
|
if (isOnOffMode && onAnimationClip != null && offAnimationClip != null)
|
|
{
|
|
AddLayerAndSetupOnOffAnimations(animatorController, layerName, onAnimationClip, offAnimationClip, parameterName);
|
|
}
|
|
else if (!isOnOffMode && animationClips.Count > 0)
|
|
{
|
|
AddLayerAndSetupAnimations(animatorController, layerName, animationClips.ToArray(), parameterName);
|
|
animationClips = new List<AnimationClip>();
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning("애니메이터나 애니메이션 클립이 부족합니다.");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void HandleDragAndDrop(Rect dropArea)
|
|
{
|
|
Event evt = Event.current;
|
|
|
|
switch (evt.type)
|
|
{
|
|
case EventType.DragUpdated:
|
|
case EventType.DragPerform:
|
|
if (!dropArea.Contains(evt.mousePosition))
|
|
return;
|
|
|
|
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
|
|
|
|
if (evt.type == EventType.DragPerform)
|
|
{
|
|
DragAndDrop.AcceptDrag();
|
|
|
|
foreach (Object draggedObject in DragAndDrop.objectReferences)
|
|
{
|
|
if (draggedObject is AnimationClip)
|
|
{
|
|
animationClips.Add((AnimationClip)draggedObject); // 애니메이션 클립 추가
|
|
}
|
|
}
|
|
|
|
evt.Use();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 온오프 애니메이션 설정
|
|
void AddLayerAndSetupOnOffAnimations(AnimatorController animatorController, string layerName, AnimationClip onClip, AnimationClip offClip, string parameterName)
|
|
{
|
|
// 레이어가 존재하는지 확인
|
|
AnimatorControllerLayer targetLayer = null;
|
|
foreach (var layer in animatorController.layers)
|
|
{
|
|
if (layer.name == layerName)
|
|
{
|
|
targetLayer = layer;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 레이어가 없으면 새로 생성
|
|
if (targetLayer == null)
|
|
{
|
|
targetLayer = new AnimatorControllerLayer
|
|
{
|
|
name = layerName,
|
|
stateMachine = new AnimatorStateMachine(),
|
|
defaultWeight = 1
|
|
};
|
|
animatorController.AddLayer(targetLayer);
|
|
|
|
targetLayer.stateMachine.name = $"{layerName}_StateMachine";
|
|
|
|
// 생성한 stateMachine을 애셋으로 저장
|
|
AssetDatabase.AddObjectToAsset(targetLayer.stateMachine, animatorController);
|
|
}
|
|
else if (targetLayer.stateMachine == null) // 기존 레이어가 있지만 stateMachine이 없으면 새로 생성
|
|
{
|
|
targetLayer.stateMachine = new AnimatorStateMachine();
|
|
targetLayer.stateMachine.name = $"{layerName}_StateMachine";
|
|
AssetDatabase.AddObjectToAsset(targetLayer.stateMachine, animatorController);
|
|
}
|
|
|
|
// 애셋을 저장하여 유니티 에디터가 변경사항을 잃지 않도록 보장
|
|
AssetDatabase.SaveAssets();
|
|
|
|
// StateMachine 참조
|
|
AnimatorStateMachine stateMachine = targetLayer.stateMachine;
|
|
|
|
// 파라미터가 있는지 확인하고 없으면 생성 (bool 타입)
|
|
if (!HasParameter(animatorController, parameterName))
|
|
{
|
|
animatorController.AddParameter(parameterName, AnimatorControllerParameterType.Bool);
|
|
}
|
|
|
|
// On 상태 추가
|
|
AnimatorState onState = stateMachine.AddState(onClip.name);
|
|
onState.motion = onClip;
|
|
|
|
// Off 상태 추가
|
|
AnimatorState offState = stateMachine.AddState(offClip.name);
|
|
offState.motion = offClip;
|
|
|
|
// On에서 Off로의 트랜지션 추가 (false일 때)
|
|
AnimatorStateTransition onToOffTransition = onState.AddTransition(offState);
|
|
onToOffTransition.exitTime = 0;
|
|
onToOffTransition.hasExitTime = false;
|
|
onToOffTransition.hasFixedDuration = false;
|
|
onToOffTransition.duration = 0;
|
|
onToOffTransition.AddCondition(AnimatorConditionMode.IfNot, 0, parameterName); // 파라미터가 false일 때
|
|
|
|
// Off에서 On으로의 트랜지션 추가 (true일 때)
|
|
AnimatorStateTransition offToOnTransition = offState.AddTransition(onState);
|
|
offToOnTransition.exitTime = 0;
|
|
offToOnTransition.hasExitTime = false;
|
|
offToOnTransition.hasFixedDuration = false;
|
|
offToOnTransition.duration = 0;
|
|
offToOnTransition.AddCondition(AnimatorConditionMode.If, 0, parameterName); // 파라미터가 true일 때
|
|
|
|
Debug.Log("On/Off 애니메이션 세팅 완료.");
|
|
}
|
|
void AddLayerAndSetupAnimations(AnimatorController animatorController, string layerName, AnimationClip[] animationClips, string parameterName)
|
|
{
|
|
// 레이어가 존재하는지 확인
|
|
AnimatorControllerLayer targetLayer = null;
|
|
foreach (var layer in animatorController.layers)
|
|
{
|
|
if (layer.name == layerName)
|
|
{
|
|
targetLayer = layer;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 레이어가 없으면 새로 생성
|
|
if (targetLayer == null)
|
|
{
|
|
targetLayer = new AnimatorControllerLayer
|
|
{
|
|
name = layerName,
|
|
stateMachine = new AnimatorStateMachine(),
|
|
defaultWeight = 1
|
|
};
|
|
animatorController.AddLayer(targetLayer);
|
|
|
|
targetLayer.stateMachine.name = $"{layerName}_StateMachine";
|
|
|
|
// 생성한 stateMachine을 애셋으로 저장
|
|
AssetDatabase.AddObjectToAsset(targetLayer.stateMachine, animatorController);
|
|
}
|
|
else if (targetLayer.stateMachine == null) // 기존 레이어가 있지만 stateMachine이 없으면 새로 생성
|
|
{
|
|
targetLayer.stateMachine = new AnimatorStateMachine();
|
|
targetLayer.stateMachine.name = $"{layerName}_StateMachine";
|
|
AssetDatabase.AddObjectToAsset(targetLayer.stateMachine, animatorController);
|
|
}
|
|
|
|
// 애셋을 저장하여 유니티 에디터가 변경사항을 잃지 않도록 보장
|
|
AssetDatabase.SaveAssets();
|
|
|
|
// Any State로부터의 전환을 위한 StateMachine 참조
|
|
AnimatorStateMachine stateMachine = targetLayer.stateMachine;
|
|
|
|
// 파라미터가 있는지 확인하고 없으면 생성
|
|
if (!HasParameter(animatorController, parameterName))
|
|
{
|
|
animatorController.AddParameter(parameterName, AnimatorControllerParameterType.Int);
|
|
}
|
|
|
|
// 기존 레이어의 애니메이션 상태 개수 확인
|
|
int existingStateCount = stateMachine.states.Length;
|
|
|
|
// 애니메이션 상태 생성 및 설정
|
|
for (int i = 0; i < animationClips.Length; i++)
|
|
{
|
|
AnimationClip clip = animationClips[i];
|
|
|
|
// 애니메이션 상태 추가
|
|
AnimatorState state = stateMachine.AddState(clip.name);
|
|
state.motion = clip;
|
|
|
|
// Any State에서 해당 애니메이션 상태로 전환 추가
|
|
AnimatorStateTransition transition = stateMachine.AddAnyStateTransition(state);
|
|
transition.exitTime = 0;
|
|
transition.hasExitTime = false;
|
|
transition.hasFixedDuration = false;
|
|
transition.duration = 0;
|
|
|
|
// 기존 상태 개수를 고려하여 인덱스를 설정
|
|
transition.AddCondition(AnimatorConditionMode.Equals, existingStateCount + i, parameterName);
|
|
}
|
|
Debug.Log("Layer and animations successfully set up.");
|
|
}
|
|
|
|
// 파라미터가 이미 있는지 확인하는 함수
|
|
bool HasParameter(AnimatorController animatorController, string paramName)
|
|
{
|
|
foreach (var param in animatorController.parameters)
|
|
{
|
|
if (param.name == paramName)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
}
|
|
#endif |