【初心者向け】UnityのInstantiateメソッドのパラメーターによる挙動を整理してみる

普段からなにげなく使っているInstantiateメソッドですが、パラメーター変えたときの挙動で迷ったことはありませんか？
パラメーターでparentを設定したらpositionは親の位置に移動するんだっけ？
scaleも一緒に変わるんだっけ？
とか迷ったことはありませんか？

そこで少し整理してみました。

# 試したこと
まずはUnityの[Object.Instantiateスクリプトリファレンス](https://docs.unity3d.com/ja/2018.4/ScriptReference/Object.Instantiate.html)を参照すると、以下パラメーターがあることがわかります。
これら全てのメソッドを使用しつつ、子のGameObjectが全て同じ位置になるように調整するプログラムを書いていきます。

```C#
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);
```


## 概要
### 親となるオブジェクトと子となるオブジェクトについて
* 親も子も同じ形で、色、サイズ違いにしていて、親がグレーの大きい方です。
* あまり深い意味はなく、向きがわかればいいかな程度です。
* 青の鼻が出ている方がfoward、緑のとさかが出ているほうがupです。
![スクリーンショット 2023-02-28 22.51.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506075/e7872209-3bed-d04f-94cd-85fd5ba8e317.png)

### 実行したこと
1. Hierarchyにparentを置いておきます。
2. それぞれのInstantiateでchildを生成していきます。
3. 最終的に2で作ったchildが同じworldPos、WorldRotになるように調整します。

## 実行内容詳細
### 基本的なとこから
完全に主観ですが、よく使うところから。
ここで気をつけるのは、parentを設定している`InstantiateWithParent`に対し、`InstantiateSimple`、`InstantiateWithPositionAndRotation`はparentを設定した後にscaleの変更をしているところかと思います。つまりInstantiateするときにparentを設定していると、自動的にparentのscaleに設定されていることがわかります。


```csharp
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が設定されている時だけということです。

```csharp
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にするのであれば、この利用方法が一番良いかと思います。
```csharp
private void InstantiateWithAllOptions()
{
    var obj = Instantiate(child, worldPos, worldRot, parent.transform);
    obj.name = "AllOptions";
}
```

### 今度こそ本当の最後
今回はそれぞれInstatiateしたGameObjectをWolrd座標の同じ位置に配置しました。本当の最後ではlocalPosition/localRoationを使って設定する場合を考えてみます。

```csharp
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](https://github.com/machidyo/InstantiateTest)にプログラム、シーンなどをあげてあります。良かったら手元で動かして試してみてください。