using UnityEngine;
using System.Collections.Generic;
using System.Linq;
#if
using UnityEditor;
using UnityEditor.Animations;
using AD = UnityEditor.AssetDatabase;
using EGL = UnityEditor.EditorGUILayout;
#endif
/// <summary>
/// キャラクタ
/// </summary>
[RequireComponent(typeof(SpriteRenderer))]
[RequireComponent(typeof(Animator))]
public class Character2 : MonoBehaviour
{
#if
/// <summary>画像</summary>
public Texture2D texture;
/// <summary>
/// エディタ拡張
/// </summary>
[CustomEditor(typeof(Character2))]
[CanEditMultipleObjects]
public class CharacterEditor : Editor
{
/// <summary>
/// 設定
/// </summary>
private static class Settings
{
/// <summary>アニメーション設定</summary>
public static List<AnimationSetting> animations =
new List<AnimationSetting>()
{
new AnimationSetting("IdolDown",new List<int>() {1}, 1, false, "IdolTree", 0, -1),
new AnimationSetting("IdolLeft",new List<int>() {4}, 1, false, "IdolTree", -1, 0),
new AnimationSetting("IdolRight",new List<int>() {7}, 1, false, "IdolTree", 1, 0),
new AnimationSetting("IdolUp",new List<int>() {10}, 1, false, "IdolTree", 0, 1),
new AnimationSetting("WalkDown",new List<int>() {0,1,2,1}, 4, true, "WalkTree", 0, -1),
new AnimationSetting("WalkLeft",new List<int>() {3,4,5,4}, 4, true, "WalkTree", -1, 0),
new AnimationSetting("WalkRight",new List<int>() {6,7,8,7}, 4, true, "WalkTree", 1, 0),
new AnimationSetting("WalkUp",new List<int>() {9,10,11,10}, 4, true, "WalkTree", 0, 1),
};
/// <summary>パラメータ</summary>
public static List<AnimatorControllerParameter> parameters =
new List<AnimatorControllerParameter>()
{
new AnimatorControllerParameter()
{
name = "DirectionX",
type = AnimatorControllerParameterType.Float,
defaultFloat = 0f
},
new AnimatorControllerParameter()
{
name = "DirectionY",
type = AnimatorControllerParameterType.Float,
defaultFloat = 0f
},
new AnimatorControllerParameter()
{
name = "Walking",
type = AnimatorControllerParameterType.Bool,
defaultBool = false
},
};
/// <summary>BlendTree</summary>
public static List<BlendTreeSetting> blendTrees =
new List<BlendTreeSetting>()
{
new BlendTreeSetting(
"IdolTree",
BlendTreeType.SimpleDirectional2D,
parameters[0].name,
parameters[1].name),
new BlendTreeSetting(
"WalkTree",
BlendTreeType.SimpleDirectional2D,
parameters[0].name,
parameters[1].name),
};
/// <summary>遷移</summary>
public static List<TransitionSetting> transitions =
new List<TransitionSetting>()
{
new TransitionSetting(
"IdolTree",
"WalkTree",
parameters[2].name,
AnimatorConditionMode.If,
0),
new TransitionSetting(
"WalkTree",
"IdolTree",
parameters[2].name,
AnimatorConditionMode.IfNot,
0),
};
}
/// <summary>
/// アニメーションの設定
/// </summary>
private class AnimationSetting
{
/// <summary>名前</summary>
public string Name { get; set; }
/// <summary>スプライトのインデックスリスト</summary>
public List<int> SpriteIndexes { get; set; }
/// <summary>フレームレート</summary>
public int FrameRate { get; set; }
/// <summary>ループフラグ</summary>
public bool Loop { get; set; }
/// <summary>登録するBlendTree名</summary>
public string BlendTreeName { get; set; }
/// <summary>登録するBlendTree座標</summary>
public Vector2 BlendTreePosition { get; set; }
public AnimationSetting(
string name,
List<int> spriteIndexes,
int frameRate,
bool loop,
string blendTreeName,
Vector2 blendTreePosition)
{
Name = name;
SpriteIndexes = spriteIndexes;
FrameRate = frameRate;
Loop = loop;
BlendTreeName = blendTreeName;
BlendTreePosition = blendTreePosition;
}
public AnimationSetting(
string name,
List<int> spriteIndexes,
int frameRate,
bool loop,
string blendTreeName,
float blendParameterX,
float blendParameterY)
: this(name, spriteIndexes, frameRate, loop, blendTreeName, new Vector2(blendParameterX, blendParameterY))
{ }
public AnimationSetting(
string name,
List<int> spriteIndexes,
int frameRate,
bool loop)
: this(name, spriteIndexes, frameRate, loop, "", Vector2.zero)
{ }
}
/// <summary>
/// フレーム情報
/// </summary>
private class AnimationFrame
{
/// <summary>スプライト</summary>
public Sprite Sprite { get; set; }
/// <summary>フレーム番号</summary>
public int FrameNo { get; set; }
public AnimationFrame(Sprite sprite, int frameNo)
{
Sprite = sprite;
FrameNo = frameNo;
}
}
/// <summary>
/// BlendTree設定
/// </summary>
private class BlendTreeSetting
{
/// <summary>名前</summary>
public string Name { get; set; }
/// <summary>種別</summary>
public BlendTreeType BlendType { get; set; }
/// <summary>X軸パラメータ</summary>
public string BlendParameterX { get; set; }
/// <summary>Y軸パラメータ</summary>
public string BlendParameterY { get; set; }
public BlendTreeSetting(
string name,
BlendTreeType blendType,
string blendParameterX,
string blendParameterY)
{
Name = name;
BlendType = blendType;
BlendParameterX = blendParameterX;
BlendParameterY = blendParameterY;
}
}
/// <summary>
/// 遷移情報
/// </summary>
private class TransitionSetting
{
/// <summary>遷移元</summary>
public string Source { get; set; }
/// <summary>遷移先</summary>
public string Description { get; set; }
/// <summary>パラメータ名</summary>
public string ConditionParameter { get; set; }
/// <summary>モード</summary>
public AnimatorConditionMode ConditionMode { get; set; }
/// <summary>閾値</summary>
public float ConditionThreshold { get; set; }
public TransitionSetting(
string source,
string description,
string parameter,
AnimatorConditionMode mode,
float threshold)
{
Source = source;
Description = description;
ConditionParameter = parameter;
ConditionMode = mode;
ConditionThreshold = threshold;
}
}
/// <summary>
/// AnimationClip生成
/// </summary>
/// <param name="sprites">スプライト</param>
/// <param name="setting">アニメーション設定</param>
/// <returns></returns>
private AnimationClip MakeAnimacionCrip(IList<Sprite> sprites, AnimationSetting setting)
{
//スプライトとフレーム番号のペアを生成していく
var animationFrames = new List<AnimationFrame>();
for(int i = 0; i < setting.SpriteIndexes.Count; ++i)
{
var index = setting.SpriteIndexes[i];
if(index < 0 || index >= sprites.Count)
continue;
animationFrames.Add(new AnimationFrame(sprites[index], i));
}
return MakeAnimacionCrip(
setting.Name,
animationFrames,
setting.FrameRate,
setting.Loop);
}
/// <summary>
/// AnimationClip生成
/// </summary>
/// <param name="name">名前</param>
/// <param name="frames">フレームのコレクション</param>
/// <param name="frameRate">フレームレート</param>
/// <param name="loop">ループ</param>
/// <returns></returns>
private AnimationClip MakeAnimacionCrip(
string name,
ICollection<AnimationFrame> frames,
float frameRate,
bool loop)
{
var clip = new AnimationClip();
var binding = new EditorCurveBinding();
binding.path = "";
binding.type = typeof(SpriteRenderer);
binding.propertyName = "m_Sprite";
//キーフレーム設定
var keyFrames = new List<ObjectReferenceKeyframe>();
foreach(var frame in frames)
{
keyFrames.Add(new ObjectReferenceKeyframe()
{
time = frame.FrameNo / frameRate,
value = frame.Sprite
});
}
//名前、フレームレート、ループ設定
clip.name = name;
clip.frameRate = frameRate;
var settings = AnimationUtility.GetAnimationClipSettings(clip);
settings.loopTime = loop;
AnimationUtility.SetAnimationClipSettings(clip, settings);
//登録
AnimationUtility.SetObjectReferenceCurve(
clip,
binding,
keyFrames.ToArray());
return clip;
}
/// <summary>AnimatorControllerアセット拡張子</summary>
private const string extensionController = ".controller";
/// <summary>AnimationClipアセット拡張子</summary>
private const string extensionClip = ".anim";
/// <summary>アセット保存フォルダパス</summary>
private static string folderPath = "Assets/Animations/";
/// <summary>AnimationClipをAnimatorControllerの子アセットにするフラグ</summary>
private static bool subassetFlag = true;
/// <summary>Inspector折りたたみフラグ</summary>
private static bool foldout = true;
/// <summary>
/// Inspectorのカスタム
/// </summary>
public override void OnInspectorGUI()
{
var t = (Character2)target;
//デフォルトInspector
DrawDefaultInspector();
//歩行アニメーション自動生成ボタン&実装
EGL.Separator();
foldout = EGL.Foldout(foldout, "アニメーション自動生成");
if(!foldout)
return;
//設定
folderPath = EGL.TextField("フォルダパス", folderPath);
subassetFlag = EGL.Toggle("Clipサブアセット化", subassetFlag);
if(subassetFlag)
EGL.HelpBox(
"AnimatorControllerアセットの子アセットとしてAnimationClipを保存",
MessageType.Info);
else
EGL.HelpBox(
"AnimatorControllerアセットと同じフォルダにAnimationClipアセットを保存",
MessageType.Info);
if(GUILayout.Button("アニメーション自動生成!"))
{
if(!folderPath.EndsWith("/"))
folderPath = folderPath + "/";
if(t.texture == null)
{
Debug.LogError("Textureが設定されていません。");
return;
}
//アセットパス
var assetPath = folderPath + t.texture.name + extensionController;
//ファイル存在確認
if(AD.LoadAssetAtPath<Object>(assetPath))
{
Debug.LogError("既に " + assetPath + " が存在します。");
return;
}
//スプライト取得
var sprites = AD.LoadAllAssetsAtPath(AD.GetAssetPath(t.texture)).
Where(x => x.GetType() == typeof(Sprite)).
Select(x => (Sprite)x).
ToList();
if(sprites == null || sprites.Count <= 0)
{
Debug.LogError("スプライトを取得できませんでした");
return;
}
//Animatorアタッチ
var animator = t.GetComponent<Animator>();
if(animator == null)
animator = t.gameObject.AddComponent<Animator>();
//AnimatorController生成
var controller = new AnimatorController();
controller.name = t.texture.name;
controller.AddLayer("Base Layer");
animator.runtimeAnimatorController = controller;
//Parameter登録
foreach(var param in Settings.parameters)
controller.AddParameter(param);
//BlendTree登録
var blendTrees = new List<BlendTree>();
foreach(var blendTree in Settings.blendTrees)
{
//BlendTreeをClone (削除されると困るので)
var tree = new BlendTree()
{
name = blendTree.Name,
blendType = blendTree.BlendType,
blendParameter = blendTree.BlendParameterX,
blendParameterY = blendTree.BlendParameterY
};
blendTrees.Add(tree);
controller.AddMotion(tree);
}
//AnimationClip生成、登録
foreach(var animSetting in Settings.animations)
{
var clip = MakeAnimacionCrip(sprites, animSetting);
if(animSetting.BlendTreeName != "")
blendTrees.Single(x => x.name == animSetting.BlendTreeName).AddChild(
clip,
animSetting.BlendTreePosition);
else
controller.AddMotion(clip);
}
//Transition生成、登録
var stateMachine = controller.layers[0].stateMachine;
foreach(var tranSetting in Settings.transitions)
{
var src = stateMachine.states.Single(x => x.state.name == tranSetting.Source);
var des = stateMachine.states.Single(x => x.state.name == tranSetting.Description);
var transition = src.state.AddTransition(des.state);
transition.AddCondition(
tranSetting.ConditionMode,
tranSetting.ConditionThreshold,
tranSetting.ConditionParameter);
}
//デフォルトスプライトを設定
t.GetComponent<SpriteRenderer>().sprite =
sprites[Settings.animations[0].SpriteIndexes[0]];
//アセットファイル出力
AD.CreateAsset(controller, assetPath);
AD.AddObjectToAsset(stateMachine, controller);
foreach(var state in stateMachine.states)
{
AD.AddObjectToAsset(state.state, controller);
if(!(state.state.motion is AnimationClip))
AD.AddObjectToAsset(state.state.motion, controller);
foreach(var transition in state.state.transitions)
AD.AddObjectToAsset(transition, controller);
}
//AnimationClip出力
if(subassetFlag)
{
//サブアセットとして登録
foreach(var clip in controller.animationClips)
AD.AddObjectToAsset(clip, controller);
AD.ImportAsset(AD.GetAssetPath(controller));
}
else
{
//アセットとして保存
foreach(var clip in controller.animationClips)
{
//重複防止のリネーム
clip.name = t.texture.name + "_" + clip.name;
AD.CreateAsset(clip, folderPath + clip.name + extensionClip);
}
}
AD.SaveAssets();
AD.Refresh();
}
}
}
#endif
}