初めてclusterでゲームワールドを制作してみたときのメモ

# 記事を書いた目的
- clusterでワールド作成自体が初めてだったので、忘れないように要点だけまとめておきたかったから。
- 続編に向けて、大変だったところを覚えておきたかったから。
- 作成時に使用したゲーム機能だけをリストアップしたかったから。
    - 実際にはもっと沢山のゲーム機能がありますが、ほとんど使いきれていません。

# 制作したワールドについて
- clusterでプレイできるVRリズムゲームです。
    - https://cluster.mu/w/17569170-80b1-4960-96a7-b5b25dc7deb1
- キセルを両手に持ってノーツを叩いて消していきます。（当てるだけで消えます）
- 沢山ノーツを消すと高ランクをゲットできます。
- WindowsPCでHTC VIVEで動作確認済み（制作したマシンがこれというだけですが。）
    - スマホやデスクトップモードではおそらくキセルが片手でしか持てないので高ランクは難しいですが、楽曲の視聴はできると思います。
- プレイ例
    - 動画はこちら（ショートバージョン）
        - https://youtu.be/ZxSACAAQJdk
    - 動画はこちら（フルバージョン）
        - https://youtu.be/_5tjwz_tQBE
    - 画像例
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104564/0ac07c92-c2a3-032d-f8af-f11a07bb11c5.png)
    


# 制作のきっかけ
- 2020年6月からclusterでゲームを作るための新機能が実装されました。
- ちょうど同じタイミングで、2020年6月5日に、いつも密かに応援させていただいている疾風P氏が新曲「初音吹雪に御用心！」を公開されました。
- 「公開記念に何か作りたい！」と思い、Beat〇aberのようなVR音楽ゲームがclusterでも出来ないだろうかと考えたのがきっかけです。
- しかもゲームワールドコンテストというのがclusterで開催されていたので合わせて応募してみようと思いました。
    - でもコンテストに気づいたのが6月末だったので、正直間に合わないだろうと思って、とりあえず作るだけ作るつもりでした。
- 色々四苦八苦しましたが、何とか公開しました。

# 楽曲について
- 初音吹雪に御用心！
    - ニコニコ動画
        - https://www.nicovideo.jp/watch/sm36982274
    - Youtube
        - https://youtu.be/KDdH_0bbQmU

