SpriteStudio / SS5PlayerForUnity

OPTPiX SpriteStudio 5 Player for Unity
http://www.webtech.co.jp/spritestudio/
MIT License
39 stars 15 forks source link

v1.3.x 複数のモーションで同一Materialを使うには? #185

Open f3b49 opened 8 years ago

f3b49 commented 8 years ago

お世話になっております。 複数のモーションで同一Materialを使いたいと思っております。 例えば、エフェクトモーションとかです。

現状ですと複数のSpriteStudioモーションデータは それぞれロード時に別々のMaterialが生成されると思います。 できればロード時に1つのMaterialを参照するようにしたいのですが、いい方法があれば 教えてほしいです。

よろしくお願いいたします。

MasamiYitsuse commented 8 years ago

f3b49 様

お世話になっております。

マテリアルについては、インポート時に同じSSPJ内に含まれているSSAEであれば、生成されたPrefabで使用しているマテリアルの実体は同じものを使用しています。 ※マテリアルはSSPJ中に含まれるSSCE(セルマップ)1つについて、通常・加算・減算・乗算の4つが生成されます。

異なるSSPJ内に含まれているセルマップは、互換がある保障がないため、現状インポート時などに共有の解決をする方法はありません。

ただ、もし共通であることが分かりきっている場合、基準となるアニメーション(Script_SpriteStudio_Root)から、マテリアルテーブルを読み取って、別のアニメーションのマテリアルテーブルに設定することで、強制的に共通にすることは可能です。

また、多少マテリアルテーブルに加工が必要な場合、下記の関数群を使用して、マテリアルテーブルを編集することが可能です。

https://github.com/SpriteStudio/SS5PlayerForUnity/wiki/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9%EF%BC%88Ver.1.3.x%E7%94%A8%EF%BC%89:-%E3%83%9E%E3%83%86%E3%83%AA%E3%82%A2%E3%83%AB%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E9%96%A2%E4%BF%82

大枠としては以上のような形になっております。 お忙しい中お手数とは存じますが、ご検証のほどお願い申し上げます。

f3b49 commented 8 years ago

ご返答ありがとうございます。 マテリアルテーブルに設定することで、強制的に共通に変更する事でアセット量を抑えることができました。 このときにテクスチャメモリ量を減らすことは可能でしょうか? 共通化してもモーションの数ぶんテクスチャメモリを消費すると思っていますがこの認識であっているのでしょうか?

MasamiYitsuse commented 8 years ago

f3b49 様

お世話になっております。 SSPJが異なるアニメーション間でマテリアルテーブルを編集して共有化した場合、(ロードや実体化の方法によって)使用していないテクスチャがメモリ中に残存してしまう可能性があるかと思われます。 その場合に、(アセットバンドルやリソースなどで廃棄に最適な関数が変わる可能性がありますが)使用しないテクスチャやマテリアルなどをUnityEngine.Object.Destroy関数などを使用して、廃棄してしまうのが一つの手かと思われます。

以上となります。 お忙しい中お手数ですが、何卒ご検証のほどお願いいたします。

f3b49 commented 8 years ago

ありがとうございます。 Destroyを試してみます。それでも使用メモリが多い場合は共有化を諦めるつもりです。

MasamiYitsuse commented 8 years ago

f3b49様

お世話になっております。 もし各種条件下で可能であるのであれば、SSPJを一緒にしてしまうのが良いかもしれません。 ※その方が、様々な面でスマートになるかもしれません。Unity内での資産の最適化処理の恩恵を受けられる可能性も高くなるかと思われます。

お忙しい中お手数とは存じますが、ご検討頂けますと幸いです。

MasamiYitsuse commented 8 years ago

f3b49様

お世話になっております。 余談的な付記となりますが……。

また、もう一つの方法のご提案として、(これもまたもし可能であるなら……ですが) エディタ上(シーンエディットの都合などで)で特段のマテリアルなどの交換を行う必要がなく・ランタイム処理でのみ行われる場合等でしたら……

・マテリアルテーブルをプログラム内などにstaticなどの形で静的に持ってしまっておき(ただ、一番大元のマテリアルテーブルだけは何らかの形で読み込んできておく必要があります) ・アニメーションデータprefabの「マテリアルテーブルの全マテリアルをNone(null)とする」か「テーブル自体をnullに書き変えてしまうエディタスクリプトなどを作成して、マテリアルへの参照をなし」にしてしまい、マテリアル群を未参照オブジェクトとしてしまう

