SpriteStudio / SS5PlayerForUnity

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

一つのアニメーションでテクスチャを差し替えた複数のオブジェクトを表示したい #71

Closed OpenSesameDevelop closed 9 years ago

OpenSesameDevelop commented 9 years ago

Aというアニメーションで、画面にAとBの二つのオブジェクトを表示したいと考えています。 ※BはAのテクスチャ差し替えしたもの。

プレハブは共有で一つ。Aのプレハブのみ。 AはプレハブからそのままInstantiateして作成。 BはプレハブからInstantiateした後、Script_SpriteStudio_PartsRootのTableMaterialの各マテリアルを複製してテクスチャを書き換え。

これで、A、B共に画面に表示されるのですが、Bの描画プライオリティがZ値を無視して必ず手前に表示されてしまうようです。

何か間違っていますでしょうか? もしくは別な方法がありましたら教えて頂けると助かります。

MasamiYitsuse commented 9 years ago

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

手順としては、間違っていないと思われます。 また、未見の症状ですので、起こっている状況の調査(当方で再現させて・原因の調査をしてみようと思いますので)にお時間を頂ければと思います。

ご不便をおかけして大変申し訳ございませんが、何卒ご理解の程お願い致します。

MasamiYitsuse commented 9 years ago

一応、いくつかのテストスクリプトを作成し、テストしてみましたが、当方の環境では、Zの値については正常に作用しているように見受けられます。

恐らく使用されている処理と近いと思われるスクリプトをサンプルとして下記させて頂きます。


using UnityEngine; using System.Collections;

public class TestInstantiate : MonoBehaviour {

public GameObject Prefab;
private GameObject Instance1 = null;
private GameObject Instance2 = null;

private bool FlagChangeMaterial = false;

private float Instance1Z = 50.0f;
private float Instance2Z = 100.0f;
private float DeltaZ = -1.0f;

void Start()
{
    if(null != Prefab)
    {
        Instance1 = Instantiate(Prefab) as GameObject;
        Instance1.transform.parent = transform;
        Vector3 Position1 = Vector3.zero;
        Position1.x = 60.0f;
        Position1.z = Instance1Z;
        Instance1.transform.position = Position1;

        Instance2 = Instantiate(Prefab) as GameObject;
        Instance2.transform.parent = transform;
        Vector3 Position2 = Vector3.zero;
        Position2.x = 120.0f;
        Position2.z = Instance2Z;
        Instance2.transform.position = Position2;
    }
}

void Update ()
{
    // Material Change (Instance2)
    if((null != Instance2) && (false == FlagChangeMaterial))
    {
        // Get "RootParts"'s GameObject
        Transform transformChild = Instance2.transform.GetChild(0);
        if((null != transformChild))
        {
            // Get "RootParts"
            GameObject gameObjectChild = transformChild.gameObject;
            Script_SpriteStudio_PartsRoot   ScriptPartsRoot = gameObjectChild.GetComponent<Script_SpriteStudio_PartsRoot>();
            if(null != ScriptPartsRoot)
            {
                // Get Texture
                Texture2D TextureNew = Resources.Load("差し替えるテクスチャ名") as Texture2D;

                // ChangeMaterial
                int Count = ScriptPartsRoot.TableMaterial.Length;
                for(int i=0; i<Count; i++)
                {
                    Material MaterialNew = new Material(ScriptPartsRoot.TableMaterial[i]);
                    MaterialNew.mainTexture = TextureNew;
                    ScriptPartsRoot.TableMaterial[i] = MaterialNew;
                }
            }

            FlagChangeMaterial = true;
        }
    }

    // Shift-Z
    Instance2Z += DeltaZ;
    if(0.0f > Instance2Z)
    {
        Instance2Z = 0.0f;
        DeltaZ = 1.0f;
    } else {
        if(100.0f < Instance2Z)
        {
            Instance2Z = 100.0f;
            DeltaZ = -1.0f;
        }
    }
    if(null != Instance2)
    {
        Vector3 Position2 = Instance2.transform.position;
        Position2.z = Instance2Z;
        Instance2.transform.position = Position2;
    }
}

}


View(と名前のついているGameObject)の子に、EmptyのGameObjectを作成して上記のスクリプトをコンポーネントとして設置した上で、生成対象のプレハブ(~_Controlとついているプレハブ)をインスペクタで設定して、実行すると

・少し横にずれた位置で同じインスタンスを生成 ・Instance2のみマテリアルを差し替え ・Instance2がZ軸を0.0f~100.0fの間を行き来する(ですので、Instance1と途中で手前にいるのが入れ替わります)