# 使用したゲーム機能
- 使用した場所ごとに紹介
    - **STARTボタン**
        - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104564/a2423379-63b3-a41d-bebb-c9b4f1e0d9a9.png)
        - フォーカスを合わせてクリックする（トリガーを引く）と楽曲が開始される
        - ゲーム機能
            - Interact Item Trigger
                - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104564/808337e0-cbdc-9bab-5d24-af4d77e3329a.png)
                - トリガーが発動するとKey「startbutton」が送信されるように設定
                - ゲームオブジェクト「TimelineManager」にアタッチされているSet Animator Value Gimmickが上記のKeyを受け取り、タイムラインを開始させて楽曲（ノーツや演出も含めて）を再生する。
    - **STOPボタン**
        - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104564/f492a183-3b12-d8d4-ccac-4b84d7bf93c0.png)
        - フォーカスを合わせてクリックする（トリガーを引く）と楽曲が停止し、初期状態へ戻る
        - ゲーム機能
            - Interact Item Trigger
                - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104564/2a01b07f-22c5-93b6-f5f8-b800ee981c3f.png)
                - トリガーが発動するとKey「stopbutton」が送信されるように設定
                - ゲームオブジェクト「TimelineManager」にアタッチされているSet Animator Value Gimmickが上記のKeyを受け取り、タイムラインを開始させて楽曲（ノーツや演出も含めて）を停止する。
                - 楽曲再生用のタイムラインとは別のタイムラインを読んで完全停止させている。
                - 今思えば、なぜSTARTボタンのTriggerのTargetを「This」にして、STOPボタンは「Global」にしているのか、謎だ。どちらかに統一しておいてもよかった気がする。
    - **TimelineManager**
        - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104564/46e7c303-1010-3d91-ed98-96b291b05deb.png)
        - 上記のSTARTとSTOPボタンに対応して、タイムラインを呼び出すゲームオブジェクト
        - 管理用オブジェクトなので実体はない
        - ゲーム機能
            - Set Animator Value Gimmick
                - STARTとSTOPのトリガーに対応して、「active」と「deactive」のAnimator ParameterをAnimatorへ送信
    - **キセル（左右）**
        - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104564/f38da9dc-326a-0147-35af-f545f11e524c.png)
        - 左右の手でグリップを握ることで持つことができる
        - キセルの先端をノーツに当てることでノーツを消すことができる
        - ゲーム機能
            - Movable Item および Grabbable Item
                - 握って持てる機能の実装
            - On Collide Item Trigger
                - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104564/dae60f12-0401-602d-6cc3-23efb9d336fd.png)
                - 衝突相手（ノーツ）へ「hitL (hitR)」を送信
                - 全体へ「distort」を送信
            - On Grab Item Trigger
                - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104564/35742748-0e6c-fcb3-b734-b6b3168f349e.png)
                - キセルを握ったときのトリガーとして「grabL (grabR)」を送信
            - Play Audio Source Gimmick
                - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104564/68bdffac-1fb2-c9d9-4d0f-47d72be6756b.png)
                - 上記の「grabL (grabR)」を受け取り、握った時のサウンドを鳴らす
    - **ノーツ（赤と青の三つ巴マーク）**
        - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104564/90d1e2e3-a2aa-b481-ade7-056adab3feac.png)
        - キセルに接触したら蝶のパーティクルと音を発生して消える
        - ゲーム機能
            - Set Animator Value Gimmick
                - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104564/3102120d-0c20-5582-04c2-c3f273d43ef0.png)
                - 蝶のパーティクルを発生
            - Play Audio Source Gimmick
                - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104564/48b75e9c-c4f3-74a6-6e1b-0910c7764246.png)
                - 消えるときのタンバリンの音を再生
    - **地面から上がるパーティクル**
        - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104564/ee9d498c-2ee9-b828-e94a-5bf966b13faa.png)
        - ノーツが消えたときに、消えたノーツに応じて、赤と青のどちらかの色に変化する
        - ゲーム機能
            - Set Animator Value Gimmick
                - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104564/9150c782-ce16-fdc5-dc45-c93507ecd769.png)
                - 消えたノーツからのKey「hitL(hitR)」を受け取ってアニメーションでパーティクルの色を変えている
                - このコンポーネントはノーツごとに複数アタッチしている。（左右８ノーツなので１６つ分）
                - 各ノーツがキセルから「hitL(hitR)」を受け取って、そのノーツからこのパーティクルが「hitL(hitR)」を受け取るという横流しのような感じのことをやっているが、果たしてこの使い方が合っているのかわからない。。。（別の方法があるかも）
    - **地面の歪み**
        - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104564/7fc07b35-d55a-c6a1-6d3e-d7a4bdc56777.png)
        - キセルが何かにヒットした場合にゆがむ
        - ゲーム機能
            - Set Animator Value Gimmick
                - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104564/54f97c33-91a2-d372-70a4-10feb4d42246.png)
                - キセルから送信された「distort」によってアニメーション再生
                - Globalを使っているので何にヒットしてもゆがむことになります。
    - **ランクゲージ（左右）**
        - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104564/caf19494-11ad-f6a4-4335-07c8cc8de71a.png)
        - ノーツを消したときにゲージが少しずつ溜まっていく
        - ゲーム機能
            - Set Animator Value Gimmick
                - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104564/cb8aad6b-660a-632a-0ba2-0a5a7e178332.png)
                - 消えたノーツからのKey「hitL(hitR)」を受け取って、ゲージを少しずつ加算「add」していく
                - 地面から出るパーティクル同様、ノーツ分アタッチしている
         
# clusterのゲーム機能以外の部分で補足
- ベースとなったワールドについて
    - ClusterCreatorKitSample内のシーン「MinimalSample」を使用させてもらいました。
        - https://github.com/ClusterVR/ClusterCreatorKitSample