というような処置を行うことで、prefabをinstantiateした後などにstaticのマテリアルテーブルをセットすることで、余分なデータをそもそも読まないことができるかと思われます(アセットバンドルやResourcesに含まれていたとしても、参照されていないので、マテリアルとテクスチャを読みにいかなくなると思われます)。 ※SS5PUの処理サイクル上、データを再生した後「描画処理(instantiateした処理ループのLateUpdate)」に到達する前までにマテリアルテーブルが設定が設定されていれば、大丈夫のはずです。

以上となります。

f3b49 commented 8 years ago

お世話になっております。アドバイスありがとうございます! マテリアルテーブルでなくマテリアル単体のstatic化はもうテスト済なのですが、おっしゃる通り大元のマテリアルテーブルの読み込みが必要で、これだとリソース的に効果があってもメモリ的には効果が薄いと感じていました。

あと SSPJを一緒にするのもテストしていたのですが、AssetBundle化した必要なエフェクトのみLoadしたくてこの場合は一緒にするのが厳しいそうで断念しました。

そこで質問させてほしいのですが、SSPJを一緒にした場合は共通テクスチャを同じMaterialとして認識させることは可能なのでしょうか?あとSSPJを一緒にしてもそこに含まれるエフェクトモーションを個別のAssetBundleにすることは可能なのでしょうか? ご教授をお願いいたします。

MasamiYitsuse commented 8 years ago

f3b49様

お世話になっております。

まず、この場合の「共通テクスチャ」という点についてですが……。 SS5PUでは、「SSPJの中にあるSSCE(セルマップ)毎に、テクスチャとマテリアルを生成」します。 そのSSPJ内にあるSSAE(アニメーション)は統一して、そのSSCEに定義されているセルを使用するため、同じSSCEを参照している場合、テクスチャは共有化されます。 ここで注意すべき点としては、

  1. 「同じ内容のセルマップ(及び参照テクスチャ)」が、SSPJ内に複数のSSCEとして存在していた場合は、それぞれのSSCEは別物として扱われます(同じ内容のマテリアル・テクスチャが作成されます)。
  2. 「同じSSCEを複数のSSPJから参照してる」場合についても、SSPJ(プロジェクト)が異なるので、別物として扱われます。 ……という点です。 ただ、1については例外的に、複数のSSCEが参照しているテクスチャ画像のファイルが同じ場合、SS5PUにインポートした段階で、「テクスチャは共有化」され、マテリアルのみが別に作られる形になります。 ※これは、複数のSSCEから同じテクスチャが参照されている場合、SS5PUでインポートした段階で、(異なるSSCEから参照していても、テクスチャが同名であるため)テクスチャをSSCEの回数分だけ上書きしてしまう……という仕様になっているためです。

つまり、同じSSPJ内に存在しているSSAEは、統一してSSPJ内にあるSSCEを参照し、SSCE毎でマテリアルとテクスチャが作成されることになります。 そうするとUnity上では同じ素材が(Resourcesやアセットバンドルから読み込まれて)メモリ上にすでに存在する場合、原則として再読み込みを行わないことから、メモリと読み込み時間の節約につながります。

ですので、「1つのSSPJに入れてしまう」というのは、正確には「1つのSSPJ内にある複数のアニメーションで、そのSSPJ内にあるSSCEを共有する」ことが「テクスチャの共有化」に直結しています。 ……これが、「同じ素材を使用しているアニメーション群を、同じSSPJに格納しておく」ことの有利な点です。

という原則を踏まえた上で、複数のアニメーションを別々のデータとして読込を行う方法ですが…… いくつかの方法があるかと思われます。


・方法A

  1. 一つのSSPJで、全てまとめたアセットバンドルにしてしまい、そのアセットバンドルから適時必要なアニメーションPrefabを読み出す。

この方法は、マテリアルとテクスチャ(だけでなく他にも共有されているScriptableObjectなども)はアセットバンドル内に共有化された状態で格納されていますので、自然にメモリなどが最適な形で運用できるかと思われます。 デメリットとしては、一つのアセットバンドルは大きくなるため、その大きさによっては読み込み時間がかかったり・メモリ中の常駐量が大きくなったりしてしまう点です。 ただ、テクスチャやマテリアルだけでなく、「CellMap」フォルダ内のデータも共有化できますので、ScriptableObjectのメモリ配置の最適化の恩恵も受けられます。


