普段からなにげなく使っているInstantiateメソッドですが、パラメーター変えたときの挙動で迷ったことはありませんか?
パラメーターでparentを設定したらpositionは親の位置に移動するんだっけ?
scaleも一緒に変わるんだっけ?
とか迷ったことはありませんか?
そこで少し整理してみました。
試したこと
まずはUnityのObject.Instantiateスクリプトリファレンスを参照すると、以下パラメーターがあることがわかります。
これら全てのメソッドを使用しつつ、子のGameObjectが全て同じ位置になるように調整するプログラムを書いていきます。
public static Object Instantiate(Object original);
public static Object Instantiate(Object original, Transform parent);
public static Object Instantiate(Object original, Transform parent, bool instantiateInWorldSpace);
public static Object Instantiate(Object original, Vector3 position, Quaternion rotation);
public static Object Instantiate(Object original, Vector3 position, Quaternion rotation , Transform parent);
概要
親となるオブジェクトと子となるオブジェクトについて
実行したこと
- Hierarchyにparentを置いておきます。
- それぞれのInstantiateでchildを生成していきます。
- 最終的に2で作ったchildが同じworldPos、WorldRotになるように調整します。
実行内容詳細
基本的なとこから
完全に主観ですが、よく使うところから。
ここで気をつけるのは、parentを設定しているInstantiateWithParent
に対し、InstantiateSimple
、InstantiateWithPositionAndRotation
はparentを設定した後にscaleの変更をしているところかと思います。つまりInstantiateするときにparentを設定していると、自動的にparentのscaleに設定されていることがわかります。
private void InstantiateSimple()
{
var obj = Instantiate(child);
obj.name = "Simple";
obj.transform.localPosition = worldPos;
obj.transform.localRotation = worldRot;
obj.transform.parent = parent.transform;
obj.transform.localScale *= parent.transform.localScale.x;
}
private void InstantiateWithPositionAndRotation()
{
var obj = Instantiate(child, worldPos, worldRot);
obj.name = "PositionAndRotation";
obj.transform.parent = parent.transform;
obj.transform.localScale *= parent.transform.localScale.x;
}
private void InstantiateWithParent()
{
var obj = Instantiate(child, parent.transform);
obj.name = "Parent";
obj.transform.position = worldPos;
obj.transform.rotation = worldRot;
}
次はparentを設定時のパラメーターを整理
instantiateInWorldSpaceを設定した場合を見てみます。
先ほどInstantiateするときにparentを設定しているとparentのscaleが設定ということを書いたのですが、ここでみた通り、instantiateInWorldSpaceを設定していると、初期設定としてWorldSpaceに配置されるだけでなく、Scaleも変更されないことがわかります。つまり先ほどの記載は完全には正しくなく、falseが設定されている時だけということです。
private void InstantiateWithParentInWorldSpace()
{
var obj = Instantiate(child, parent.transform, true);
obj.name = "ParentInWorldSpace";
obj.transform.position = worldPos;
obj.transform.rotation = worldRot;
obj.transform.localScale *= parent.transform.localScale.x;
}
最後は全てを設定する場合
とてもシンプルですね。今回の設定をGameObjectにするのであれば、この利用方法が一番良いかと思います。
private void InstantiateWithAllOptions()
{
var obj = Instantiate(child, worldPos, worldRot, parent.transform);
obj.name = "AllOptions";
}
今度こそ本当の最後
今回はそれぞれInstatiateしたGameObjectをWolrd座標の同じ位置に配置しました。本当の最後ではlocalPosition/localRoationを使って設定する場合を考えてみます。
private void InstantiateWithParentAndSetLocalValue()
{
var obj = Instantiate(child, parent.transform);
obj.name = "ParentWithLocalValue";
// よく使うので一回pTrnの変数におく
var pTrn = parent.transform;
// Instantiateした時点でparentの位置に設定されているので、残りの移動に必要なworldPosとparentの位置の差分を取得する
var diff = worldPos - pTrn.position;
// ワールド座標での差分を、localに適用するためparentのスケールで調整する
var scaledLocalPos = diff / pTrn.lossyScale.x;
// parentが回転してしまっている分を戻して、localPositionに設定する
obj.transform.localPosition = Quaternion.Inverse(pTrn.rotation) * scaledLocalPos;
// parentが回転してしまっている分を戻して、localRotationに設定する
obj.transform.localRotation = Quaternion.Inverse(pTrn.rotation) * worldRot;
}
実際にこのようなことをすることはないかもしれませんが、localで考えようとすると急に複雑になるなと感じると思います。それでも一つずつ見ていけば、そういうものかという感じでもあると思います。一つずつ考えていくためには、おまけの前までの知識が整理されていることが前提になると思います。こういところを整理しておいて、いざという時に備えたいですねという記事でした。
サンプルプログラム
GitHubにプログラム、シーンなどをあげてあります。良かったら手元で動かして試してみてください。