LoginSignup
5
1

More than 1 year has passed since last update.

Unity Spine CreateRuntimeInstance について

Last updated at Posted at 2022-12-22

PONOS Advent Calendar 2022の23日目の記事です。
昨日は@ackylaさんの「NESエミュレータを作る-キャラクターROM見てみる編」でした。

はじめに

Unityの2Dゲームアニメーションによく使われているSpineですが、
本日はSpineのランタイム生成や注意してほしい箇所をご紹介したいと思います。
まず、開発環境から設定しましょう。

テスト環境

Spineの構造

まずはUnityのサンプルプロジェクトを作成してspine-unity sdkをインポートしましょう!
spine.png

  • Skeleton Data Asset
  • Skeleton Animation
  • Skeleton Renderer

ご覧の通りSpineはSkeletion Animationコンポーネントが核となっており、
Skeleton Data Assetデータから様々なレンダリングまで行われています。
今回はSpineのGameObject Cloneではなくてランタイム生成について触れていきます。

ランタイム生成メソッド

SkeletonDataAsset.cs
public static SkeletonDataAsset CreateRuntimeInstance (
                                TextAsset skeletonDataFile, 
                                AtlasAssetBase[] atlasAssets,
                                bool initialize,
                                float scale = 0.01f)
--------------------------------------------------------------------
SkeletonAnimation.cs
public static SkeletonAnimation NewSkeletonAnimationGameObject (
                                SkeletonDataAsset skeletonDataAsset,
                    			bool quiet = false)
  • 上記のメソッド2つで簡単に作られます。

ランタイムで生成するSkeleton Data AssetやAtlas AssetsのInspectorサンプルは以下となります。
SpineRuntimeLoader GameObjectがデータ参照している形です。
spine-inspector.png

SpineRuntimeLoader

using UnityEngine;
using Spine.Unity;

public class SpineRuntimeLoader : MonoBehaviour
{
    [SerializeField] SkeletonDataAsset skeletonDataAsset = default;
    [SerializeField] AtlasAssetBase[] atlasAssets = default;

    void CreateSpineInstance()
    {
        var newSkeletonDataAsset = SkeletonDataAsset.CreateRuntimeInstance(skeletonDataAsset.skeletonJSON, atlasAssets, true);
        var newInstance = SkeletonAnimation.NewSkeletonAnimationGameObject(newSkeletonDataAsset);
    }
}
  • コードでは参照しているSkeletonDataAssetとAtlasAssetからCreateRuntimeInstanceを作成し
  • SkeletonAnimationコンポーネントがついたUnity GameObjectを生成します。
    spine-create.gif
  • ランタイム中に簡単にSpineの作成ができました。

CreateRuntimeInstance使用時の注意点

ゲームの開発においてオブジェクトプールは利用しつつも、
毎回スケルトンデータインスタンスを生成するとどうなるのでしょうか?
メモリープロファイリングしてみたら驚きの結果が!?

テストコード

using UnityEngine;
using Spine.Unity;

public class SpineRuntimeLoader : MonoBehaviour
{
    [SerializeField] SkeletonDataAsset skeletonDataAsset = default;
    [SerializeField] AtlasAssetBase[] atlasAssets = default;
    List<SkeletonAnimation> skeletonInstances = new List<SkeletonAnimation>();

    void CreateSpineInstance()
    {
        var newSkeletonDataAsset = SkeletonDataAsset.CreateRuntimeInstance(skeletonDataAsset.skeletonJSON, atlasAssets, true);
        var newInstance = SkeletonAnimation.NewSkeletonAnimationGameObject(newSkeletonDataAsset);
        skeletonInstances.Add(newInstance);
    }

    void Update()
    {
        if (Input.GetKeyUp(KeyCode.Alpha1))
        {
            for (int i = 0; i < 1000; i++)
            {
                CreateSpineInstance();
            }
        }
        if (Input.GetKeyUp(KeyCode.Alpha2))
        {
            for (int i = 0; i < skeletonInstances.Count; i++)
            {   
                Destroy(skeletonInstances[i].gameObject);
                skeletonInstances[i] = null;
            }
            skeletonInstances.Clear();
        }

    }
}
  • キー1を押して1000体のオブジェクトを作り、キー2を押して破棄するシンプルなコードです。
     

Memory Profile

メモリープロファイラーのインポートはこちらをご参考ください。

まず何もない状態でのSnapShotです。特に気になるものはありませんね。
spine-empty.png

キー1を押して1000体のインスタンスを生成した状態です。
先とは違ってメモリー使用量が増えているのがわかります。
spine-1000.png

今度はキー2で生成した1000体のインスタンスを削除した状態です。
なんと謎のデータが残っている!
待って?Spineとかついてるのが気になりますね…
spine-destroy-marker.png

原因はランタイム生成したスケルトンデータ

Unity GameObjectは破棄したともCreateRuntimeInstanceから作られたSkeletonDataAssetの参照が解放されてなかったみたいですね…
破棄する際にはSkeletonDataAssetまできちんと解放しましょう!

修正コード

    for (int i = 0; i < skeletonInstances.Count; i++)
    {
        var skeletonAnimation = skeletonInstances[i];

        // スケルトンアニメーションが参照しているスケルトンデータアセットも解放しよう!
        skeletonAnimation.skeletonDataAsset.Clear();
        skeletonAnimation.skeletonDataAsset = null;
            
        Destroy(skeletonInstances[i].gameObject);
        skeletonInstances[i] = null;
    }
    skeletonInstances.Clear();

結果

同じく1000体のインスタンスを生成した後、破棄した結果スクショです。
Spineがついてるブロックはないですね!これでスッキリ。
spine-clear-destroy.png

最後に

Spineのランタイム生成から破棄まで触れてみました。
ランタイム生成は一見便利ではありますが、使い回しの際にはロードしたデータをちゃんと解放してあげましょう。
長時間のプレイでは小さくても使用済みメモリーの積み重ねでアプリが落ちてしまうかもしれません!

明日は@ANIZA_15さんの記事です!

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1