Transform値の引き戻り現象
UnityはGameObjectの親子関係を変更してもワールド座標は維持されるので、位置や角度が変わったりすることはないのが普通です。
しかし、VRChatのアバターなどで、GameObjectの親子関係を変更すると、ローカル座標のほうが維持されてしまい、ワールド座標の位置や角度がおかしくなってしまう現象が発生します。
こんな感じですね。
※こちらは妻が調整したシルヴィアさんの改変アバターです
先日、私が発表したスカートDynamicBone設定支援ツール「SkirtSupporter」でこの現象が発生してしまいました。(修正済です)
いや、それより前から発表しているDynamicBone吊りスカートの理論を各自でやってみた方も中にはこの現象が発生していたかもしれません。
それを思うと申し訳ないです。
この現象を突き詰めてみると、そこに組み込まれているDynamicBoneに原因があることがわかりました。
どういう必要があってかはわかりませんが、DynamicBoneの視覚化機能の中に、内部で保持してるローカル座標をTransformに書き戻す処理が入っています。
#手操作で発生したときの対処法1
「X」というGameObjectの親を「A」から「B」に変更したい場合とします。
前提として、シーンのどこかに、Rootに「X」が指定されたDynamicBoneが存在するはずです。(多くの場合は「X」そのものにあるでしょうが、他のGameObjectにあっても同じことです。)
問題が発生した後だともう対処できないので、Ctrl+ZやプレハブのRevertなどで状態を戻したところから開始してください。
- 該当のDynamicBoneのRootに他のオブジェクトを設定する(ただし、親でもない無関係のものとする)
- 「X」を「A」から「B」に移動する
- DynamicBoneのRootを「X」に戻す
これでOKです。
#手操作で発生したときの対処法2
親子関係を変更したい対象が沢山あって、対処法1では煩わしい場合です。
一時的にDynamicBoneのスクリプトを書き換えてしまいましょう。
void OnDrawGizmosSelected()
{
if (!enabled || m_Root == null)
return;
if (Application.isEditor && !Application.isPlaying && transform.hasChanged)
{
//InitTransforms(); <- 一時的にコメントアウト
SetupParticles();
}
:
:
225行目あたり、OnDrawGizmosSelectedの中のInitTransforms();を「//」でコメントアウトします。
これで、GameObjectを参照したときの書き戻しが発生しなくなります。
親子関係の変更を行ってください。
しかし、このままツールのコードを壊しておくのは決して望ましくありません。
親子関係の変更が終了したら、必ず元に戻しておいてください。
#スクリプティングの対処法
さて、Editorなどのスクリプトを組もうとする場合ですが、
結論としてはDynamicBoneのコードを破壊せずに根本的に対処する方法が無くて詰みました。
内部の「p.m_InitLocalPosition」「p.m_InitLocalRotation」の値を外部操作で更新できればいいのですが……。
現状、これらの値を書き換えられるpublicメソッドはありません。
良い方法があれば教えてください。
とりあえずの対処法としては、「変更前後の親のワールド座標を完全に一致させる」ことです。
親の座標が一致していれば、ローカル座標が書き戻っても問題なくなります。
変更先のGameObjectの下にもう一つGameObjectを加え、positionとrotation(いずれもワールド座標)を変更前の親と一致させ、そこへ移動させるようにしましょう。