AnimationClipをスクリプトから生成

まず、AnimationClipの生成に必要なデータをいれるためのクラスをいくつか用意します。


UnityのVector構造体は意外と複雑だったので、

必要な変数だけ用意したシンプルなデータクラスを一応作りました。

Copy
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Vector3Data
{
public float X {get; set;}
public float Y {get; set;}
public float Z {get; set;}
public Vector3Data()
{
}
public Vector3Data(float x, float y, float z)
{
this.X = x;
this.Y = y;
this.Z = z;
}
public Vector3 toVector3()
{
return new Vector3(X, Y, Z);
}
}


1個のキーフレーム中の値をまとめたデータクラス

Copy
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// AnimationClipの1キーフレームの情報
/// </summary>
public class AnimationKeyframe
{
[SerializeField]
public float testvalue;
public float Time { get; set; }
public Vector3Data Position { get; set; }
public Vector3Data Rotation { get; set; }
public Vector3Data Scale { get; set; }
public AnimationKeyframe(){}
public AnimationKeyframe(float time, Vector3Data position, Vector3Data rotation, Vector3Data scale)
{
this.Time = time;
this.Position = position;
this.Rotation = rotation;
this.Scale = scale;
}
}


それをリストとしてまとめたクラス

これのインスタンスにAnimationClipに変換したいデータをいれてください。

Copy
using System;
using System.Collections;
using System.Runtime.Serialization;
using System.Collections.Generic;
using UnityEngine;
public class AnimationCurveData
{
public List<AnimationKeyframe> Keyframes { get; set;} = new ();
}


あとはそれをUnityに読み込ませるだけです。

以下のクラスで読み込めるようにしておきました。

(Rotationだけは内部でQuaternionを使っている関係上、

一度変換してw成分も設定しなければならないことに注意!)