挙動をします。 一応、Updateの中でマテリアルの差し替えを行っているのは、単にInstantiateで生成されたオブジェクトについているScript_SpriteStudio_LinkPrefab.csの処理が確実に終っている状態での差し替えを保証するためです。

いささか乱暴なサンプルとなっておりますが、OpenSesameDevelop様の環境で正常に動作するか、ご確認頂けますと助かります。

お手数をおかけいたしますが、何卒宜しくお願い致します。

MasamiYitsuse commented 9 years ago

また、もし状況が改善されない場合、 ・ご使用のUnityのバージョンと動作させている環境(Mac/WindowsなどやOSバージョン・グラフィックボードの種類) ・ご使用のSS5Player for Unityのバージョン を追加情報としていただけますと助かります。

OpenSesameDevelop commented 9 years ago

ご返答ありがとうございます。 テクスチャの差し替えに関しては頂いた物とほぼ同じ処理を行っており、問題無さそうでした。

まず、開発環境です。 MacOS X 10.9.5 グラフィックボードはオンチップでAMD Radeon HD 6630M Unity 4.5.5 SS5Player for Unity 1.1 先にお知らせしておくべきでした。申し訳ありません。

先程の説明では情報が足りませんでした。 Unityで作成したスプライトとの描画順番がおかしくなっているようです。 頂いたサンプルで下記の様な修正を行いました。 ・テクスチャ設定するオブジェクトを3体〜ぐらいに増やして横に並べる ・前後移動している中間ぐらいにUnityで作ったSpriteのオブジェクトを置く すると、テクスチャ書き換えオブジェクトの2体目以降はUnity Spriteの後ろに隠れる事が無くなります。 元のオブジェクトとテクスチャ書き換え1体目は正常に見えます。

以上、ご確認よろしくお願いします。

一応、修正したコードです。

using UnityEngine;
using System.Collections;

public class TestInstantiate : MonoBehaviour {

    public const int objLen = 3;

    public GameObject Prefab;
    private GameObject Instance1 = null;
    private GameObject[] Instance2 = new GameObject[objLen];

    private bool[] FlagChangeMaterial = new bool[objLen];

    private float Instance1Z = 50.0f;
    private float Instance2Z = 100.0f;
    private float DeltaZ = -1.0f;

    void Start()
    {
        if(null != Prefab)
        {
            Instance1 = Instantiate(Prefab) as GameObject;
            Instance1.transform.parent = transform;
            Vector3 Position1 = Vector3.zero;
            Position1.x = 60.0f - 450f;
            Position1.z = Instance1Z;
            Instance1.transform.position = Position1;

            for (int i=0; i<objLen; i++)
            {
                Instance2[i] = Instantiate(Prefab) as GameObject;
                Instance2[i].transform.parent = transform;
                Vector3 Position2 = Vector3.zero;
                Position2.x = 120.0f + i * 40f - 450f;
                Position2.z = Instance2Z;
                Instance2[i].transform.position = Position2;

                FlagChangeMaterial[i] = false;
            }
        }
    }

    void Update ()
    {
        // Material Change (Instance2)
        for (int idx=0; idx<objLen; idx++)
        {
            if((null != Instance2[idx]) && (false == FlagChangeMaterial[idx]))
            {
                // Get "RootParts"'s GameObject
                Transform transformChild = Instance2[idx].transform.GetChild(0);
                if((null != transformChild))
                {
                    // Get "RootParts"
                    GameObject gameObjectChild = transformChild.gameObject;
                    Script_SpriteStudio_PartsRoot   ScriptPartsRoot = gameObjectChild.GetComponent<Script_SpriteStudio_PartsRoot>();
                    if(null != ScriptPartsRoot)
                    {
                        // Get Texture
                        Texture2D TextureNew = Resources.Load("Development/001002_SDT_0") as Texture2D;

                        // ChangeMaterial
                        int Count = ScriptPartsRoot.TableMaterial.Length;
                        for(int i=0; i<Count; i++)
                        {
                            Material MaterialNew = new Material(ScriptPartsRoot.TableMaterial[i]);
                            MaterialNew.mainTexture = TextureNew;
                            ScriptPartsRoot.TableMaterial[i] = MaterialNew;
                        }
                    }

                    FlagChangeMaterial[idx] = true;
                }
            }
        }

        // Shift-Z
        Instance2Z += DeltaZ;
        if(0.0f > Instance2Z)
        {
            Instance2Z = 0.0f;
            DeltaZ = 1.0f;
        } else {
            if(100.0f < Instance2Z)
            {
                Instance2Z = 100.0f;
                DeltaZ = -1.0f;
            }
        }
        for (int i=0; i<objLen; i++)
        {
            if(null != Instance2[i])
            {
                Vector3 Position2 = Instance2[i].transform.position;
                Position2.z = Instance2Z;
                Instance2[i].transform.position = Position2;
            }
        }
    }
}
MasamiYitsuse commented 9 years ago

