Unity再生中にTransformの値を保存する【エディタ拡張】

editor thumbnail

この記事はUnityの再生中に変更したTransformの値を再生終了後に反映させる機能を、エディタ拡張によって実装する記事です。

<スクリプトの内容>

<注意>無理矢理スクリプトです

Unityの再生中ゲームオブジェクトの位置や回転などをいろいろ調節したいけど、再生を終了すると元に戻ってしまいます。

再生終了後いちいち値をコピーして貼り付けるのがめんどくさいし、複数のオブジェクトをやろうとするとすごく大変です。

というわけでかなり無理矢理ですが、ワンボタンで値を何とかするようにしました。

↓のコードをコピって、「Editor」と名前のついたフォルダにぶち込んでもらえたらOKです。

#if UNITY_EDITOR
 using UnityEngine;
 using UnityEditor;
 using System.Reflection;
 [SerializeField]
 public class SaveTransform
 {
     //参照型を保存するとインスタンスIDのみが保存され、再生前の状態に戻るので値型を保存する
     [SerializeField] private Vector3 position;
     [SerializeField] private Quaternion rotation;
     [SerializeField] private Vector3 scale;
     public Transform GetValue(Transform t)
     {
          t.position = position;
          t.rotation = rotation;
          t.localScale = scale;
          return t;
      }

     public void SetValue(Transform t)
     {    
          position = t.position;
          rotation = t.rotation;
          scale = t.localScale;
     }
 }

 //再生中に変更されたTransformの値を再生終了後に反映させるスクリプト
 [CustomEditor(typeof(Transform), true)]
 [CanEditMultipleObjects]
 public class InspectorTransform : Editor
 {
     private Editor editor;
     private Transform myParam;
     private bool set;

     private void OnEnable()
     {    
          Transform transform = target as Transform;
          myParam = transform;
          System.Type t = typeof(UnityEditor.EditorApplication).Assembly.GetType("UnityEditor.TransformInspector");
          editor = Editor.CreateEditor(myParam, t);
      }

      private void OnDisable()
      {
          MethodInfo disableMethod = editor.GetType().GetMethod("OnDisable", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
          if (disableMethod != null) disableMethod.Invoke(editor, null);
          myParam = null;
          DestroyImmediate(editor);
      }

      public override void OnInspectorGUI()
      {
          editor.OnInspectorGUI();
          if (EditorApplication.isPlaying || EditorApplication.isPaused)
          {
              if (GUILayout.Button("再生中の状態を保存"))
              {
                  SaveTransform s = new SaveTransform();                 
                  s.SetValue(myParam);
                  string json = JsonUtility.ToJson(s);            
                  EditorPrefs.SetString("Save Param " + myParam.GetInstanceID().ToString(), json);
                  if (!set)
                  {
                      EditorApplication.playModeStateChanged += OnChangedPlayMode;                 set = true;
                  }
              }
          }
      }

      private void OnChangedPlayMode(PlayModeStateChange state)
      {
          //Unityの再生が終了した
          if (state == PlayModeStateChange.EnteredEditMode)
          {
              Transform transform = target as Transform;
              string key = "Save Param " + transform.GetInstanceID().ToString();        
              string json = EditorPrefs.GetString(key);
              SaveTransform t = JsonUtility.FromJson<SaveTransform>(json);
              EditorPrefs.DeleteKey(key);
              transform = t.GetValue(transform);
              EditorUtility.SetDirty(target);        
              EditorApplication.playModeStateChanged -= OnChangedPlayMode;
          }
      }
 }
#endif

ネット上でいろいろな方が保存する物を作成されていますが、他の方々が作成したものとの違いは↓のような感じです。

他の方々はTransformだけでなくシリアライズ化されているオブジェクト全部総括でやられている方が多かったので、Transformピンポイントのものを作成しました。

他の方々との違い

・オート保存ではなく、ボタンを押したら保存

・[InitializeOnLoad]とか[ExecuteInEditMode]を使用していない

・全Transformではなく、ボタンを押したオブジェクトのみ保存

・やり方が汚い

・Transformしか対応していない(クワッ

・とても単純簡単

まず、保存したい時だけ保存したいのでボタンを押したらという挙動にしました。

自分は[InitializeOnLoad]とか[ExecuteInEditMode]が嫌いなのでこれを使わないようにしました。

これらがたくさん詰め込まれたプロジェクトはエディタがクソ重くなる+他の物と競合すると水面下で見えないバグを起こしやすい為、嫌い。ヤダ

個別保存がしたかったのでそうしています。全オブジェクトを保存したい場合は他の方々が作ったスクリプトを使うといいかもしれません。

これらを実現しよういろいろ思考錯誤したのですが、Unityの再生を停止すると全インスタンスが一旦解放されてしまい、中の値が全部消え失せてしまいました。

というわけで、無理矢理EditorPrefsに一旦保存して、再生が終わったら取り出すというクソ技をキメにいきました。

保存する際に、Jsonにしてしまえば1つで済むなーと考えていたのですが、参照型のメンバ変数はインスタンスIDが保存されるらしく、ロードしたらそのインスタンスIDのものは再生前の状態に戻ってしまっていたので、値をいちいち取り出して保存しています。

ロードしたら保存した値は破棄しているのでご安心を

スポンサーリンク

<このスクリプトの使い方>

transform edit

Unityを再生中、もしくは一時停止中のみインスペクタのTransformの下に「再生中の状態を保存」というボタンが出てきます。

これを押せばOKです。

注意点として、シーンは保存されていないので、再生後にシーンを保存するのを忘れないでください。

Transformそのものを保存しているわけではなく、値を一時退避させて、再生終了後に値を代入しているだけです。

その為再生終了後、再生開始位置に戻ってから保存した状態になります。不恰好ここに極まる。だから何だ結果が得られればいい。

<まとめ>

とても簡単に使えて、嫌いな[InitializeOnLoad]とか[ExecuteInEditMode]を使用せずにできたのでよかったかなと思います。

EditorPrefsを使っている点を除いては

とても簡単なコードなので、他のスクリプトにも使えまわせそうです。影響範囲が極小なのもいい感じです。

タイトルとURLをコピーしました