うまげーむさん

ゲームの情報を主に投稿します。

【Unity】ソースコードコピペ可能!マリオ風2Dアクションゲームの作り方 #4 アニメーション / ダッシュ

はじめに

こんばんは。Unity2Dチュートリアル第4回です。

今回はアニメーションの追加と、ダッシュの実装を行います。

記事の長さ的に、ジャンプの着地判定(地面と接触しているときのみジャンプできるようにする設定)は次回に先延ばしします。

前回:

www.umagame.info

向き変更

まず、左右移動によるキャラクターの向きの変更を実装します。

早速コードを書きます。前回作ったPlayer.csのUpdate()の中に以下のコードを追加します。

そろそろコードが長くなってきたので、以降は変更部分のみを書いておくことにします。

//Player.cs

void Update()
{
    //Sprite Flip
    if (rb.velocity.x > 0)
    {
        GetComponent<SpriteRenderer>().flipX = false;
    }
    else if (rb.velocity.x < 0)
    {
        GetComponent<SpriteRenderer>().flipX = true;
    }
}

前回も使用したRigidbody2D.velocity.x(プレイヤーのX軸方向の速度)の正負によってプレイヤーの向きを変えています。

向きの変更は、GetComponent()でPlayerオブジェクトのSprite Rendererを取得し、そのflipXの真偽を変えることで出来ます。

これで向きが変わります。

勘の良い方ならお気づきかもしれませんが、ウマ娘は左右非対称なのでこの方法で向きを変えるには向いていません。

(本来はこっちからみて左側に赤いリボンの髪飾りがついていますが、左を向くと髪飾りが右に移動しています)

これは後ほど修正するとして、次はアニメーションを作ります。

アニメーション

テクスチャの追加・設定

Assets/Textures/Playerに新しく画像ファイルを追加します。

今回は移動(Run)・ジャンプ(Jump)・落下(Fall)・ダッシュ(Dash)用のスプライトを用意しました。

これは移動(Run)用のスプライトです。今回は32x32の画像を横に複数繋げたものをアニメーション用の画像として使っています。

前回と同様に、追加した画像ファイルの設定をしていきます。

「単位毎のピクセル数」を32に、「フィルターモード」を「ポイント」に、「圧縮」を「なし」にして、アニメーション用画像の場合は「スプライトモード」を「複数」にしておきます。

Sprite Editorで画像のスライスをするのも忘れないように。(やり方は第2回参照)

これで画像の準備は完了です。

アニメーションファイルの作成

アニメーションの設定ファイルを入れるためのフォルダを作ります。

作成→フォルダをクリック。

名前はAnimationsにします。

この中に、新しくアニメーションファイルを作ります。右クリック→作成→アニメーションをクリック。

アイドル状態(棒立ち)のアニメーションを作るので、名前は「Player_Idle」にします。

作成したファイルをダブルクリックすると、新しく「アニメーション」というウィンドウが表示されます。

ウィンドウ状態だと邪魔なので、ドラッグでゲームタブの横に置いておきます。

作成したアニメーションファイルをPlayerオブジェクトにドラッグアンドドロップします。

これでオブジェクトにアニメーションが適応され、「Player」という名前のアニメーターコントローラーが作成されます。(Player_Idleの左のやつ)

アニメーションを作っていきます。左画面(ヒエラルキー)でPlayerオブジェクトを選択した状態で、アニメーションタブのタイムラインにテクスチャをドラッグアンドドロップします。

今回Idle状態のテクスチャは一枚絵なので、これ以上設定することはありません。

同様にRun状態のアニメーションファイルも作ります。

Run状態はアニメーションのループが必要なので、アニメーションファイルのインスペクターから「時間をループ」にチェックを入れておきます。

Idleのやつと同様に、ドラッグアンドドロップでPlayerオブジェクトに適用しておきます。

複数枚のテクスチャを使う場合、このように分割された画像をすべて選択して(今回は8枚)ドラッグアンドドロップします。

その後、赤く囲ってある「サンプル」の数字を変えてアニメーション速度を変更します。今回は10にしました。(数字が小さいほど速度が遅くなります)

これでIdle状態とRun状態のアニメーションが完成しました。Jump・Fall状態は一旦置いておいて、次のステップへ移ります。

アニメーターの設定

オブジェクトのアニメーションを管理する「アニメーター」を開きます。メニュー→「ウィンドウ」→「アニメーション」→「アニメーター」をクリックします。

アニメーターが開いたら、「パラメーター」タブから新しくパラメーターを追加します。

パラメーター(Float, Int, Bool, Trigger型)を追加し、その値によってアニメーションがどう変わるかをアニメーター内で設定することによりアニメーションを実装します。(パラメーターの値はスクリプトから操作できます)

「+」ボタン→「Int」をクリックします。

名前は「state」にしておきます。

