simplestarの技術ブログ

目的を書いて、思想と試行、結果と考察、そして具体的な手段を記録します。

CubeWalk:キューブの選択

まえがき

2度の作り直しを経て、現在は VRoid Hub SDK 経由で VRM キャラクターデータを利用して Cube の世界を自由に歩き回ることができるようになっています。
www.youtube.com

バックエンドサービスの PlayFab の CloudScript から API Gateway と Lambda 経由で AWS の ElastiCache のデータを読み書きできるようになったので
再び Unity クライアントサイドで、キューブの選択と破壊、設置を行える仕組みを入れていきます。

前試作映像

参考までに前回、実装していた内容を確認
www.youtube.com

データ自体はすべてクライアント側で処理していたので、不正し放題でしたが、これからはクライアントまでマスターデータは降りてこず
世界中のプレイヤーとキューブのデータをサーバー上でやりとりすることになります。

大きな違いは、前作は FPS (主人公視点)だったのに対し、今作は自身のアバターを確認しながら進められるようTPS(三人称視点)で操作が進みます。
一人称視点は画面中央にカーソルを置けば、視点操作のみでブロックを選択できましたが、三人称視点はそうはいきません。

新しい UI

ゲーム内にて、カーソルを表示し、見えている範囲でカーソルで選択したキューブに対してアクションができるようにしてみようと思います。
いずれ FPS をサポートしたときも、同様にカーソルを使って視点を動かすことなくキューブへのアクションができるようになります。

Ctrl キーでカメラロックしたときに、ゲーム内カーソルが出現し、カーソルの先にあるキューブが選択できるようにしてみます。

simplestar-tech.hatenablog.com

でーきたっと…

選択されたことの合図

前作では透明で少し大きめのキューブを明滅させていましたが、今回は対象となるキューブが「もこ」っとアニメーションしながら膨れる
という変化で対応してみようと思います。

カーソルが当たっているキューブをどのように特定するか

選択ロジックをみてみましょう

using CubeWalk;
using UnityEngine;

public class CubeSelector : MonoBehaviour
{
    #region SceneComponents
    [SerializeField] GameInputMode gameInputMode;
    [SerializeField] GameObject cursorCube;
    #endregion

    void Start()
    {
        this.gameInputMode.onChangeInputMode += OnChangeInputMode;
    }

    private void OnChangeInputMode(bool showCursor)
    {
        // カーソルが表示されているフラグを立てる
        this.existingCursor = showCursor;
        // カーソル表示中はカーソルキューブも表示する
        this.cursorCube.GetComponent<Renderer>().enabled = showCursor;
    }

    void Update()
    {
        if (!this.existingCursor)
            return;
        // スクリーン中のマウスポインタに向かってカメラからレイを作成
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        LocateCursorCube(ray);
    }

    private void LocateCursorCube(Ray ray)
    {
        // レイが CubeChunk にヒットした場合にキューブの位置を特定
        if (Physics.Raycast(ray, out RaycastHit cameraHit, IntaractRayLength, LayerMask.GetMask(LayerMask_Level)))
        {
            Vector3 cubePosition = cameraHit.point - (cameraHit.normal * ChunkConst.CubeSide * 0.5f);

            Vector3 roundSource = cubePosition / ChunkConst.CubeSide;
            Vector3Int gridSource = new Vector3Int(Mathf.RoundToInt(roundSource.x), Mathf.RoundToInt(roundSource.y), Mathf.RoundToInt(roundSource.z));
            Vector3 newCubePositon = new Vector3(gridSource.x, gridSource.y, gridSource.z) * ChunkConst.CubeSide;

            // 現在配置しているカーソルキューブの位置からずれたか確認
            if ((this.currentCubePosition - newCubePositon).magnitude >= ChunkConst.CubeSide)
            {
                this.currentCubePosition = newCubePositon;

                // キューブデータの取得

                // キューブを再配置
                this.cursorCube.transform.position = newCubePositon;
            }
        }
    }

    Vector3 currentCubePosition = Vector3.zero;
    bool existingCursor = false;
    readonly float IntaractRayLength = 20;
    readonly string LayerMask_Level = "CubeChunk";
}

なるほど、法線とヒットした位置について丸めて、キューブの int 位置というものを特定しているようですね


カーソルが当たっているキューブに少し大きめのキューブを描画するテストを行ってみましょう。

チャンクの Layer を変更すると キャラクターが接地しない不具合を見つける
例の TPS アセットのキャラクターコントローラーの Layers プロパティを確認すると Default の Layer を利用することになっています。
グラウンドについては CubeChunk のレイヤーも見るように設定を加えました。(VRMにコントローラを割り当てるところ)

具体的にはここかな?

            var vController = vrmRoot.AddComponent<vThirdPersonController>();
            vController.groundLayer.value |= LayerMask.GetMask("CubeChunk");

正解

新しいキューブの選択デモ

上記の実装を走らせると次の通り
youtu.be

まとめ

Unity クライアントサイドで、キューブの選択と破壊、設置を行える仕組みを入れていきたいとして
今回はキューブの選択を UI どうしようか想像しながら、一つの案を具現化してみました。

いずれキューブの情報を参照
キューブの破壊
キューブの配置
とステップを踏んで、オンラインデータを更新、参照する仕組みにつなげていこうおと思います。

今回はここまで