simplestarの技術ブログ

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

Unity:キューブの配置

前書き

破壊についてはこちら
simplestar-tech.hatenablog.com

今回は配置を考えていきます。

作る前から見えている絵は…

右クリック開始で 配置先が見える
アローキーで配置先が回転
右クリック解除で配置

具体的な実装を進めてみましょう。

前回の配置は?

以下のようなコードを書いて、回転配置にも対処していた模様

    internal static void RotateCube(Direction direction, ItemEntity itemEntity, Transform cameraTransform)
    {
        switch (itemEntity.cubeSt.rot)
        {
            case CubeRotation.Top000:
                switch (direction)
                {
                    case Direction.X:
                        itemEntity.cubeSt.rot = CubeRotation.Top270;
                        break;
                    case Direction.Y:
                        switch (GetTransformDirection(cameraTransform))
                        {
                            case Direction.X:
                                itemEntity.cubeSt.rot = CubeRotation.Right090;
                                break;
                            case Direction.Z:
                                itemEntity.cubeSt.rot = CubeRotation.Forward180;
                                break;
                            case Direction._X:
                                itemEntity.cubeSt.rot = CubeRotation.Left270;
                                break;
                            case Direction._Z:
                                itemEntity.cubeSt.rot = CubeRotation.Back000;
                                break;
                            default:
                                break;
                        }
                        break;

マウスの右クリックイベントを発行

すでにマウス左クリックの仕組みがあったので、同様にして対処

    void Start()
    {
        // キューブ選択処理のイベントを購読
        this.cubeSelector.onLocateStarted += this.OnLocateStarted;
        this.cubeSelector.onLocateCanceled += this.OnLocateCanceled;
        this.cubeSelector.onChangeSelected += this.OnChangeSelected;
    }

右クリック完了時にキューブを配置

適当な素材の適当な回転で配置する
破壊と同じ仕組みで、空気ブロックにするところ、何かのブロックにするという対応でいけるだろうか?

空気ブロックにする実装がこちら

        // 対象キューブを空気に
        var chunkInt3 = this.chunkCollider.gameObject.GetComponent<ChunkMeshInfo>().chunkInt3;
        this.cubeDataWorld.SetCubeData(this.cubeCenter, chunkInt3, CubeCategoryType.Basic, CubeRotationType.Top000, SideType.Air, SideType.Air);

かなり簡単にいけそう

        this.cubeCenter = cubeObject.transform.position + raycastHit.normal;
        // 対象キューブ表面を適当なブロックに
        var chunkInt3 = this.chunkCollider.gameObject.GetComponent<ChunkMeshInfo>().chunkInt3;
        this.cubeDataWorld.SetCubeData(this.cubeCenter, chunkInt3, CubeCategoryType.Basic, CubeRotationType.Top000, SideType.Bubbles, SideType.Bubbles);

いけた

テストしていて、さっそく問題に…
キューブを置く場所がチャンクをまたぐと、チャンクの特定のための情報がずれる
正しい情報はキューブの位置から特定できなければならない

次の実装で期待通り動いた

        var x = this.cubeCenter.x / (ChunkConst.ChunkSizeX * ChunkConst.CubeSide);
        if (0.5f > Mathf.Abs(this.cubeCenter.x))
        {
            x = 0;
        }
        var y = this.cubeCenter.y / (ChunkConst.ChunkSizeY * ChunkConst.CubeSide);
        if (0.5f > Mathf.Abs(this.cubeCenter.y))
        {
            y = 0;
        }
        var z = this.cubeCenter.z / (ChunkConst.ChunkSizeZ * ChunkConst.CubeSide);
        if (0.5f > Mathf.Abs(this.cubeCenter.z))
        {
            z = 0;
        }
        var chunkInt3 = new Vector3Int(Mathf.FloorToInt(x), Mathf.FloorToInt(y), Mathf.FloorToInt(z));

        Vector3Int CubePositionToCubeInt3(Vector3 cubePosition)
        {
            int x = Mathf.FloorToInt(cubePosition.x / ChunkConst.CubeSide) % ChunkConst.ChunkSizeX;
            x = 0 > x ? ChunkConst.ChunkSizeX + x : x;
            int y = Mathf.FloorToInt(cubePosition.y / ChunkConst.CubeSide) % ChunkConst.ChunkSizeY;
            y = 0 > y ? ChunkConst.ChunkSizeY + y : y;
            int z = Mathf.FloorToInt(cubePosition.z / ChunkConst.CubeSide) % ChunkConst.ChunkSizeZ;
            z = 0 > z ? ChunkConst.ChunkSizeZ + z : z;

            return new Vector3Int(x, y, z);
        }

実装ヒントは過去のキューブ情報の特定の部分から

遠くから眺めたい

デバッグ機能でいいので、ホイール操作でカメラと被写体までの距離を伸ばせませんかね
できた

    void Start()
    {
        this.dollyAction.performed +=
           ctx =>
           {
               var orbit = this.cinemachineFreeLook.m_Orbits[1];
               orbit.m_Radius += ctx.ReadValue<float>() * this.scale;
               this.cinemachineFreeLook.m_Orbits[1] = orbit;
           };
    }

右クリック開始時に半透明のキューブの表示

もこもこの応用で素早く用意できそうな気がしてるけど

半透明ってところに困ってる
マテリアルの設定箇所を探す?
新しく ShaderGraph を作って対処しました。
いったんは小ゴールクリアですね。
www.youtube.com

アローキーで配置先が回転

まずはアローキーイベントを拾わねば

f:id:simplestar_tech:20191027150906p:plain
新しい InputSystem で拾えました

各方向へのアクションとしてDirection を定義

    public enum Direction
    {
        X = 0,
        Y,
        Z,
        _X,
        _Y,
        _Z,
        Max
    }

アローキーアクションでこのように回転を与えます。

    void Awake()
    {
        this.locatorAction.performed +=
           ctx =>
           {
               if (this.locatingFlag)
               {
                   var direction = Direction.Max;
                   var directionFloat2 = ctx.ReadValue<Vector2>();
                   if (0.5f < directionFloat2.x)
                   {
                       direction = Direction.X;
                   }
                   else if (-0.5f > directionFloat2.x)
                   {
                       direction = Direction._X;
                   }
                   if (0.5f < directionFloat2.y)
                   {
                       direction = Direction.Y;
                   }
                   else if (-0.5f > directionFloat2.y)
                   {
                       direction = Direction._Y;
                   }
                   this.RotatePrePutCube(direction, Camera.main.transform);
                   this.PreLocateCube();
               }
           };
    }

そして、冒頭の過去の実装につなげれば OK。
予想通りいけました。

配置をキャンセルしたい

あとはちょっとした不具合を直します。
キューブの破壊と違って、配置場所がずれたらキャンセル扱いにしたい
となると

CubeSelector 側で新しいイベントかな

            if (this.currentGridLocatePosition != gridLocatePosition)
            {
                this.currentGridLocatePosition = gridLocatePosition;
                this.onChangeLocatePosition?.Invoke();
            }

このような実装を加えて解決した