右画面から、アニメーション状態の間の遷移を作成します。Player_Idleを右クリック→「遷移を作成」をクリック。

その後Player_Runをクリックして遷移を作成します。

作成された矢印をクリックして、右の設定画面から「終了時間あり」のチェックを外し「遷移間隔」を0に設定します。

下にスクロールして「Conditions」の+ボタンをクリックし、state, Equals, 1の順に値を設定します。

これで「『state』が1になったとき、Player_Idle状態からPlayer_Run状態へ遷移する」という設定になりました。

今回はIdle状態を0、Run状態を1としているのでこのような形になります。

逆側の遷移も作ります。Player_Run→Player_Idleの矢印を作り、「終了時間あり」・「遷移間隔」の設定をして「Conditions」を上画像のようにします。

スクリプトの編集(Idle・Run間)

最後に、先程アニメーターで作った「state」というパラメーターをスクリプトで制御します。

//Player.cs
void Update()
    //Animation State
    if (rb.velocity.x != 0)
    {
        GetComponent<Animator>().SetInteger("state", 1);
    }
    else
    {
        GetComponent<Animator>().SetInteger("state", 0);
    }
}

Update()に上記のコードを追加します。

X軸方向の速度が0のときIdle状態(state=0)、そうでないときはRun状態(state=1)にするように設定しています。

これでIdle状態←→Run状態の遷移が行われます。

ジャンプ・落下アニメーション

次はジャンプ(Jump)・落下(Fall)状態のアニメーションを作っていきます。

Idle、Runと同様にアニメーションファイルを作り、Playerオブジェクトに適用しておきます。

今回はどちらもループしなくて良いので、アニメーションファイル自体の設定はデフォルトでOKです。

同様にアニメーションウィンドウでも設定を行います。

今回はFallのサンプル数を20にしておきました。Jump状態は一枚絵なので設定しなくて良いです。

Jump状態のstateを2、Fall状態のstateを3として、さっきと同じようにアニメーターで遷移を作成します。

量が増えるとめんどくさいですが、設定さえすればちゃんと動いてくれるので頑張りましょう。

stateを制御するコードも追加します。

//Player.cs
void Update()
{
    //上記で追加したコードを削除してこの一文を追加
    UpdateAnimationState();
}

private void UpdateAnimationState()
{
    //Run
    if (rb.velocity.x != 0)
    {
        GetComponent<Animator>().SetInteger("state", 1);
    }
    else
    {
        GetComponent<Animator>().SetInteger("state", 0);
    }

    //Jump / Fall
    if (rb.velocity.y > 0.1f)
    {
        GetComponent<Animator>().SetInteger("state", 2);
    }
    else if (rb.velocity.y < -0.1f)
    {
        GetComponent<Animator>().SetInteger("state", 3);
    }
}

Idle・Run間のアニメーション追加時に書き加えたコードを新しく作ったUpdateAnimationState()というメソッドに移し、さらにジャンプ・落下の判定をするコードも追加します。

ジャンプ・落下の判定は、Y軸方向の速度がある一定値(今回は0.1、-0.1)より大きいか小さいかを調べて行っています。

これを0より大きいか小さいかで判定すると、地面にいる状態でも常にジャンプor落下状態と判定されてしまいます。

おそらくY軸方向の速度については、一見Y座標が変わっていないように見えてもY軸速度が0ではないが0に限りなく近い値になっているのでしょうね。

これでジャンプしたときのアニメーションが実装できました。

ダッシュの実装

前回予告した通り、ダッシュを実装していきます。

まず、ダッシュのキー設定を入力マネージャーで行います。

デフォルトで作られていた「Fire1」という軸を「Dash」という名前に変えて、「left shift」をボタンとして設定します。

//Player.cs
public class Player : MonoBehaviour
{
    //2つの変数を追加
    public float DashSpeed = 6f;

    private bool isDashing = false;

    void Update()
    {
        //Player Movement
        if (!isDashing)
        {
            rb.velocity = new Vector2(Input.GetAxisRaw("Horizontal") * MoveSpeed, rb.velocity.y);
        }
        else
        {
            rb.velocity = new Vector2(Input.GetAxisRaw("Horizontal") * DashSpeed, rb.velocity.y);
        }

        if (Input.GetButton("Dash"))
        {
            isDashing = true;
        }
        else
        {
            isDashing = false;
        }

        UpdateAnimationState();
    }

    private void UpdateAnimationState()
    {
        //Run
        if (rb.velocity.x != 0)
        {
            if (!isDashing)
            {
                GetComponent<Animator>().SetInteger("state", 1);
            }
            else
            {
                GetComponent<Animator>().SetInteger("state", 4);
            }
        }
        else
        {
            GetComponent<Animator>().SetInteger("state", 0);
        }
    }
}