- ゲーム開始時の緞帳（どんちょう）について
    - 画面全体を覆いたかったので、別のカメラを使用しています
        - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104564/e75d0dc3-6fc1-eeb3-bc5e-c12445627769.png)
        - Clear Flagsを「Depth only」にします
        - Field of Viewはすごく小さく「0.1」にしていますが、こうしなくてもいいかもしれません。
        - Depthを「0」以上にすることで前面に貼りついて見えます。
        - Target Displayは「Display 1」にします。（おそらくMain Cameraと同じにするということ？）
    - 別のカメラへ出力するCanvasを用意します
        -  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104564/e0e29fac-c418-0087-e5b1-6547b45c0592.png)
        -  Render Modeを「Screen Space - Camera」にします
        -  Render Cameraを上記で作成したカメラに設定します
        -  Plane Distanceは「900」と非常に遠くしていますが、別にこの数値でなくてもいいかもしれません。
    - Canvas内の画像について
        - Imageコンポーネントで表示しています
            - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104564/eba9e2ca-e2da-1b97-7a95-fde3d74b0cbd.png)
            - Sprite Rendererを使いたかったのですが、実際使うと何か２重に重なって見えるような感じになってしまったのでやめました。
- タイムラインのノーツについて
    - 左８ノーツ、右８ノーツを用意して、出したいときにActivation ClipをActiveにしています。
    - 各ノーツは更に４つまでゲームオブジェクト複製して用意しているので、同時に４つまで出せます。（逆に４つ以上出せない）
    - タイムライン画像
        - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104564/bd1542ae-a777-54ce-c490-e46e69b9b27d.png)

# 大変だったところ
- ノーツを楽曲に合わせてタイミングよくTimelineに配置していくところ
    - １つ１つほぼ手作業でした（400個ほどあった）
- そもそも和風の雰囲気をどうやって作ればいいか。
    - これを考えるところが一番時間かかったかも。

# 焦ったところ
- clusterのアップロードはとてもシンプルでやりやすかったですが、エラーで止まったときは完全に詰んでしまいました。（再アップロードしたらできたので助かりましたが。）

# できなかったこと
- できるかもしれないけど自分ができずに諦めたところを紹介します。
    - キセルを振る方向を判定する
        - Beat〇aberのような、いわゆる「切る方向」を決めることができませんでした
        - しかし、これはノーツに、方向に応じて成功と失敗のコライダーを付けてあげればできそうな気もする。
    - 歌詞の焦点が合いずらい
        - Cameraの設定を色々いじってみましたがあまり改善されなかったので、結局今の状態に落ち着きました。
        - VRで左右のカメラがどのように視差を設定しているのかよくわかっていない。
    - 同じ位置のノーツは連続４つまで
        - 連続して同じ位置へ短い間隔でノーツを出したい場合は連続４つまでにしました。
        - これは増やそうと思えばもっと増やせたのですが、作りが非常に大変になるため４つまでの制限にしました。
        - また、プレハブをタイムラインから生成して呼び出すことも試したのですが、なぜかギミックがうまく動かなかったので諦めました。（これができれば一番楽だったのだが。）

# 次回やりたいこと
- キセルの振る方向を判定する
- 複数人でプレイできるようなゲーム性
- 振って当てる形式もいいが、歩いて踏む形式もやってみたい。
- 軽量化をほとんど考えていなかったので次回は少し考えたい。（110MBもあった）
- スマホで動作確認をしている暇がなかったので次回はやりたい。
- ノーツの曲に合わせた微調整が大変すぎて丸２日くらいかかったので、もっと楽な方法を考えたい。。。

# まとめ
- ほとんど「Set Animator Value Gimmick」を使っている感じでした。
- あとは、Unityのアニメーションで力業で動かしていった感じです。


# お借りした素材
- のりべん専門店
    - https://noriben.booth.pm/
        - Noriben Fireworks（花火パーティクル）
        - ビームライトシェーダーとモデル
        - ゆがみシェーダー（空間歪ませ Distortion Space）
        - 桜パーティクル
    - かなり活用させていただきました！ありがとうございました！
- virtual-boys
    - https://virtual-boys.booth.pm/
        - GPUパーティクルセット ver1.3
    - 床から出てくるパーティクルで使用
- Japanese textures
    - https://www.deviantart.com/thethecoco/art/Japanese-textures-96321419
    - 背景に使用
- pattern-sozai.website
    - http://www.pattern-sozai.website/data/%E5%92%8C%E9%A2%A8%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E6%9F%84%E3%81%AE%E5%90%8D%E5%89%8D
    - 床に使用