VRCワールド作成をしようとUdonsharpでコードを書いたところ沼ったのでメモ
やりたいこと
ピックアップオブジェクトをピックアップしたあと、Useすると手元から一定座標オフセット下箇所に別のオブジェクトをスポーンさせる。
最終的に出来上がったのが下の画像。
Magic: the Gathering のカードパックを見立てたオブジェクトをUseすると、
排出されたカードがパックの上に表示される用になっている。
座標変換
Unityには便利な機能があって(たどり着くまでだいぶかかったが)
ワールド座標とローカル座標を交互に変換できる関数が用意されている。
参考にしたサイト: 【Unity】ワールド座標とローカル座標の相互変換
公式ドキュメント: Transform.TransformPoint
ワールド座標 → ローカル座標への変換
transform.InverseTransformPoint(座標)
ローカル座標 → ワールド座標への変換
ransform.TransformPoint(座標)
実装
上の内容を踏まえて、最終的にできた処理順。
Useしたら手元のオブジェクトからy軸に0.3移動させた箇所にオブジェクトをスポーンさせたい場合
- Vector3で座標を宣言&手元のオブジェクトの座標を代入
- ワールド座標→ローカル座標に変換
- y軸方向に座標をずらす
- ワールド座標に戻す
- オブジェクトを宣言した座標に移動
の処理が必要だった。最終的に書いたコードは下記の通り。
実際のコード
(コピペなので余計な処理も書かれていることはご勘弁を)
// オブジェクトを複製
GameObject duplicatedObject = Instantiate(objectToDuplicate.gameObject);
// 複製したオブジェクトをシーンに追加 (非表示オブジェクトの複製時に必要)
duplicatedObject.transform.SetParent(transform);
// 複製後の位置を計算 (5x3のグリッド状に配置)
int row = i / 5; // 行
int col = i % 5; // 列
Vector3 currentSpawnPosition = transform.InverseTransformPoint(this.gameObject.transform.position);
currentSpawnPosition = SpawnPosition + new Vector3(col * xOffset, row * yOffset, 0f);
// 親オブジェクトの回転を取得
Quaternion currentSpawnRotation = this.gameObject.transform.rotation;
// ローカル座標でY軸180度回転させるQuaternionを作成
Quaternion localRotation = Quaternion.Euler(0, 180, 0);
// 現在の回転にローカル回転を適用
transform.rotation = currentSpawnRotation * localRotation;
// 複製したオブジェクトの座標を移動
duplicatedObject.transform.position = transform.TransformPoint(currentSpawnPosition);
duplicatedObject.transform.rotation = currentSpawnRotation;
沼ったこととか
まず上のワールド⇔ローカルの変換方法を見つけるのにしばらくかかったのはおいて、
次に沼ったのを4.のワールド座標へ戻す処理が必要なことに気が付かなかったこと。
ローカル座標のまま、オブジェクトをオフセット分ずらして最終的な座標に適用した結果、
オブジェクトが原点に移動する事になった。
(よくよく考えればそれはそうとしか言えない内容だったが)
ローカル計算したあと、移動させるときはワールド座標に変換し直すことをお忘れなく。
皆も気をつけましょう。