・方法B

  1. マテリアルやテクスチャを別のアセットバンドルにしておくかResources内などに格納しておき、予めプログラムから読み込んで、マテリアルテーブルを形成しておく。
  2. インポートしたアニメーションのPrefab毎でアセットバンドル化し、アセットバンドルからPrefabを実体化した段階で、1で生成しておいたマテリアルテーブルを設定する。

この方法は、アセットバンドル化する前に各Prefabのマテリアルテーブルをnullにしておけば、余分なデータも付くことはありません(即物的には使用しないマテリアルとテクスチャ群を消してしまえば自動的に参照が失われるので、それで解決する……といういささか乱暴な方法もありますが、あまり推奨できません)。 またアセットバンドル化する際には、「Prefab(これがPrefabの本体が格納されているフォルダです)」「Animation」「CellMap」のフォルダを一緒にアセットバンドルに入れておけばよいだけになります。 この方法では、CellMapの中の静的データ(ScriptableObject)の分の最適化の恩恵がうけづらくなります。 また、マテリアルテーブルなどの取り回しが、Unity任せにできない(若干手間が増える)デメリットがあります。 ※各Prefabから参照しているAnimation内のScriptableObjectのデータは、名称が原則「SSAE名」と同じになっていて、Prefabと一対になっています。

ただ、メモリ中にもストレージ的にも、テクスチャとマテリアルのデータは一つとなりますので、その点で節約には繋がります。

※この方法の変化形として、1で扱うデータの中に、「CellMap」も一緒に入れてしまい、マテリアルテーブルを設置するときに、一緒に「CellMap」中のデータ(セルマップ情報)のScriptableObjectも管理しておいて、Script_SpriteStudio_Rootにマテリアルテーブルを設定する時に一緒にそのセルマップ情報も設定してしまう……というような亜形もありますが……若干面倒くさい処理が必要になるかと思われます。


……アセットバンドルで複数のSSAEで共有するテクスチャを共有させたままで扱う場合には、基本的にこの2方法から派生する方法になるかと思われます。

細かな実装手段については、開発されているプロジェクト毎で事情が異なるため、細かな派生手段の言及は(ここでは)できかねますが、上記の方法からの派生・発展形でなんとかなるのではないかと思われます。

これは余談で……ドキュメントにも軽くは記載されていますが…… 各アニメーションで使用するマテリアルは、マテリアルテーブルで参照しているもの……となりますので、マテリアルテーブルで参照されていないデータはResourcesやアセットバンドル内から省いてしまっても、実用上影響はない……ということがあります。 ※つまり、プログラム側でマテリアルテーブルとそれにまつわるマテリアルとテクスチャを構成してしまうのであれば、データ側にそれを搭載しておく必要はない……となります。

以上となります。 お忙しい中お手数とは存じますが、何卒ご検討下さいますようお願い致します。

f3b49 commented 8 years ago

お世話になっております。 丁寧なご回答ありがとうございました。参考になりました。

対応する場合は方法Bがよさそうですね。 このときマテリアルテーブルの形成はTableMaterialCopy()を使えばいいということですよね。 実験してみたいと思います。ありがとうございました。

MasamiYitsuse commented 8 years ago

f3b49様

お世話になっております。

このときマテリアルテーブルの形成はTableMaterialCopy()を使えばいいということですよね。

TableMaterialCopyを使うと、同じ内容のマテリアルを作成してしまうので、 もし、元になるアニメーション自体が存在するのであれば、

InstanceSSRootNew.TableMaterial = InstanceSSRoot.TableMaterial;

※注釈 InstanceSSRootNew: 新しくマテリアルテーブルをセットするScript_SpriteStudio_Rootの実体 InstanceSSRoot: 元になるマテリアルテーブルを持っているScript_SpriteStudio_Rootの実体

でテーブル自体をコピーしてしまうのが良いかもしれません。 (テーブルや含まれるMaterialをDestroyしないなら、普通に参照しておけば、GCなどで知らないうちに解放されてしまうことはないはずです)

以上となります。 何卒よろしくお願いいたします。