情報と検証用修正ソースのご提供ありがとうございます。

結果から申し上げますと、現在、SS5P for UnityのV1.1では、ドローコールの最適化の仕様の関係で、UnityネイティブのスプライトやNGUIスプライト(など、他のアセットでのスプライト機能)と混用した場合の描画順序の制御方法がありません。

https://github.com/SpriteStudio/SS5PlayerForUnity/wiki/FAQ こちらの(SS5P for UnityのwikiのFAQ内)「NGUI 上のコントロールの間に挟みたい」の項目にある通りなのですが……Unity-NativeやNGUIなどのオブジェクトを混用する場合、一筋縄ではいかない現状があります。

一つの解決方法に近いものとしては、Unity-Nativeのスプライトオブジェクトの描画位置が静的であるのでしたら、Script_SpriteStudio_DrawManagerView.csのLateUpdate関数内から呼び出されている、Library_SpriteStudio.cs内のMeshSetCombine関数でメッシュを描画順を守りながらバッチングして・マテリアルを描画順序にあわせて再生成している箇所があるので、そこを改造してUnity-Nativeのスプライトで使用しているマテリアルとの描画順を調整する方法もありはします。

ただ、どのように帳尻をとるかは作成されているアプリケーションの仕様に依存する部分が大きく「汎用的に」という手段は現在存在していません。

参考までに、最終的にUnityでの描画順序を決定するのは、そのスプライトで使用しているマテリアルのMaterial.renderQueueの値になり、この値が大きければ大きいほど、原則「後から」描画されます。 但し、この値はその値範囲毎で「描画レイヤー」としての意味を持っており、 http://docs-jp.unity3d.com/Documentation/Components/SL-SubshaderTags.html (このURLはUnityのシェーダのリファレンスになります)の中盤あたりに書いてある 「各キューは内部で整数インデックスにより表現され、Background は 1000、 Geometry は 2000、 AlphaTest は 2450、 Transparent は 3000、そしてOverlay は 4000です。」 という指標を元に 「シェーダ内のパラメータであるQueueの値を操作して、プライオリティを決定する」方法と同じ方法で、適時、ユーザーが制御してやる必要があるかと思います。 (Unityの挙動上、シェーダ内のQueueのパラメータと、Material.renderQueueの値設定は同値となり・同期しています)

大変申し訳ございませんが、 ・上記を踏まえた上でUnity-Nativeスプライトと描画順をMaterial.renderQueueの値で調整する か、 ・Unityスプライトで作成しているスプライトも、SpriteStudioで作成するか のどちらかを選択していただけますようお願い致します。 (あくまで私個人の見解としましては、描画順序がZ値などで動的に変わるスプライトの場合、SpriteStudioで統一してスプライトを作成した方が、扱いや最終的なドローコール最適化がスムーズに行われると思います)

大変申し訳ございませんが、何卒ご理解の程お願い申し上げます。

MasamiYitsuse commented 9 years ago

補足ですが、SS5PUで最適化メッシュに合わせて、マテリアル毎のメッシュの描画順序を設定している箇所は、 Library_SpriteStudio.csの959行目~970行目の「/* Material-Table Set */」の注釈がある箇所になっています。

ここで、MaterialRenderQueueに「OffsetDrawQueue + i」を加算している行がありますが、この「i」を調整してやり(例えば「(i * 5)」とかにして)・その値の間をUnity-Nativeのスプライトで使用しているマテリアルに設定してやることで、間にオブジェクトなどを挟むようなことも可能かと思います。 (但し、上記の通り、この方法も作成アプリケーションの仕様によりますので、万能とはいえないかと思いますが……)

もし、Unity-Nativeでのスプライトがテクスチャに半透明部分(アンチエリアス部分も含む)を持っていないのであれば、そちらの描画をTransparentではなくGeometryなどで行って、Zバッファのチェックで前後を付けるという手もありますが……Unity-Nativeのスプライトに半透明ピクセルがある場合、その半透明部分が重なった部分の描画がおかしくなる可能性があります。

OpenSesameDevelop commented 9 years ago

いろいろ情報ありがとうございます。 状況理解しました。 仕様合わせて実装を検討してきます。