Unity上でSpineオブジェクトをアニメさせつつ連番PNGを作成
果たして需用あるのか謎だが、spineのデータ(json)をunityに読み込み、各パーツのアニメフレームを再生しつつ、連番PNGを出力させる事に成功した。c#。
(別にspineでも同じ事はできるが、パーツが多すぎるとON,OFFの切り替えが面倒そうだったので…)
using UnityEngine; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; public class Screenshot : MonoBehaviour { private Animator model_animator; void Awake(){ model_animator = GameObject.Find("skeleton.json").GetComponent<Animator>(); model_animator.speed = 0; // アニメ停止 StartCoroutine(Test()); } private IEnumerator Test() { // 全オブジェクトリストアップ。引っ掛けたくないものは非アクティブにする List<GameObject> ary = new List<GameObject>(); foreach (GameObject obj in UnityEngine.Object.FindObjectsOfType(typeof(GameObject))){ if(obj.transform.FindChild(obj.name))ary.Add(obj); } // 画像を変えていく System.IO.Directory.CreateDirectory(@"フォルダを作る場所"); Texture2D tex = new Texture2D(Screen.width, Screen.height, TextureFormat.ARGB32, false); for (int i = 0 ; i < ary.Count ; i++){ yield return new WaitForEndOfFrame(); // 描画系には描画を待つだけのウェイトを // yield return new WaitForSeconds(1.5f); for (int j = 0 ; j < ary.Count ; j++){ GameObject child = ary[j].transform.FindChild(ary[j].name).gameObject; var s_compo = child.GetComponent<SkinnedMeshRenderer>(); s_compo.enabled = (i == j); } System.IO.Directory.CreateDirectory(@"フォルダを作る場所" + ary[i].name); var anime_flame = 0.0f; var count = 0; while(model_animator.GetCurrentAnimatorStateInfo(0).normalizedTime < 1.0f){ // アルファチャンネル・保存のために yield return new WaitForEndOfFrame(); tex.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0); tex.Apply(); byte[] bytes = tex.EncodeToPNG(); File.WriteAllBytes("フォルダを作る場所" + ary[i].name + "\\" + ary[i].name + count.ToString() + ".png", bytes); count++; anime_flame += (1.0f / 60.0f); model_animator.ForceStateNormalizedTime(anime_flame); // アニメフレームコマ送り } model_animator.ForceStateNormalizedTime(0.0f); } Destroy(tex); // エディタの再生、終了 UnityEditor.EditorApplication.isPlaying = false; } }
Unityに取り込んだspineのオブジェクト名は”skeleton.json”だと仮定している。
フォルダの生成箇所も手動で書き換えよう
要はアニメフレームを1つずつめくりながら画面のスクリーンショットを撮る、という荒業。
自分のPCでは20fpsくらいになってしまうという負荷だった…
画面スクショは再生ウィンドーに大きさが影響されるので、再生(GAME)ウィンドーの大きさを固定する為のクラスも作成。
この場合は横1000、縦800としている。
http://answers.unity3d.com/questions/267097/editor-programming-experts-regarding-layouts.html
このフォーラムを参考にしてコピペ。
これをEditorと名前を付けたフォルダに入れると、メニュー欄に"Caputer">"start"という項目が加わるので、それを押せば勝手に始まって勝手に終わる。
using System; using UnityEditor; using UnityEngine; using System.Collections; public class EditorGamePosition : EditorWindow { [MenuItem ("Caputer/start")] static void Init () { WindowInfos windows = new WindowInfos(); windows.game.position = new Rect(0, 0, 1000, 800 + 17); // titleバー分が17px? UnityEditor.EditorApplication.isPlaying = true; } // 簡単な書き方で各エディターにアクセスしやすいようにしてくれてるクラス class WindowInfos { // note: some of this data might need to change a little between different versions of Unity public WindowInfo scene = new WindowInfo("UnityEditor.SceneView", "Scene", "Window/Scene"); public WindowInfo game = new WindowInfo("UnityEditor.GameView", "Game", "Window/Game"); public WindowInfo inspector = new WindowInfo("UnityEditor.InspectorWindow", "Inspector", "Window/Inspector"); public WindowInfo hierarchy = new WindowInfo("UnityEditor.HierarchyWindow", "Hierarchy", "Window/Hierarchy"); public WindowInfo project = new WindowInfo("UnityEditor.ProjectWindow", "Project", "Window/Project"); public WindowInfo animation = new WindowInfo("UnityEditor.AnimationWindow", "Animation", "Window/Animation"); public WindowInfo profiler = new WindowInfo("UnityEditor.ProfilerWindow", "Profiler", "Window/Profiler"); public WindowInfo console = new WindowInfo("UnityEditor.ConsoleWindow", "Console", "Window/Console"); public WindowInfo navigation = new WindowInfo("UnityEditor.NavMeshEditorWindow", "Navigation", "Window/Navigation"); public WindowInfo occlusion = new WindowInfo("UnityEditor.OcclusionCullingWindow", "Occlusion", "Window/Occlusion Culling"); public WindowInfo lightmapping = new WindowInfo("UnityEditor.LightmappingWindow", "Lightmapping", "Window/Lightmapping"); public WindowInfo assetServer = new WindowInfo("UnityEditor.ASMainWindow", "Server", "Window/Asset Server"); public WindowInfo assetStore = new WindowInfo("UnityEditor.AssetStoreWindow", "Asset Store", "Window/Asset Store"); public WindowInfo particle = new WindowInfo("UnityEditor.ParticleSystemWindow", "Particle Effect", "Window/Particle Effect"); public MainWindow main = new MainWindow(); } class WindowInfo { string defaultTitle; string menuPath; Type type; public WindowInfo(string typeName, string defaultTitle = null, string menuPath = null, System.Reflection.Assembly assembly = null) { this.defaultTitle = defaultTitle; this.menuPath = menuPath; if (assembly == null) assembly = typeof(UnityEditor.EditorWindow).Assembly; type = assembly.GetType(typeName); if (type == null) Debug.LogWarning("Unable to find type \"" + typeName + "\" in assembly \"" + assembly.GetName().Name + "\".\nYou might want to update the data in WindowInfos."); } public EditorWindow[] FindAll() { if (type == null) return new EditorWindow[0]; return (EditorWindow[])(Resources.FindObjectsOfTypeAll(type)); } public EditorWindow FindFirst() { foreach (EditorWindow window in FindAll()) return window; return null; } public EditorWindow FindFirstOrCreate() { EditorWindow window = FindFirst(); if (window != null) return window; if (type == null) return null; if (menuPath != null && menuPath.Length != 0) EditorApplication.ExecuteMenuItem(menuPath); window = EditorWindow.GetWindow(type, false, defaultTitle); return window; } // shortcut for setting/getting the position and size of the first window of this type. // when setting the position, if the window doesn't exist it will also be created. public Rect position { get { EditorWindow window = FindFirst(); if (window == null) return new Rect(0, 0, 0, 0); return window.position; } set { EditorWindow window = FindFirstOrCreate(); if (window != null) window.position = value; } } // shortcut for deciding if any windows of this type are open, // or for opening/closing windows public bool isOpen { get { return FindAll().Length != 0; } set { if (value) FindFirstOrCreate(); else foreach (EditorWindow window in FindAll()) window.Close(); } } } // experimental support for getting at the main Unity window class MainWindow { Type type; UnityEngine.Object window; public MainWindow() { type = typeof(UnityEditor.EditorWindow).Assembly.GetType("UnityEditor.ContainerWindow"); if (type != null) { foreach (UnityEngine.Object w in Resources.FindObjectsOfTypeAll(type)) { object parent = type.InvokeMember("get_mainView", System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.Instance, null, w, null); if (parent != null && parent.GetType().Name.Contains("MainWindow")) { window = w; break; } } } if (window == null) Debug.LogWarning("Unable to find main window.\nMaybe you'll need to update the MainWindow constructor for your version of Unity."); } public Rect position { get { if (window == null) return new Rect(0, 0, 0, 0); return (Rect)type.InvokeMember("get_position", System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.Instance, null, window, null); } set { if (window != null) type.InvokeMember("set_position", System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.Instance, null, window, new object[] { value }); } } public bool isOpen { get { return window != null; } set { if (!value) { if (EditorApplication.SaveCurrentSceneIfUserWantsTo()) EditorApplication.Exit(0); } } } } }