Tilemapを使ってキャラクターを歩かせたりジャンプさせたりする

本記事はUnity Advent Calendar2022 その1 15日目の記事です。
前回の記事は @AzuQiita さんの[PhotonFusionを使って初めてゲーム開発をする上で知っておくと嬉しいTips集](URL"https://qiita.com/AzuQiita/items/c6ca7c372e53c8435753")でした。

Tilemapを使って横スクロールアクションゲームを作っていた際に個人的につまずいた部分を中心に書いていきます。

# 素材のサイズに合わせてSpriteのPixelsPerUnitを調整する。
デフォルトの100のままにしておくと配置した時にズレまくって凄いことになります。
例えば32×32で制作した物なら、PixelsPerUnitも32(2^nの数値)に直しましょう。
ちなみにドット絵の場合、FilterModeはPoint(nofilter)にしておきましょう。
![sprite.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2630371/ec050fbd-0549-ca80-7aee-e1b8444a3cc8.png)


# Hierarchy内を右クリックし、2DObject>Tilemap>RectangularでTilemapを作成する。
Gridが生成されて、Scene内で白い線が格子状に引かれたら成功です。
![tilemap.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2630371/8217157c-b5dc-1b80-fa48-052a9d2a5636.png)

# TilePalette内にあるActiveTilemapの項目を素材ごとに分ける。
当たり判定を付けるものと付けないものに分けるためです。
Grid内に用意されている子オブジェクト(Tilemap)をCtrl+Dで複製して2つにした後、Window>2D>TilePaletteで開くと、ActiveTilemapの項目が2つに増えています。
![tilepalette.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2630371/c54f29a4-4c2f-a20a-a4f7-9b44c4787c57.png)

# 素材を敷き、TilemapCollider2Dで当たり判定を設けた後、Rigidbody2DとCompositeCollider2Dをアタッチして当たり判定を滑らかにする。
SpriteをTilePaletteにドラッグさせると、Scene上で敷くための素材がTilePalette内に生成されます。
Grid内の子オブジェクトにTilemapCollider2D（AddComponent>Tilemap>TilemapCollider2D）をアタッチして当たり判定を設けると、敷いたときにブロック1つずつに当たり判定が設けられます。
![tilemapcollider.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2630371/80229b6a-975b-7fba-f011-324b80ef6a05.png)

このままだとキャラクターを歩かせたときにガクガクしたり隙間に挟まってあらぬ動きをしたりしてしまうので、当たり判定を合体させます。
CompositeCollider2D（AddComponent>Physics2D>CompositeCollider2D）をアタッチするために、Rigidbody2D（AddComponent>Physics2D>Rigidbody2D）をアタッチしてBodyTypeをStaticにします。そして、TilemapCollider2DのUsedByCompositeにチェックを入れます。
![composite.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2630371/1b928229-4b89-2ffa-2aee-a130234d5cb5.png)
![composite2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2630371/24819f95-b566-ec51-d380-c63710c99fbc.png)


# キャラクターのRigidbody2DのConstraintsのFreezeRotationをチェックしておく。
チェックしないとキャラが壁に当たった時に回転してしまいます。私は忘れて何回も回転させました。
Colliderをアタッチすることも忘れないように。

# キャラクターの移動をvelocityでコーディングする。
Translateだとキャラクターを壁に向かって歩かせたときに、壁に無理矢理にでもめり込もうとします。
velocityにすることで壁に接触したら止まるようにさせ、壁にめり込もうとすることを防ぎます。
また、その際にvoid Update()内ではなく、void FixedUpdate()内でコーディングします。

    private Rigidbody2D rbody2D;
    private float speed;

    void FixedUpdate()
    {
        rbody2D.velocity = new Vector2(
            Input.GetAxisRaw("Horizontal") * speed * Time.deltaTime,
            rbody2D.velocity.y);
    }

あとはspeedの値などを適当に調整してください。
ここまでが済めばキャラクターを歩かせることができます。

# 地面やジャンプ可能なオブジェクトに足元が触れている時にのみジャンプ出来るようにする。
単純に↑ボタンを押したら↑に移動する(ジャンプする)という感じでコードを組むと、キャラが無限にジャンプしだします。ジャンプが出来る条件下（例えば文字通り地に足がついているとか）を設定して、その時にのみジャンプできるようにします。

Physics2D.Linecastを用いて、それが地面のレイヤーやジャンプできるオブジェクトのレイヤーに触れている時にのみジャンプできるようにコードを組みます。ジャンプの速度はAddForceで操作します。

    private Rigidbody2D rbody2D;
    private float jumpForce;
    private bool groundedR;
    private bool groundedL;
    [SerializeField] LayerMask groundLayer;
    [SerializeField] LayerMask objectLayer;        

    void Update()
    {        
        groundedR = Physics2D.Linecast(transform.position - Vector3.right * 0.182f,
           transform.position - Vector3.right * 0.182f - transform.up / 20,
           groundLayer | objectLayer);
        groundedL = Physics2D.Linecast(transform.position - Vector3.left * 0.252f,
           transform.position - Vector3.left * 0.252f - transform.up / 20,
           groundLayer | objectLayer);

        if ((Input.GetKeyDown(KeyCode.UpArrow) || Input.GetKeyDown(KeyCode.W)) && (groundedR || groundedL))
        {
            this.rbody2D.AddForce(transform.up * jumpForce);
        }
    }


あとはjumpForceの値などを適当に調整してください。

ここまででジャンプすることもできるようになり、基本的なキャラ操作とステージ作成の下地が完成します。