VRChatでパターゴルフ場を作る①


全体構成(随時アップデート)

  1. VRChatでパターゴルフ場を作る①...フィールドでまずはボールを弾くまで

今回の内容:フィールドでまずはボールを弾くまで

どちらかと言えば全体構想を練りまくってスタートするよりもできることの確認からスタートするタイプのため、今回もまずはボールを打ってみようと考えてスタートした。最終的には円柱の棒を手に持ち、とりあえず打つことには成功したのでそこまでを書く。

事前に学んだこと・参考にしたサイト

まずやってみるという割に本は好きなので本から入った。VRChat専門の本は現在では見当たらず、また統合的にまとめているサイトもなかったためUnityの基本的な使い方がわかる本から入った。なお、C#は昔本を一冊読んだ程度には知っている。
Unityの教科書 Unity 2021完全対応版 2D&3Dスマートフォンゲーム入門講座 (Entertainment&IDEA)
本の内容で学んだことは下記。

  • Unityの基本のすべて(全体の構成、GameObject、Component、Prehab、プログラム上でのそれぞれの参照の仕方、Collider、Rigidbodyなど)
  • C#の復習
  • Terrainの使い方
  • テキストの表示のさせ方など

また、下記のサイトを参考にした。主にスティックを手に持つための座標関連。
UdonSharp日記~ローカルプレイヤーの座標の取得~ - Qiita
UdonSharpメモ ~コードの付箋~ (zenn.dev)
GravityUdonワールドのように飛ぶギミック · GitHub
【Unity入門】必ず分かる!一番簡単なQuaternionの使い方入門!
Unity 3Dオブジェクトの原点は変えられるのか #23.1

フィールドの作成

最初に作ったフィールドはこのような形。

見えにくいが奥に地面の隆起がある。今回のフィールドはTerrainで作ってある。Terrainはフィールドを作るのに最適で、ゲームで使いそうな地形を作ることができる。今回はパターゴルフということで地面に隆起を作りたくてTerrainを使った。詳しい使い方は書かないが流れは下記のような流れ。

  1. Hierarchyの右クリックで3D Object → Terrainを配置
  2. TransformのPositionを(X, Y, Z) = (-500, 0, -500)に移動
  3. TerrainのPaint Terrainボタンを押しRaise or Lower Terrainを指定
  4. フィールドを触って隆起を発生(今回はBrush Size 2を使用)

パター場ということで芝生を配置したかったため、Asset StoreでStandard Assetsというものを入手しImportした。
引き続きTerrainで
5. TerrainのPaint Terrainボタンを押しPaint Textureを指定
6. Create LayerでGrassHillAlbedoを指定

ここまででフィールドは完成。Terrainは自動でTerrain Colliderというものが付くため、置くだけでそのフィールドに乗ることができるようになる。

パタークラブとボールの用意

クラブというかただの円柱をまずは用意した。これは書くまでもないかもしれないが、Cylinderを使って実現した。Scaleは(X, Y, Z) = (0.1, 0.9, 0.1)、あまり調整はしてないがこれ以下にすると届かない。衝突判定はもともとついているCapsule Colliderをそのまま使用、特に設定はなし。Inspectorの一番下のAdd ComponentでVRC Pickupを追加、ここで本来はOrientationなどをいじって持ち方を変えるようだが、どれにしてもうまく持ってくれないため持つ方向はUdonのスクリプトで設定することにした。
ボールもただのSphereを配置してscaleを全方向0.1に設定したのみ(ここでしっかり数値を調整していれば、悲劇的な挙動はしなかったかも・・・)。

パタークラブ(ただの円柱)を持つ

ここが今回一番苦労した。大きく分けると2つで、1つは持った時に手に追従させるところ、もう1つはその向き。
まず持つこと自体は難しくなく、CylinderにVRC Pickupをつけた時点で持てるようになる。持った時の向きはAny, Gun, Gripの3つのなかから選ぶようになっている。(参考:https://vrcprog.hatenablog.jp/entry/component-Pickup)この中で適合しない場合は自分で調整が必要(よくよく考えると合わなかったらCylinderのGravityをOffにしてRotationなどを調整しておけば良かったのかも?)。今回はパタークラブを握っている感覚が欲しかったので手にピッタリ追従するようにUdon Sharpでスクリプトを書いた。

まずはCylinderのAdd ComponentでUdon Behaviourを追加し、New Program、Create Scriptと押していき、Udon Sharp用のスクリプトを作成する。

Cylinder.cs

using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

public class Cylinder : UdonSharpBehaviour
{
    private VRCPlayerApi targetPlayer;
    public GameObject playModeObject;

    public VRCPlayerApi.TrackingDataType type;

    void Start()
    {
        //今のところおまじない、入れないと動かない
#if !UNITY_EDITOR
        targetPlayer = Networking.LocalPlayer;
#endif

    }

    void Update()
    {
        //他のデバッグで使った項目
        //Vector3 pos = targetPlayer != null ? targetPlayer.GetTrackingData(type).position : playModeObject.transform.position;
        //Quaternion rotate = targetPlayer != null ? targetPlayer.GetTrackingData(type).rotation : playModeObject.transform.rotation;

        //右手位置を取得
        Vector3 rightHand_pos = targetPlayer.GetTrackingData(VRCPlayerApi.TrackingDataType.RightHand).position;
        Quaternion rightHand_rot = targetPlayer.GetTrackingData(VRCPlayerApi.TrackingDataType.RightHand).rotation;
        
        //クラブの位置を右手と同じに
        transform.position = rightHand_pos;

        //クラブを手で握るような方向に回転
        Quaternion move_q = Quaternion.Euler(90f, 00f, 0f);
        transform.rotation =rightHand_rot*move_q;

    }
}

手の追従

苦労した点の1つ目の手の追従。ここは意外と手の位置を取得する方法を調べるのに苦労した。最終的にはGetTrakingDataを使うことで実現できたが、最初はOculusのAssetをいれてOVRInputで実現しようとしたため時間がかかった。

今回やった結果としてわかったことはOVRInputはUnity上では使えるがVRChatでは使えないかもしれない点。まずC#スクリプトでOVRInputを使うと問題なくビルドされ、Game Viewでは確認できるがVRChatでは動作しなかった。次にUdon Sharpスクリプト上でOVRInputを使用しようとしたところビルドができなかった(依存関係の問題?)。結局OVRInputを使うのをあきらめてGetTrackingDataを使うことにした。

右手位置は引数にVRCPlayerApi.TrackingDataType.RightHandを指定することで取得できる。これをtransform.positionに入れることで手に追従させることができた。

UdonSharpでクラブを回転

もう一点は回転について、位置を変えただけだとイメージとしては手のひらを貫くような方向にCylinderが配置されるようになる。グリップ感を出すためにはコントローラのグリップと向きを同じにする必要があったため90度回転が必要だった。

ややこしいのは現在の向きがQuaternionで示されるため、どの向きに回転すべきかわからないところだった。今回は適当にオイラー角の軸を一つずつ変えて90度回転させて正解を探す方法をとった。最初は真面目にQuaternionについて勉強し始めていたが、30分みたところでチンプンカンプンだったためあきらめた。調べてみるとQuaternionにするのは計算を楽にするためで結局みんなオイラー角で指定していたのでそれだけでよかった。今回の正解はXに90度を指定することだったため、Xに90度を入れてQuaternionを作り右手の向きに乗算することでtransform.rotationを設定した。

出来上がり