[Unity]uniSwfとNGUIのScrollViewを併用した際のクリッピング処理

前回は、Thetaで撮影したパノラマ静止画の移動ロジックにについてご紹介しました。
今回は、uniSwfとNGUIのScrollViewを併用した際のクリッピング処理についてご紹介します。

以前の記事で登場したUniSwfの再登場です。
UniSwfは、Swfで作成したアニメーションを簡単に扱うことが出来るため非常に便利なAssetですが、NGUIと併用した際にいくつか問題があります。

1.NGUIのオブジェクトより奥に表示される
2.ScrollViewのクリップ機能が効かない

他にもあるかもしれませんが、私が使用した中で特に困った2点をピックアップしてみました。
これらの根本的な問題は、NGUIとuniSwfのレンダリングの設定をそれぞれのAssetが行っているためです。

まず、1つ目の問題点は、UnityのRenderQueueが関係してきます。
RenderQueueとは、シェーダーが持っているプロパティで、Unityにおいてマテリアルの描画順を制御するものです。
いろいろなサイトで説明されていますが、NGUIの各オブジェクトはRenderQueueが3000を基準とし、UIWidgetが持つDepthという奥行きを制御するプロパティの組み合わせで最終的なRenderQueue値が決定します。
このあたりの処理は、UIDrawCallというクラスによって行われているので、興味がある方は一度見てみるとよいと思います。
先ほどの3000を基準としている部分が、uniSwfのオブジェクトが奥に表示されてしまう原因です。
そのため、1つ目の問題はuniSwfのRenderQueueを3000より大きい値に設定することで解決することが出来ます。

■サンプル

MovieClipBehaviour movieClipBehaviour = GameObject.Find("MC").GetComponent();

Material[] materials = movieClipBehaviour.GetComponent().renderer.sharedMaterials;
int len = materials.Length;
for (int i = 0; i < len; i++)
{
    if (materials[i] != null)
    materials[i].renderQueue = 3100; //←3000以上に設定
}

上記サンプルでは固定値で3100を設定していますが、実際にアプリを作成する際は、いろいろなオブジェクトが配置されるため、他のオブジェクトに合わせて適当な値を設定してください。

続いて、2つ目の問題です。
NGUIのScrollViewのクリッピング処理は、先ほども出てきたUIDrawCallクラスで行っています。
そのため、UIWidgetをアタッチしてUIDrawCallの管理対象に追加することで解決できないか試してみましたが、うまく動作しませんでした。
中を見ていくと、Vectorの設定値を操作する方法でクリッピング処理を行っているようなので、もっと手軽に実装できないかと考えた結果、今回はシェーダーのマスク処理を使用することにしました。

MovieClipBehaviourのオブジェクト描画には、MeshRendererクラスを使用しています。
MovieClipBehaviourをアタッチすると自動的に、付与されるコンポーネントの内の1つですね。

マスク処理には、Unityの公式サイトでも紹介されているステンシルを使用します。
ステンシル処理自体は、こちらの記事を参考にさせていただきました。
使用しているAsset自体は違いますが、MeshRendererのマスク処理ということで、試してみたところマスク効果を実現することが出来ました。
kuuki_yomenaioさんありがとうございます。

ただ、これをuniSwfのオブジェクトに適応しようとした場合、一工夫必要です。
MovieClipBehaviourは、アクティブ時にuniSwfで用意されているシェーダーが強制的に設定されるようになっているようで、インスペクター上でマテリアルのシェーダーを設定するだけではマスク処理が行われません。
そのため、対象のオブジェクトがアクティブになった後に、スクリプトから動的にシェーダーを上書きします。
リンク先でご紹介されている2つのシェーダーを用意して、Resources配下に格納して下さい。

その後、下記のスクリプトにてMovieClipBehaviourがアタッチされているシェーダーを書き換えます。

Shader shader = Resources.Load("Shaders/SpineShader", typeof(Shader)) as Shader;
Material[] materials = movieClipBehaviour.GetComponent().renderer.sharedMaterials;
int materialCount = materials.Length;
for (int i = 0; i < materialCount; i++)
{
    if (materials[i] != null)
    {
        materials[i].shader = shader;
    }
}

Resourcesからシェーダーを取得している理由ですが、バイナリにした際にShader.Findで見つからないという問題を回避するためです。
Resources配下のファイルは、ビルド時に全てのファイルがバイナリに内包されるため、予期せぬ事故を防ぐためにもこちらの方法を使用するのが良いと思います。

ここまで設定できれば後はマスク用のオブジェクトを配置すれば終了です。
NGUIのScrollViewのクリップ領域に、同じ大きさのオブジェクトを重なるように配置します。
今回は32*32の真っ白な画像でNGUIのUI2DSpriteを用意し、UIWidgetのSizeプロパティにクリップ領域と同じ値を設定しました。
シェーダーには、リンク先でご紹介されているSpineSoriteMask.shaderを設定するのを忘れないようにしてください。
このシェーダーの設定がマスク処理で一番重要です。

これで全ての設定が終わりました。
設定した値が、間違っていなければuniSwfオブジェクトがNGUIオブジェクトと同じように、ScrollViewの領域に合わせてクリッピングされているはずです。
もし、上手くクリッピングされていないようでしたら、もう一度設定している値を確認してみてください。

私は、今回使用した処理をまとめて行うカスタムクラスを用意して使用しています。
MovieClipBehaviourクラスを継承しているため、インスペクターでMovieClipBehaviourの代わりにアタッチするだけで、自動的にマスク処理が適応されるため、開発効率もよくなりました。
カスタムクラスを用意したことで、今後MovieClipBehaviourに関連した処理の追加が必要になった際にも、柔軟に対応できるようになったため、みなさんもいろいろ考えて独自クラスを作ってみてはいかがでしょうか。

以上で、uniSwfとNGUIのScrollViewを併用した際のクリッピング処理のご紹介は終了となります。
最後までご覧いただきありがとうございました。

##1,2を設定する前と後の、オブジェクト構造キャプチャと動画サンプルです##
■各設定適用する前

■各設定適用した後


弊社では全国各地の請負い(ご自宅)で作業協力頂ける、フリーランスエンジニアの方を常時探しております。
ご興味ある方は、お気軽にお問い合わせ下さい。


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

*