AnyPortrait > マニュアル > メッシュ取付時の「Sorting Order」設定
ゲームでは、キャラクターの外部のメッシュを衣装や武器のように取り付けます。
このとき、付着したメッシュのレンダリング順序を適切に修正することは非常に重要です。
他のマニュアルで「Sorting Order」を利用して、レンダリングの順序を定める方法について紹介したことがあります。
ここに加えて、このページは「[Extra設定」によって、リアルタイムでメッシュのレンダリング順序が変わる場合にも説明します。
このページに関連する、以下のマニュアルを一緒に見れば助けになるでしょう。
- スクリプト : 初期化、基本設定
- スクリプト : メッシュ、メッシュグループ
- 「Sorting Layer/Order」設定
- レンダリング順序と画像の切り替え
- キャラクターにアイテム装備
- 他のキャラクターとの同期
- ボーンを同期してコスチュームを変更
特にこの例では、「ボーンの同期」機能を利用します。
ボーンの同期の詳細については、関連ページでご確認ください。
説明のために作られたロボットキャラクターです。
この例では、一つの腕が体の周りを回りながら動くようにすることです。
腕が体の正面、背面に動くように作るためには、「関連ページ)を使用してください。
(1) 「Color Only(Controller)」モディファイヤを追加します。
(2) 「Extra Option」を有効にします。
(3) 腕の描画位置を制御する制御パラメータを生成した状態でキーを追加します。
(4) 腕メッシュを選択します。
(5) 「Setボタン」を押して「 Extra設定」ダイアログを開きます。
(6) メッシュの「Detph」が体の前、後方に移動するようにします。
腕が制御パラメータに応じて、体の前または後ろのレンダリングされるようになりました。
ボーンアニメーションまでに完成しました。
腕が体の周りをぐるぐる回りながら動くのを見ることができます。
新しいキャラクターを作成します。
このキャラクターは、「ロボットアームに装着されている機器の」です。
「Equip1〜4」という4つのメッシュがあります。
このキャラクターは、「ボーンの同期」を利用するために製作された。
製作方法と下のスクリプトの詳細については、関連ページで詳細を確認することができます。
今スクリプトを作成して装置がロボットアームに取り付けされるように作られてみましょう。
using UnityEngine;
using AnyPortrait;
public class RobotDepthSyncScript : MonoBehaviour
{
public Transform robotGroup; // ロボットの親GameObjectです。
public apPortrait mainCharacter; // ロボットキャラクターです。
public apPortrait equipment; // ロボットに取り付けること装置です。
void Start()
{
}
void Update()
{
// Aキーを押すと、装置が付きます。
if(Input.GetKeyDown(KeyCode.A))
{
Attach();
}
// Sキーを押すと、付着が解除されます。
if(Input.GetKeyDown(KeyCode.S))
{
Detach();
}
}
private void Attach()
{
// 装置をロボットの親GameObjectの子として登録します。
equipment.transform.parent = robotGroup;
equipment.transform.localPosition = Vector3.zero;
// ボーンを同期します。
equipment.Synchronize(mainCharacter, false, false, false, true, SYNC_BONE_OPTION.MatchFromRoot);
}
private void Detach()
{
// 装置をロボットの親GameObjectから分離します。
equipment.transform.parent = null;
equipment.transform.position = new Vector3(-5, 0, 0);
// 同期をオフにします。
equipment.Unsynchronize();
}
}
上記のような簡単なスクリプトを介して共通のボーンの二つのキャラクターが同期されるようにすることができます。
作成したスクリプトをUnityで適用してみましょう。
(1) 「RobotGroup」という新しいGameObjectを作成し、ロボットのキャラクターを子として登録されます。スクリプトを追加するGameObjectも生成します。
(2) スクリプトのメンバーまで割り当てて完成します。
ゲームを実行させてAキーを押すと、装置がロボットにくっついて動くのを見ることができます。
しかし、装置のメッシュがレンダリングされる順序が非常に奇妙に見えます。
スクリプトを修正して、レンダリングの順序を指定してみましょう。
using UnityEngine;
using AnyPortrait;
public class RobotDepthSyncScript : MonoBehaviour
{
public Transform robotGroup; // ロボットの親GameObjectです。
public apPortrait mainCharacter; // ロボットキャラクターです。
public apPortrait equipment; // ロボットに取り付けること装置です。
// Sorting Order値を参照するロボットアームのメッシュです。
private apOptTransform targetMesh;
void Start()
{
// 装置が取り付けられるロボットのアームメッシュの中「最も前にレンダリングされるメッシュ」を変数に格納します。
targetMesh = mainCharacter.GetOptTransform("HandMidGear");
}
void Update()
{
// Aキーを押すと、装置が付きます。
if(Input.GetKeyDown(KeyCode.A))
{
Attach();
}
// Sキーを押すと、付着が解除されます。
if(Input.GetKeyDown(KeyCode.S))
{
Detach();
}
}
private void Attach()
{
// 装置をロボットの親GameObjectの子として登録します。
equipment.transform.parent = robotGroup;
equipment.transform.localPosition = Vector3.zero;
// ボーンを同期します。
equipment.Synchronize(mainCharacter, false, false, false, true, SYNC_BONE_OPTION.MatchFromRoot);
// 装置がロボットアームに取り付けたときに、装置のメッシュのレンダリング順序を更新します。
RefreshMeshDepth();
}
private void Detach()
{
// 装置をロボットの親GameObjectから分離します。
equipment.transform.parent = null;
equipment.transform.position = new Vector3(-5, 0, 0);
// 同期をオフにします。
equipment.Unsynchronize();
}
// 装置のメッシュのSorting Orderを更新します。
private void RefreshMeshDepth()
{
// ロボットキャラクターの腕メッシュのSorting Orderを取得します。
int baseSortingOrder = targetMesh.GetSortingOrder();
// ロボットキャラクターの腕メッシュより前のレンダリングされるようにSorting Orderを一つずつ設定します。
equipment.SetSortingOrder("Equip4", baseSortingOrder + 1);
equipment.SetSortingOrder("Equip1", baseSortingOrder + 2);
equipment.SetSortingOrder("Equip2", baseSortingOrder + 3);
equipment.SetSortingOrder("Equip3", baseSortingOrder + 4);
}
}
修正されたスクリプトに基づいて、レンダリング順序が変わるように設定を変えてみましょう。
(1) メインキャラクターであるロボットキャラクターを選択します。(機器のキャラクターがありません。)
(2) 「Sorting Order Option」を「Detph To Order」に変更し、「Order Per Depth」を適当に大きな「10」に設定します。
(1) 親GameObjectを選択します。
(2) 「Sorting Group」コンポーネントを追加します。
ゲームを実行して、装置をロボットに装着すると、メッシュが正常に表示されるのを見ることができます。
しかし、完全に問題が解決されたわけではない。
このロボットは、「Extra設定」によって腕メッシュが体の後ろでレンダリングされることもあるからです。
ロボットアームが後ろからレンダリングされた時も装備メッシュは体の前でレンダリングがされていることを見ることができます。
(装置を取り付けるのタイミングによりましてはその逆になります。)
以前は、「付着したときに、メッシュのSorting Orderを指定」したが、この問題を解決するには、「リアルタイムでメッシュのSorting Orderを更新」する必要があります。
スクリプトをより修正してみましょう。
using UnityEngine;
using AnyPortrait;
public class RobotDepthSyncScript : MonoBehaviour
{
public Transform robotGroup; // ロボットの親GameObjectです。
public apPortrait mainCharacter; // ロボットキャラクターです。
public apPortrait equipment; // ロボットに取り付けること装置です。
// Sorting Order値を参照するロボットアームのメッシュです。
private apOptTransform targetMesh;
// 腕メッシュのレンダリング順序が更新されることを確認するための変数です。
private bool isAttached = false; // 装置が接続されているかどうかです。
private int prevSortingOrder = -1; // 前のフレームからのtargetMeshのSortingOrderの値です。
void Start()
{
// 装置が取り付けられるロボットのアームメッシュの中「最も前にレンダリングされるメッシュ」を変数に格納します。
targetMesh = mainCharacter.GetOptTransform("HandMidGear");
}
void Update()
{
// Aキーを押すと、装置が付きます。
if(Input.GetKeyDown(KeyCode.A))
{
Attach();
}
// Sキーを押すと、付着が解除されます。
if(Input.GetKeyDown(KeyCode.S))
{
Detach();
}
}
// AnyPortraitキャラクターのアップデートが処理された後にSorting Orderの値を参照するためにLateUpdate()を利用します。
private void LateUpdate()
{
// 装置が付着した状態のとき、ロボットアームメッシュのレンダリング順序が変わったのか確認します。
if (isAttached)
{
if (targetMesh.GetSortingOrder() != prevSortingOrder)
{
// 腕メッシュのSorting Orderが前のフレームと異なる場合、付着された装置のメッシュのSorting Orderを更新します。
RefreshMeshDepth();
// 処理が完了した後、現在のフレームのSorting Order値に更新します。
prevSortingOrder = targetMesh.GetSortingOrder();
}
}
}
private void Attach()
{
// 装置をロボットの親GameObjectの子として登録します。
equipment.transform.parent = robotGroup;
equipment.transform.localPosition = Vector3.zero;
// ボーンを同期します。
equipment.Synchronize(mainCharacter, false, false, false, true, SYNC_BONE_OPTION.MatchFromRoot);
// 装置がロボットアームに取り付けたときに、装置のメッシュのレンダリング順序を更新します。
RefreshMeshDepth();
// 付着直後に腕メッシュのSorting Orderを保存したり、付着したことをisAttached変数に格納します。
prevSortingOrder = targetMesh.GetSortingOrder();
isAttached = true;
}
private void Detach()
{
// 装置をロボットの親GameObjectから分離します。
equipment.transform.parent = null;
equipment.transform.position = new Vector3(-5, 0, 0);
// 同期をオフにします。
equipment.Unsynchronize();
// 付着が解除されたので、LateUpdate()で腕メッシュのレンダリング順序を続けるチェックしないようにfalseを入力します。
isAttached = false;
}
// 装置のメッシュのSorting Orderを更新します。
private void RefreshMeshDepth()
{
// ロボットキャラクターの腕メッシュのSorting Orderを取得します。
int baseSortingOrder = targetMesh.GetSortingOrder();
// ロボットキャラクターの腕メッシュより前のレンダリングされるようにSorting Orderを一つずつ設定します。
equipment.SetSortingOrder("Equip4", baseSortingOrder + 1);
equipment.SetSortingOrder("Equip1", baseSortingOrder + 2);
equipment.SetSortingOrder("Equip2", baseSortingOrder + 3);
equipment.SetSortingOrder("Equip3", baseSortingOrder + 4);
}
}
変更されたスクリプトの核心は、「基準となる腕メッシュの「Sorting Order」の値が変わったのか」を継続的にチェックすることです。
AnyPortraitキャラクターは「LateUpdate()」で更新されるため、ここでも「LateUpdate()」で「Sorting Order」をチェックする必要があります。
特に、前のフレームの「Sorting Order」を別の変数(prevSortingOrder)に保存して比較するための構文を注意深く見てください!
「LateUpdate()」で「Sorting Order」値を検査するように作成したが、それでもそのコードがAnyPortraitキャラクターのアップデートより先に呼び出すことができます。
この場合には、通常、「現在のSorting Order」を確認することが困難である。
このスクリプトは、AnyPortraitより後で動作するようにする必要があります。
(似たような問題に対して、関連ページで扱っています。)
(1) Unityの「Project Settings」を開き、「Script Execution Order」を選択します。
(2) 作成したスクリプトをドラッグして、スクリプトのリストに追加した後、「Default Time」より下に位置します。
ゲームを実行すると、ロボットの腕の動き、レンダリングの順序に合わせて装置が動いて表示されるのを見ることができます。
このページでは、特定のメッシュを変数に格納した後、「Sorting Order」の変化をチェックしました。
そのほかにも、「Extra設定を決定する制御パラメータの値」を利用したり、「アニメーションイベント」を利用することも可能です。
製作するイラストによってメッシュの順序が決まることです。
したがって取り付けられるメッシュがどのメッシュの前、後ろに配置されるなるリソースに応じて定してください。
このページでの場合のように、メッシュの名前と順序をスクリプトから直接指定することが迅速かつ簡単です。
しかし、さまざまなオブジェクトを付着する場合、別のデータやルールを決めて、スクリプトを作成することがよいでしょう。
別の画像や材質のメッシュが交わって表示される場合ドローコールが大幅に増加します。
これを解決するには、画像を共有したり、特別な材料を作成します。
私たちのチームは、このような場合にドローコールの増加を最小限に抑えることができる機能を考慮しています。
これに対する意見をいただければ、開発に積極的に反映します。