Copy
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public static class AnimationClipLoader
{
// https://docs.unity3d.com/ja/2020.3/ScriptReference/AnimationClip.SetCurve.html
// Transform.localPosition のようにTransformの変数一覧がキーになるらしい
/// <summary>
/// 相対x座標をSetCurveする場合のキー
/// </summary>
const string LOCAL_POSITION_X_KEY = "localPosition.x";
/// <summary>
/// 相対y座標をSetCurveする場合のキー
/// </summary>
const string LOCAL_POSITION_Y_KEY = "localPosition.y";
/// <summary>
/// 相対z座標をSetCurveする場合のキー
/// </summary>
const string LOCAL_POSITION_Z_KEY = "localPosition.z";
/// <summary>
/// 回転x成分をSetCurveする場合のキー
/// </summary>
const string ROTATION_X_KEY = "localRotation.x";
/// <summary>
/// 回転y成分をSetCurveする場合のキー
/// </summary>
const string ROTATION_Y_KEY = "localRotation.y";
/// <summary>
/// 回転z成分をSetCurveする場合のキー
/// </summary>
const string ROTATION_Z_KEY = "localRotation.z";
/// <summary>
/// 回転w成分をSetCurveする場合のキー
/// </summary>
const string ROTATION_W_KEY = "localRotation.w";
/// <summary>
/// 拡大x成分をSetCurveする場合のキー
/// </summary>
const string SCALE_X_KEY = "localScale.x";
/// <summary>
/// 拡大y成分をSetCurveする場合のキー
/// </summary>
const string SCALE_Y_KEY = "localScale.y";
/// <summary>
/// 拡大z成分をSetCurveする場合のキー
/// </summary>
const string SCALE_Z_KEY = "localScale.z";
/// <summary>
/// AnimationClipをAnimatorに設定
/// </summary>
/// <param name="clip"></param>
/// <param name="clipname">置換するアニメーションクリップの名前</param>
/// <param name="anim"></param>
public static void setClip(AnimationClip clip, string clipname, Animator animator)
{
clip.name = clipname;
// AnimationClipを変更(AnimatorOverrideControllerを経由しないと動かない)
AnimatorOverrideController animatorOverrideController = new AnimatorOverrideController(animator.runtimeAnimatorController);
animator.runtimeAnimatorController = animatorOverrideController;
animatorOverrideController[clip.name] = clip;
}
/// <summary>
/// JsonからAnimationClipに変換
/// </summary>
/// <param name="curve"></param>
/// <returns></returns>
public static AnimationClip convertToAnimationClip(AnimationCurveData curve)
{
if (curve == null || ListUtils.IsEmpty(curve.Keyframes)) {
return null;
}
AnimationClip clip = new AnimationClip();
// Positionの成分ごとのカーブを作成
// 参考
// https://im0039kp.jp/%E3%80%90unity%E3%80%91animationclip%E3%82%92%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%81%8B%E3%82%89%E7%94%9F%E6%88%90/
AnimationCurve xPositionCurve = new AnimationCurve();
AnimationCurve yPositionCurve = new AnimationCurve();
AnimationCurve zPositionCurve = new AnimationCurve();
AnimationCurve xRotationCurve = new AnimationCurve();
AnimationCurve yRotationCurve = new AnimationCurve();
AnimationCurve zRotationCurve = new AnimationCurve();
AnimationCurve wRotationCurve = new AnimationCurve(); // rotationはw成分もあるので注意
AnimationCurve xScaleCurve = new AnimationCurve();
AnimationCurve yScaleCurve = new AnimationCurve();
AnimationCurve zScaleCurve = new AnimationCurve();
// 各キーフレームの情報を成分ごとにカーブに追加していく
foreach(var keyframe in curve.Keyframes) {
if (keyframe.Position != null) {
xPositionCurve.AddKey(keyframe.Time, keyframe.Position.X);
yPositionCurve.AddKey(keyframe.Time, keyframe.Position.Y);
zPositionCurve.AddKey(keyframe.Time, keyframe.Position.Z);
}
if (keyframe.Rotation != null) {
// rotationは一度変換が必要
// 参考 https://monaski.hatenablog.com/entry/2015/11/15/172907
Quaternion rotation = Quaternion.Euler(keyframe.Rotation.X, keyframe.Rotation.Y, keyframe.Rotation.Z);
xRotationCurve.AddKey(keyframe.Time, rotation.x);
yRotationCurve.AddKey(keyframe.Time, rotation.y);
zRotationCurve.AddKey(keyframe.Time, rotation.z);
wRotationCurve.AddKey(keyframe.Time, rotation.w);
}
if (keyframe.Scale != null) {
xScaleCurve.AddKey(keyframe.Time, keyframe.Scale.X);
yScaleCurve.AddKey(keyframe.Time, keyframe.Scale.Y);
zScaleCurve.AddKey(keyframe.Time, keyframe.Scale.Z);
}
}
// AnimationClipにカーブを設定
clip.SetCurve("", typeof(Transform), LOCAL_POSITION_X_KEY, xPositionCurve);
clip.SetCurve("", typeof(Transform), LOCAL_POSITION_Y_KEY, yPositionCurve);
clip.SetCurve("", typeof(Transform), LOCAL_POSITION_Z_KEY, zPositionCurve);
clip.SetCurve("", typeof(Transform), ROTATION_X_KEY, xRotationCurve);
clip.SetCurve("", typeof(Transform), ROTATION_Y_KEY, yRotationCurve);
clip.SetCurve("", typeof(Transform), ROTATION_Z_KEY, zRotationCurve);
clip.SetCurve("", typeof(Transform), ROTATION_W_KEY, wRotationCurve);
clip.SetCurve("", typeof(Transform), SCALE_X_KEY, xScaleCurve);
clip.SetCurve("", typeof(Transform), SCALE_Y_KEY, yScaleCurve);
clip.SetCurve("", typeof(Transform), SCALE_Z_KEY, zScaleCurve);
return clip;
}
}


使用例はこんな感じ。実際はJSONから値を設定したりすると思います。

Copy
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AnimationClipExample : MonoBehaviour
{
/// <summary>
/// 置換するAnimationClipの名前
/// </summary>
[SerializeField]
private string targetClipname;
/// <summary>
/// AnimationClipを置換するためのAnimator
/// </summary>
[SerializeField]
private Animator targetAnimator;
void Start()
{
// 適当に置換するアニメーションを用意
AnimationCurveData curveData = new();
curveData.Keyframes.Add(new AnimationKeyframe(0, new Vector3Data(0, 0, 0), new Vector3Data(0, 0, 0), new Vector3Data(1, 1, 1)));
curveData.Keyframes.Add(new AnimationKeyframe(10, new Vector3Data(1, 2, 3), new Vector3Data(30, 60, 90), new Vector3Data(11, 12, 13)));
// AnimationClipに変換
AnimationClip newClip = AnimationClipLoader.convertToAnimationClip(curveData);
// AnimationClipを置換する
AnimationClipLoader.setClip(newClip, targetClipname, targetAnimator);
}
}


値が正しく読み込まれ、アニメーションが行われます。








・・・ちなみに、私もQuaternionが何かはぶっちゃけわかってません。

一応詳しい解説は見つけたのでいずれしっかり読むかも・・・

結局読まないかも・・・


コメント