Unityを用いた2Dアクションゲームの作成

# Unityを用いた2Dアクションゲームの作成

## 目次
* はじめに
* 開発環境
* Unityのダウンロード
* ゲーム作成に使用するアセットの準備
* ステージの作成
* プレイヤーの動作
* プレイヤーのアニメーション
* プレイヤーとカメラの動きを合わせる設定
* ゲームクリアとゲームオーバーの表示
* おわりに
* 参考文献

## はじめに
この記事は、SLP KBIT AdventCalender2022の17日目の記事になります。今回は、Unityを用いて簡単な2Dアクションゲームを制作していきます。

## 開発環境
今回の開発環境は以下のようになっています。
* Windows11
* Unity　2021.3.11f1
  
## Unityのインストール・準備

まずは、Unityの[公式ホームページ](https://unity3d.com/jp/get-unity/download)からUnityをダウンロードします。次に、新しいプロジェクトを始めます。今回は、2Dのゲームを制作するため、新しいプロジェクトの「2D」を選択します。そして、新たに作成したプロジェクトを編集していきます。

## アセットの準備

アセットは基本的に[アセットストア](https://assetstore.unity.com/)から入手することが可能です。今回使用したステージのアセットは[こちら](https://assetstore.unity.com/packages/2d/environments/free-platform-game-assets-85838)です。次に、アセットストアから入手したアセットを実際にプロジェクトの中で実行して行きます。実行したいアセットをダウンロードした後に、インポートすることで、プロジェクトの中で開くことができる様になります。

## ステージの作成
アセットの準備が終わったら、ステージの作成に移っていきます。今回のステージは、「Tile Palette」を用いて作成していきます。「Tile Palette」にアセットのタイルを並べてステージを作成していきます。タイルは、Asset＞BayatGames＞Free Platform Game Assets＞2.5D Tiles＞Spring＞png＞128×128にあるものを使用し、「Tile Palette」に追加していきます。
次に、作成したステージの後ろに背景を設置していきます。背景は、Assets＞BayatGames＞Free Platform Game Assets＞Backgrounds＞Background＞png＞1920×1080＞Backgroundにあるものを使用していきます。
また、Assets＞BayatGames＞Free Platform Game Assets＞Environments＞Environments＞png＞All＞1×に草木のオブジェクトがあるため、ステージに設置するとよりゲームとしての見栄えが良くなります。
最後に、敵のキャラクターを設置する場合は、Assets＞BayatGames＞Free Platform Game Assets＞Enemies＞Enemies＞png＞128×128＞Maceにあるものを使用します。ステージに使用する背景や草木などは、プレハブ化しておくことで複数個利用する時に楽になります。

## プレイヤーの動作
次は、使用するプレイヤーの動作を作成していきます。
まず、使用するプレイヤーは、Assets＞BayatGames＞Free Platform Game Assets＞Character＞Character Animation＞Idle＞1×にあるものを使用していきます。このプレイヤーは複数の画像が並んだ状態であるため、Sprite Editorのスライス機能を用いて画像のスライスを行います。また、プレイヤーのタグを「Player」としておきます。
プレイヤーをステージに設置した後、プレイヤーを動かすためのスクリプトを書いていきます。今回のスクリプトは、C#を用いて書いていきます。スクリプト名は、「PlayerController」として書いていきます。スクリプトの内容は以下に示しておきます。
```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(CapsuleCollider2D))]
[RequireComponent(typeof(Animator))]

public class PlayerController : MonoBehaviour
{
    [SerializeField] private Rigidbody2D rb;
    [SerializeField] private Animator animator;
    [SerializeField] private int moveSpeed;
    [SerializeField] private int jumpForce;
    
    private bool isMoving = false;
    private bool isJumping = false;
    private bool isFalling = false;
    void Update()
    {
        float horizontal = Input.GetAxis("Horizontal");
        isMoving = horizontal != 0;
        isFalling = rb.velocity.y < -0.5f;

        if (isMoving)
        {
            Vector3 scale = gameObject.transform.localScale;
            if (horizontal < 0 && scale.x > 0 || horizontal > 0 && scale.x < 0)
            {
                scale.x *= -1;
            }
            gameObject.transform.localScale = scale;
        }

        if (Input.GetKeyDown(KeyCode.Space) && !isJumping && !isFalling)
        {
            Jump();
        }
        
        rb.velocity = new Vector2(horizontal * moveSpeed, rb.velocity.y);
        animator.SetBool("IsMoving", isMoving);
        animator.SetBool("IsJumping", isJumping);
        animator.SetBool("IsFalling", isFalling);
    }
    void Jump()
    {
        isJumping = true;
        rb.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse);
    }
    
    void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.CompareTag("Stage"))
        {
            isJumping = false;
        }
    }
}
```
スクリプトが書けたら、プレイヤーにドラッグ＆ドロップして取り付けます。このスクリプトを取り付けることでプレイヤーがキーボードの矢印キーで左右に移動でき、スペースキーでジャンプできるようになります。ジャンプは「Jump Force」の値を変更することで高さを変化できます。また、Rigidbody 2DとCapsule Collider2D、Player Controllerを取り付け、Player Material2Dで摩擦係数などを調整していきます。ステージにも当たり判定を付けるため、TilemapにTilemap Collider 2Dと「Stage」というタグを取り付けていきます。

## キャラクターのアニメーション
キャラクターが走っている時やジャンプしている時のアニメーションを設定していきます。アニメーションは、「Animation」を使用していきます。まず、「Animation」の画面でCreat New Clipを選択して、Assets＞BayatGames＞Free Platform Game Assets＞Character Animation＞Run＞1×の画像をドラッグ＆ドロップしていきます。ジャンプの画像も同じように新しいCreat New Clipにドラッグ＆ドロップしていきます。その後、「Animator」を使用していきます。「Animator」にBool型で「isMoving」「isJumping」「isFalling」を作ります。次に、以下の画像のように繋げていきます。![](https://i.imgur.com/DiC3iRe.png)

それぞれを繋げ終わったら、以下のように条件を設定していきます。
* 「Player Idle Animation」＞「Player Run Animation」は「IsMoving」を「true」
 
* 「Player Idle Animation」＞「Player Jump Animation」は「IsJumping」を「true」

* 「Player Idle Animation」＞「Player Fall Animation」は「IsFalling」を「true」

* 「Player Run Animation」＞「Player Idle Animation」は「IsMoving」を「false」

* 「Player Run Animation」＞「Player Jump Animation」は「IsJumping」を「true」

* 「Player Run Animation」＞「Player Fall Animation」は「IsFalling」を「true」

* 「Player Jump Animation」＞「Player Fall Animation」は「IsFalling」を「true」

* 「Player Fall Animation」＞「Player Idle Animation」は「IsMoving」と「IsJumping」と「IsFalling」を「false」

* 「Player Fall Animation」＞「Player Run Animation」は「IsMoving」を「true」、「IsJumping」と「IsFalling」を「false」

条件を設定出来たら、「Has Exit Time」のチェックを外して、「Fixed Duration」の値を「０」にしておきます。

## プレイヤーとカメラの動きを合わせる設定
プレイヤーとカメラの動きを合わせるために、カメラに好きリプとを取り付けていきます。プレイヤーに取り付けたスクリプトと同じように「FollwCamera」と名前を付けたC#スクリプトを作成します。スクリプトの内容は以下の通りです。
```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(Camera))]

public class FollowCamera : MonoBehaviour
{
    GameObject playerObj;
    PlayerController player;
    Transform playerTransform;
    void Start()
    {
        playerObj = GameObject.FindGameObjectWithTag("Player");
        player = playerObj.GetComponent<PlayerController>();
        playerTransform = playerObj.transform;
    }
    void LateUpdate()
    {
        MoveCamera();
    }
    void MoveCamera()
    {
        transform.position = new Vector3(playerTransform.position.x, transform.position.y, transform.position.z);
    }
}
```
このスクリプトをカメラに取り付けることで、横方向にだけカメラがプレイヤーと移動するようになります。

## ゲームクリアとゲームオーバーの表示
最後に、ゲームクリアの表示が出てくるようにしていきます。ゴール（ゲームクリアとなる場所）に新しく「Canvas」を作り、「Panel」と「Text」を子オブジェクトに取り付けていきます。次に、メッセージを表示させるスクリプトを作成します。作成したスクリプトは、「Canvas」に取り付けていきます。スクリプトの内容は以下の通りとなります。
```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FadeActiveUGUI : MonoBehaviour
{
    [Header("フェードスピード")] public float speed = 1.0f;
    [Header("上昇量")] public float moveDis = 10.0f;
    [Header("上昇時間")] public float moveTime = 1.0f;
    [Header("キャンバスグループ")] public CanvasGroup cg;
    [Header("プレイヤー判定")] public PlayerTriggerCheck trigger;

    private Vector3 defaltPos;
    private float timer = 0.0f;

    private void Start()
    {
        
        if (cg == null && trigger == null)
        {
            Debug.Log("インスペクターの設定が足りません");
            Destroy(this);
        }
        else
        {
            cg.alpha = 0.0f;
            defaltPos = cg.transform.position;
            cg.transform.position = defaltPos - Vector3.up * moveDis;
        }
    }

    private void Update()
    {
        
        if (trigger.isOn)
        {
        
            if (cg.transform.position.y < defaltPos.y || cg.alpha < 1.0f)
            {
                cg.alpha = timer / moveTime;
                cg.transform.position += Vector3.up * (moveDis / moveTime) * speed * Time.deltaTime;
                timer += speed * Time.deltaTime;
            }
        
            else
            {
                cg.alpha = 1.0f;
                cg.transform.position = defaltPos;
            }
        }
        
        else
        {
        
            if (cg.transform.position.y > defaltPos.y - moveDis || cg.alpha > 0.0f)
            {
                cg.alpha = timer / moveTime;
                cg.transform.position -= Vector3.up * (moveDis / moveTime) * speed * Time.deltaTime;
                timer -= speed * Time.deltaTime;
            }
        
            else
            {
                timer = 0.0f;
                cg.alpha = 0.0f;
                cg.transform.position = defaltPos - Vector3.up * moveDis;
            }
        }
    }
}
```
また、「Canvas」を敵と同じ位置に配置し、「Text」の内容を変えることで、敵に触れるとゲームオーバーの表示が出るようになります。

## おわりに
今回作成した2Dアクションゲームは、とても簡易的なものとなっています。また、説明も省略している部分があるため、詳しい説明や本格的なゲーム制作に興味がある方は、各自で調べてみてください。最後まで読んでくださり、ありがとうございました。

## 参考文献
[Unityで2Dアクションゲームの作り方を解説](https://raiungames.com/archives/255)
[Unity 2Dアクションの作り方](https://dkrevel.com/makegame-beginner/make-2d-action-message-continue-clear/)