5 Tips for Better Platformers in Unity (日本語訳) | FOOT STEPS

こちらの記事の続き.

xrdnk.hateblo.jp

www.youtube.com

今回はFOOT STEPSです.

FOOT STEPS

Particle Systemを利用しております.
こちらの記事がわかりやすいと思う.

qiita.com

FOOT STEPS とありますが,エフェクトは2種類あって,
足跡エフェクトと着地エフェクトがあります.

FootSteps Effect

実装自体は単純で,以下の考え方でコーディングすればよいです.
地面にいる状態 かつ 水平方向に動いている状態の時,パーティクルを発生させる.
地面にいる状態 かつ 水平方向に動いている状態 の時,単位時間あたりの放出量を発生させる.
上記の条件以外の場合は,放出量を0にすればよいです.

コードは最低限の部分のみ記述しております.

    [SerializeField, Tooltip("足跡エフェクト")]
    private ParticleSystem footSteps;
    /// <summary>
    // パーティクルエミッションモジュール
    /// </summary>
    private ParticleSystem.EmissionModule footEmission;

    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        footEmission = footSteps.emission;
    }

    private void Update()
    {
        float x = Input.GetAxis("Horizontal");

   // COYOTE TIME
        if (isGround())
        {
            // 地面にいる時,滞空カウンターを滞空時間の初期値に設定する
            hangCounter = hangTime;
        }
        else
        {
            // 地面から離れている時,滞空カウンターを減らし続ける
            hangCounter -= Time.deltaTime;
        }

        // JUMP BUFFER
        if (Input.GetKeyDown("space"))
        {
            jumpBufferCounter = jumpBufferLength;
        }
        else
        {
            jumpBufferCounter -= Time.deltaTime;
        }

        // 滞空カウンターが0以上の場合,ジャンプ
        if (hangCounter > 0)
        {
            // ジャンプバッファカウンタが0以上の場合
            if (jumpBufferCounter >= 0)
            {
                Jump();
                // 2段ジャンプ防止のためにカウンタをリセット
                jumpBufferCounter = 0;
            }
        }

        // SMALL JUMPS
        // スペースキーを離したとき かつ 上向きに移動している時 
        if (Input.GetKeyUp("space") && rb.velocity.y > 0)
        {
            // y方向の速度を現在の速度の半分する
            rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * .5f);
        }

        // FOOT STEPS EFFECT
        // 水平方向に動いている時 かつ 地面にいる時
        if (x != 0 && isGround())
        {
            footEmission.rateOverTime = 35f;
        }
        else
        {
            // それ以外の時
            footEmission.rateOverTime = 0f;
        }
    }

    bool isGround()
    {
        // 視点と終点を作成
        Vector3 leftStartPoint = transform.position - Vector3.right * 0.2f;
        Vector3 rightStartPoint = transform.position + Vector3.right * 0.2f;
        Vector3 endPoint = transform.position - Vector3.up * 0.1f;

        // Linecast
        return Physics2D.Linecast(leftStartPoint, endPoint, blockLayer) || Physics2D.Linecast(rightStartPoint, endPoint, blockLayer);
    }

これによって歩いている時に足元にエフェクトが出るようになりました.
f:id:xrdnk:20200222144953g:plain

Impact Effect

上のFoot Effectsに付け加える形でまとめます.Updateの特性を使います.

    [SerializeField, Tooltip("足跡エフェクト")]
    private ParticleSystem footSteps;
    /// <summary>
    // パーティクルエミッションモジュール
    /// </summary>
    private ParticleSystem.EmissionModule footEmission;

    [SerializeField, Tooltip("着地エフェクト")]
    private ParticleSystem impactSteps;
    /// <summary>
    /// 1フレーム前に地面にいたのか
    /// </summary>
    private bool wasOnGround;

    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        footEmission = footSteps.emission;
    }

    private void Update()
    {
        float x = Input.GetAxis("Horizontal");

   // COYOTE TIME
        if (isGround())
        {
            // 地面にいる時,滞空カウンターを滞空時間の初期値に設定する
            hangCounter = hangTime;
        }
        else
        {
            // 地面から離れている時,滞空カウンターを減らし続ける
            hangCounter -= Time.deltaTime;
        }

        // JUMP BUFFER
        if (Input.GetKeyDown("space"))
        {
            jumpBufferCounter = jumpBufferLength;
        }
        else
        {
            jumpBufferCounter -= Time.deltaTime;
        }

        // 滞空カウンターが0以上の場合,ジャンプ
        if (hangCounter > 0)
        {
            // ジャンプバッファカウンタが0以上の場合
            if (jumpBufferCounter >= 0)
            {
                Jump();
                // 2段ジャンプ防止のためにカウンタをリセット
                jumpBufferCounter = 0;
            }
        }

        // SMALL JUMPS
        // スペースキーを離したとき かつ 上向きに移動している時 
        if (Input.GetKeyUp("space") && rb.velocity.y > 0)
        {
            // y方向の速度を現在の速度の半分する
            rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * .5f);
        }

        // FOOT STEPS EFFECT
        // 水平方向に動いている時 かつ 地面にいる時
        if (x != 0 && isGround())
        {
            footEmission.rateOverTime = 35f;
        }
        else
        {
            // それ以外の時
            footEmission.rateOverTime = 0f;
        }

        // IMPACT STEP EFFECT
        // 1フレーム前が地面におらず,現フレームが地面にいる場合
        if (!wasOnGround && isGround())
        {
            impactSteps.gameObject.SetActive(true);
            impactSteps.Stop();
            impactSteps.transform.position = footSteps.transform.position;
            impactSteps.Play();
        }
        // フレーム終了前にプレイヤーが地面にいるかどうか判断する
        wasOnGround = isGround();
    }

    bool isGround()
    {
        // 視点と終点を作成
        Vector3 leftStartPoint = transform.position - Vector3.right * 0.2f;
        Vector3 rightStartPoint = transform.position + Vector3.right * 0.2f;
        Vector3 endPoint = transform.position - Vector3.up * 0.1f;

        // Linecast
        return Physics2D.Linecast(leftStartPoint, endPoint, blockLayer) || Physics2D.Linecast(rightStartPoint, endPoint, blockLayer);
    }

Updateは毎フレームごとに呼ばれます.フレームの終了前にプレイヤーが地面にいるかどうか判断します.
次フレームにて,地面にいるか判定する前に,もし前フレームでプレイヤーが地面にいなかった かつ
現フレームでは地面にいる 場合に着地エフェクトを生成すればよいです.

f:id:xrdnk:20200222145636g:plain

着地時にエフェクトが出るようになりました.
Particleのプロパティをうまく調整すれば好きなエフェクトが出せると思います.

参考記事

【Unity】ダッシュで移動した際の土煙っぽい表現 - テラシュールブログ

終わりに

来週の2/24(月)~3/1(日)にUnity1Weekが開催されます.

unityroom.com

今迄完遂させたことがなかったので,今度こそ完遂させるという意思で頑張ろうと思います.
とりあえず来週から1週間弱ブログのネタはUnity1Weekの進捗状況について報告しようかな.