DashSpeed(ダッシュ時の速度)とisDashing(ダッシュしているかどうか)という2つの変数を追加し、

Update()にダッシュの判定・ダッシュ時に速度を変更するコードを、UpdateAnimationState()にダッシュ時にstateを4にするコードを追加します。

そして他のアニメーションと同様にDash状態のアニメーションファイル(Player_Dash)を作成しPlayerオブジェクトに適用、アニメーターで他の状態との間の遷移を作成します。

ここで、Jump→Idle、Jump→Run、Jump→Dashの遷移を削除しておきます。

これを削除しないと、ジャンプの最高点ではY軸方向の速度が0に近くなるため空中でIdle, Run, Dashのいずれかのアニメーションが一瞬だけ再生されてしまいます。

これでダッシュの実装は完了です。

こんな感じになりました。

左向きテクスチャの追加・実装

先述の通り、テクスチャが左右非対称の場合に生じる不具合の解決をします。

多くの場合は左右対称だと思うので、やり方はそこまで詳しく説明しません。

まずこのような左向きの画像を用意して、

Unityに取り込みます。(スプライトの設定もします)

アニメーターで、Player_IdleからPlayer_DashをCtrl+C→Ctrl+Vでコピペします。(遷移の設定がめんどくさいので)

これだとただ単にコピーされているだけなので、左向きテクスチャを適用したアニメーションファイルを用意して、ドラッグアンドドロップで設定します。

インスペクター上部から名前も変えておきます。

右向きと左向きの間の遷移を作成します。左側で新しくBool型のパラメーター(今回はflipという名前)を作成し、遷移の設定を行います。

stateが0~4のうちのどの状態かの条件と、左向きかどうか(flip)の条件を設定します。

先述の通り、Jump→Idle、Jump→Run、Jump→Dashの遷移は必要ないので追加しないようにしましょう。

右向き→左向きの遷移と、左向き→右向きの遷移すべてを追加したらOKです。めちゃくちゃ面倒くさいです。何かもっと良い方法はないのか。

最後に、この記事の最初で編集した向き変更の部分を書き換えます。

void Update()
{
    //Sprite Flip
    if (rb.velocity.x > 0)
    {
        GetComponent<Animator>().SetBool("flip", false);
    }
    else if (rb.velocity.x < 0)
    {
        GetComponent<Animator>().SetBool("flip", true);
    }
}

これで左を向いたときにflipがtrueに、右を向いたときにflipがfalseになります。

そんなこんなで、左向き状態のアニメーションも追加できました。

今回の進捗 / ソースコード

最終的にこんな感じになりました。

意外と記事が長くなってしまったので、空中でジャンプが出来てしまう不具合の修正は次回にやることにします。

Player.csのソースコードはこちらです。(これ、スクリプトが多くなってくると直接貼るのきついので次回ぐらいからGitHubでの公開にしたほうが良いですね...)

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    public float MoveSpeed = 3f;
    public float DashSpeed = 6f;
    public float JumpForce = 15f;

    private Rigidbody2D rb;

    private bool isDashing = false;

    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        //Player Movement
        if (!isDashing)
        {
            rb.velocity = new Vector2(Input.GetAxisRaw("Horizontal") * MoveSpeed, rb.velocity.y);
        }
        else
        {
            rb.velocity = new Vector2(Input.GetAxisRaw("Horizontal") * DashSpeed, rb.velocity.y);
        }

        if (Input.GetButtonDown("Jump"))
        {
            rb.velocity = new Vector2(rb.velocity.x, JumpForce);
        }

        if (Input.GetButton("Dash"))
        {
            isDashing = true;
        }
        else
        {
            isDashing = false;
        }

        //Sprite Flip
        if(rb.velocity.x > 0)
        {
            GetComponent<Animator>().SetBool("flip", false);
        }
        else if (rb.velocity.x < 0)
        {
            GetComponent<Animator>().SetBool("flip", true);
        }

        UpdateAnimationState();
    }

    private void UpdateAnimationState()
    {
        //Run
        if (rb.velocity.x != 0)
        {
            if (!isDashing)
            {
                GetComponent<Animator>().SetInteger("state", 1);
            }
            else
            {
                GetComponent<Animator>().SetInteger("state", 4);
            }
        }
        else
        {
            GetComponent<Animator>().SetInteger("state", 0);
        }

        //Jump / Fall
        if(rb.velocity.y > 0.1f)
        {
            GetComponent<Animator>().SetInteger("state", 2);
        }
        else if(rb.velocity.y < -0.1f)
        {
            GetComponent<Animator>().SetInteger("state", 3);
        }
    }
}

さいごに

今回はここまでとなります。

次回は

  • 着地判定(ジャンプの修正)

  • カメラ移動(+マップ拡張)

  • デス判定(マップ外へ落ちたときの判定)

です。

www.umagame.info