【Unity入門】2Dアクションを作ろう【プログラム整理】

この記事は本のように順を追って解説しています。この記事は途中のページになります。
この記事を見ていて、現在の状況がわからない場合や忘れてしまった事などが出てきたら↓のリンクから目次ページへ飛べますので立ち戻って見てください。

<今まで書いたスクリプト>

今まで様々なスクリプトを書いてきました。

クリックすると展開します。
 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 public class ScreenSetting : MonoBehaviour
 {
     // Start is called before the first frame update
     void Start()
     {
         Screen.SetResolution(960, 540, false);
     }
 
     // Update is called once per frame
     void Update()
     {
 
     }
 }
 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 public class ScreenSetting : MonoBehaviour
 {
     private bool firstPush = false; 

     // Start is called before the first frame update
     void Start()
     {

     }
 
     // Update is called once per frame
     void Update()
     {
 
     }
  
     public void PressStart()
     {
         Debug.Log("Press Start!");
         if (!firstPush)
         {
             Debug.Log("Go Next Scene!");
             //ここに次のシーンへいく命令を書く
             
             //
             firstPush = true;
         }
     } 
 }
using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 public class player : MonoBehaviour
 {
     //インスペクターで設定する
     public float speed;           //移動速度
     public float gravity;         //重力
     public float jumpSpeed;       //ジャンプ速度
     public float jumpHeight;      //ジャンプする高さ
     public AnimationCurve dashCurve; //ダッシュの速さ表現
     public AnimationCurve jumpCurve; //ジャンプの速さ表現
 
     //プライベート変数
     private Animator anim = null;
     private Rigidbody2D rb = null;
     private string groundTag = "Ground";
     private bool isGroundEnter, isGroundStay, isGroundExit;
     private bool isGround = false;
     private bool isJump = false;
     private float jumpPos = 0.0f;
     private float dashTime, jumpTime;
     private float beforeKey;  //new!
     
     void Start()
     {
         //コンポーネントのインスタンスを捕まえる
         anim = GetComponent<Animator>();
         rb = GetComponent<Rigidbody2D>();
     }
 
     void FixedUpdate()
     {
         //接地しているかどうかの判定をとる
         if(isGroundEnter || isGroundStay)
         {
             isGround = true;
         }
         else if(isGroundExit)
         {
             isGround = false;
         }
 
         //キー入力されたら行動する
         float horizontalKey = Input.GetAxis("Horizontal");
         float verticalKey = Input.GetAxis("Vertical");
         float xSpeed = 0.0f;
         float ySpeed = -gravity;
         if(isGround)
         {
             if (verticalKey > 0)
             {
                 ySpeed = jumpSpeed;
                 jumpPos = transform.position.y;        //ジャンプした位置を記録する
                 isJump = true;
             }
             else
             {
                 isJump = false;
  
             }
             jumpTime = 0.0f;
         }
         else if(isJump)
         {
             //上ボタンを押されている。かつ、現在の高さがジャンプした位置から自分の決めた位置より下ならジャンプを継続する
             if (verticalKey > 0 && jumpPos + jumpHeight > transform.position.y)
             {
                 ySpeed = jumpSpeed;
                 jumpTime += Time.deltaTime;
             }
             else
             {
                 isJump = false;
                 jumpTime = 0.0f;
             }
         }
         
         if (horizontalKey > 0)
         {
             transform.localScale = new Vector3(1, 1, 1);
             anim.SetBool("run", true);
             xSpeed = speed;
             dashTime += Time.deltaTime;
         }
         else if (horizontalKey < 0)
         {
             transform.localScale = new Vector3(-1, 1, 1);
             anim.SetBool("run", true);
             xSpeed = -speed;
             dashTime += Time.deltaTime;
         }
         else
         {
             anim.SetBool("run", false);
             xSpeed = 0.0f;
             dashTime = 0.0f;
         }

//New !
         if (horizontalKey > 0 && beforeKey < 0)
         {
             dashTime = 0.0f;
         }
         else if (horizontalKey < 0 && beforeKey > 0)
         {
             dashTime = 0.0f;
         }

         xSpeed *= dashCurve.Evaluate(dashTime);
         if (isJump)
         {
             ySpeed *= jumpCurve.Evaluate(jumpTime);
         }

         rb.velocity = new Vector2(xSpeed, ySpeed);
         anim.SetBool("jump", isJump);
         anim.SetBool("ground", isGround);
 
         //接地しているか判断する為のフラグをリセットする
         isGroundEnter = false;
         isGroundStay = false;
         isGroundExit = false;
         beforeKey = horizontalKey;  //new!
     }
     private void OnTriggerEnter2D(Collider2D collision)
     {
         if (collision.tag == groundTag)
         {
             isGroundEnter = true;
         }
     }
 
     private void OnTriggerStay2D(Collider2D collision)
     {
         if (collision.tag == groundTag)
         {
             isGroundStay = true;
         }
     }
     
     private void OnTriggerExit2D(Collider2D collision)
     {
         if (collision.tag == groundTag)
         {
             isGroundExit = true;
         }
     }
 }

<合間合間に整理しよう>

さて、色々なプログラムや機能を多々追加してきて、プログラムが煩雑になってきたので整理整頓を始めましょう。

いや、最初からやれよって思うかもしれませんが、作っているときはグチャグチャになりやすいのですよ。特にわからないことを調べて切って貼って作って壊してを繰り返していたりすると。

正直、最初から完璧を目指したプログラムを書こうとするより、定期的に整理整頓できるようになっておいたほうがいいかなと筆者は思うわけです。

と、いうわけでこの中途半端な時に整理整頓をします。

完成してから整理整頓しようとするとバグったりしますからね。一気に整理整頓をしようとすると手違いを起こしやすいです。

合間、合間に整理するのが大事なのです。それが今回の記事のキモでもあります。

というわけでやっていきましょう。

<インスペクターを整理しよう>

まずは、インスペクターにパラメータが増えすぎてわからなくなってしまう前に注釈を入れたいと思います。

現在インスペクターは↓のようになっていると思います。

player inspector

今は英語からなんとなく何に使うパラメータなのかかろうじてわかりますが、時間が経ったりパラメータが増えてくると何がなんなのかわからなくなってしまいます。

now script variable

スクリプトにコメントを書いてもインスペクターからみることができないのでいちいちスクリプトを開くのは面倒です。

ですので、↑に書いてあるコメントをインスペクターに表示させてみましょう。

変数の前に[Header(“コメント”)]とします。

     //インスペクターで設定する
    [Header("移動速度")]public float speed;
    [Header("重力")]public float gravity;
    [Header("ジャンプ速度")]public float jumpSpeed;
    [Header("ジャンプする高さ")]public float jumpHeight;
    [Header("ダッシュの速さ表現")]public AnimationCurve dashCurve;
    [Header("ジャンプの速さ表現")]public AnimationCurve jumpCurve;

すると

add header attribute

このようにインスペクターでパラメータにつけたコメントを見れるようになりました。

こうすることで、未来の自分が見ても間違うことはなさそうです。

1ヶ月後にはなんの変数だったのか忘れてしまってコードを辿るという無駄な時間を過ごした方は自分だけじゃないはず・・・!

また、チームでゲーム開発をしようとしている方は結構重要なので覚えておいてくださいね。

特に人によって解釈が変わることって多いですから。

↑の場合だと、人によっては「Gravity」は下方向なのでマイナスの値を入れてしまうなど。コメントに「正の値」と入れておいてもいいかもしれません。あとDash Curveとか何の値やねんって感じですしね。

この[○○]変数とする書き方はAttributeと呼ばれる手法で変数や関数に属性を付与する書き方です。○○の中にはいろんな命令を書けます。C#の機能ですが、Unityで使用する場合、Unity側に働きかける命令を書くことができます。

<関数化しよう>

さて、今FixedUpdateの中にごちゃごちゃ書いてあって見にくいので整頓しましょう。特に、色々な処理を後から足していく時、ごちゃごちゃしているせいで変な所に書いてしまい、バグを生み出してしまう可能性があります。

クリックすると展開します
     void FixedUpdate()
    {
        //接地しているかどうかの判定をとる
        if(isGroundEnter || isGroundStay)
        {
            isGround = true;
        }
        else if(isGroundExit)
        {
            isGround = false;
        }

        //キー入力されたら行動する
        float horizontalKey = Input.GetAxis("Horizontal");
        float verticalKey = Input.GetAxis("Vertical");
        float xSpeed = 0.0f;
        float ySpeed = -gravity;
        if(isGround)
        {
            if (verticalKey > 0)
            {
                ySpeed = jumpSpeed;
                jumpPos = transform.position.y;        //ジャンプした位置を記録する
                isJump = true;
                jumpTime += Time.deltaTime;
            }
            else
            {
                isJump = false;
                jumpTime = 0.0f;
            }
        }
     else if(isJump)
        {
            //上ボタンを押されている。かつ、現在の高さがジャンプした位置から自分の決めた位置より下ならジャンプを継続する
            if (verticalKey > 0 && jumpPos + jumpHeight > transform.position.y)
            {
                ySpeed = jumpSpeed;
                jumpTime += Time.deltaTime;
            }
            else
            {
                isJump = false;
                jumpTime = 0.0f;
            }
        }
        
        if (horizontalKey > 0)
        {
            transform.localScale = new Vector3(1, 1, 1);
            anim.SetBool("run", true);
            xSpeed = speed;
            dashTime += Time.deltaTime;
        }
        else if (horizontalKey < 0)
        {
            transform.localScale = new Vector3(-1, 1, 1);
            anim.SetBool("run", true);
            xSpeed = -speed;
            dashTime += Time.deltaTime;
        }
        else
        {
            anim.SetBool("run", false);
            xSpeed = 0.0f;
            dashTime = 0.0f;
        }
        if (horizontalKey > 0 && beforeKey < 0)
        {
            dashTime = 0.0f;
        }
        else if (horizontalKey < 0 && beforeKey > 0)
        {
            dashTime = 0.0f;
        }
        xSpeed *= dashCurve.Evaluate(dashTime);
        if (isJump)
        {
            ySpeed *= jumpCurve.Evaluate(jumpTime);
        }
        rb.velocity = new Vector2(xSpeed, ySpeed);
        anim.SetBool("jump", isJump);
        anim.SetBool("ground", isGround);

        //接地しているか判断する為のフラグをリセットする
        isGroundEnter = false;
        isGroundStay = false;
        isGroundExit = false;
        beforeKey = horizontalKey;
    }

んー。一つの関数の中にいろんな機能を書きすぎですね。

まとめられるものはまとめてしまいましょう。各種機能を関数にしてしまいます。

まずこれ、

        //接地しているかどうかの判定をとる
        if(isGroundEnter || isGroundStay)
        {
            isGround = true;
        }
        else if(isGroundExit)
        {
            isGround = false;
        }

        //接地しているか判断する為のフラグをリセットする
        isGroundEnter = false;
        isGroundStay = false;
        isGroundExit = false;

接地判定をしているところを関数化したいと思います。

     /// <summary>
    /// 接地しているかどうかの判定をとる
    /// </summary>
    /// <returns><c>true</c>, 接地している, <c>false</c> 接地していない</returns>
    private void GroundCheck()
    {
        if(isGroundEnter || isGroundStay)
        {
            isGround = true;
        }
        else if(isGroundExit)
        {
            isGround = false;
        }
        isGroundEnter = false;
        isGroundStay = false;
        isGroundExit = false;
    }

と、このように関数で分離する事ができます。値をbool型で返してisGroundに代入してますね。

<summary>というのはVisual Studioの機能で、呼び出す関数の詳細を見る事ができるようになります。

summary comment

↑のように関数を呼び出そうとしたところで中身の内容を見る事ができる機能です。

まぁ、今はあまり使わないかもしれませんが、今後使用する事もあると思いますので、一応紹介しました。

特にいろんなスクリプトを書いて、他のスクリプトからアクセスするとき便利です。

チームで開発されている方は必ず覚えましょう。

次にY軸に関する処理を分けてしまいましょう。

     float verticalKey = Input.GetAxis("Vertical"); 
        float ySpeed = -gravity;
         if(isGround)
         {
             if (verticalKey > 0)
             {
                 ySpeed = jumpSpeed;
                 jumpPos = transform.position.y;        //ジャンプした位置を記録する
                 isJump = true;
                 jumpTime += Time.deltaTime;
             }
             else
             {
                 isJump = false;
                 jumpTime = 0.0f;
             }
         }
     else if(isJump)
         {
             //上ボタンを押されている。かつ、現在の高さがジャンプした位置から自分の決めた位置より下ならジャンプを継続する
             if (verticalKey > 0 && jumpPos + jumpHeight > transform.position.y)
             {
                 ySpeed = jumpSpeed;
                 jumpTime += Time.deltaTime;
             }
             else
             {
                 isJump = false;
                 jumpTime = 0.0f;
             }
         }


         if (isJump)
         {
             ySpeed *= jumpCurve.Evaluate(jumpTime);
         }

これらはまとめられそうです。

     /// <summary>
    /// Y成分で必要な計算をし、速度を返す。
    /// </summary>
    /// <returns>The y.</returns>
    private float SetY()
    {
        float verticalKey = Input.GetAxis("Vertical");
        float ySpeed = -gravity;
        if(isGround)
        {
            if (verticalKey > 0)
            {
                ySpeed = jumpSpeed;
                jumpPos = transform.position.y;        //ジャンプした位置を記録する
                isJump = true;
            }
            else
            {
                isJump = false;
            }
            jumpTime = 0.0f;
        }
        else if(isJump)
        {
            //上ボタンを押されている。かつ、現在の高さがジャンプした位置から自分の決めた位置より下ならジャンプを継続する
            if (verticalKey > 0 && jumpPos + jumpHeight > transform.position.y)
            {
                ySpeed = jumpSpeed;
                jumpTime += Time.deltaTime;
            }
            else
            {
                isJump = false;
                jumpTime = 0.0f;
            }
        }
        if (isJump)
        {
            ySpeed *= jumpCurve.Evaluate(jumpTime);
        }
        return ySpeed;
    }

こんな感じでまとめてみました。最後にY軸の速度を返すようにしています。

Xも同じようにすると

     /// <summary>
    /// X成分で必要な計算をし、速度を返す。
    /// </summary>
    /// <returns>The x.</returns>
    private float SetX()
    {
        float xSpeed;
        float horizontalKey = Input.GetAxis("Horizontal");
        
        if (horizontalKey > 0)
        {
            transform.localScale = new Vector3(1, 1, 1);
            anim.SetBool("run", true);
            xSpeed = speed;
            dashTime += Time.deltaTime;
        }
        else if (horizontalKey < 0)
        {
            transform.localScale = new Vector3(-1, 1, 1);
            anim.SetBool("run", true);
            xSpeed = -speed;
            dashTime += Time.deltaTime;
        }
        else
        {
            anim.SetBool("run", false);
            xSpeed = 0.0f;
            dashTime = 0.0f;
        }
        
        if (horizontalKey > 0 && beforeKey < 0)
        {
            dashTime = 0.0f;
        }
        else if (horizontalKey < 0 && beforeKey > 0)
        {
            dashTime = 0.0f;
        }
        
        xSpeed *= dashCurve.Evaluate(dashTime);
        beforeKey = horizontalKey;
        return xSpeed;
    }

こうなります。ここで、アニメーションの機能は分けたいので、新たにisRunという変数を作ってアニメーションも別で分けてしまいましょう。

     private bool isRun = false;
     anim.SetBool("run", isRun);

こうして、SetX()のフラグを変えているところをisRunに変えてしまいましょう。

そして、アニメーションはアニメーションで一まとめにしましょう。

     /// <summary>
    /// アニメーションを設定する
    /// </summary>
    private void SetAnimation()
    {
        anim.SetBool("jump", isJump);
        anim.SetBool("ground", isGround);
        anim.SetBool("run", isRun);
    }

こうする事でFixedUpdateの中身がスッキリしました

     void FixedUpdate()
    {
        GroundCheck();
        rb.velocity = new Vector2(SetX(), SetY());
        SetAnimation();
    }

<regionでたたもう>

Visual Studioの機能で、コードの中身をたたむ事ができます。

#region//ここにコメントをかける
#endregion

と書くとコードをたためます。

region endregion

↑のような感じで間に挟むと

Fold the code

こんな感じにたたむ事ができます。

Fold the code all

随分とスッキリしましたね。

これで、何か機能を追加したり、変更したりする際にどこをいじればいいのかがハッキリしたかなと思います。

また、別のゲームを作りたくなった時にコードを使い回しやすくなるのでオススメです。↑のような状態だと全部のコードを使わなくても一部分だけコピペする事も容易になります。

乱雑に書くと、そのときはいいのですが、後から変更したくなったりいじりたくなったりするとバグを生みやすくなるのでなるべく整理整頓した方がいいかなと思います。

めんどくさくなるのはわかりますが、バグるよかいいので

というわけで、プログラムを整理しようという記事でした。

今回で整理できたコードは以下です。

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

public class player : MonoBehaviour
{
    #region//インスペクターで設定する
    [Header("移動速度")]public float speed;
    [Header("重力")]public float gravity;
    [Header("ジャンプ速度")]public float jumpSpeed;
    [Header("ジャンプする高さ")]public float jumpHeight;
    [Header("ダッシュの速さ表現")]public AnimationCurve dashCurve;
    [Header("ジャンプの速さ表現")]public AnimationCurve jumpCurve;
    #endregion

    #region//プライベート変数
    private Animator anim = null;
    private Rigidbody2D rb = null;
    private string groundTag = "Ground";
    private bool isGroundEnter, isGroundStay, isGroundExit;
    private bool isGround = false;
    private bool isJump = false;
    private bool isRun = false;
    private float jumpPos = 0.0f;
    private float dashTime, jumpTime;
    private float beforeKey;
    #endregion

    void Start()
    {
        //コンポーネントのインスタンスを捕まえる
        anim = GetComponent<Animator>();
        rb = GetComponent<Rigidbody2D>();
    }

    void FixedUpdate()
    {
        GroundCheck();
        rb.velocity = new Vector2(SetX(), SetY());
        SetAnimation();
    }
    
    /// <summary>
    /// 接地しているかどうかの判定をとる
    /// </summary>
    /// <returns><c>true</c>, 接地している, <c>false</c> 接地していない</returns>
    private void GroundCheck()
    {
        if(isGroundEnter || isGroundStay)
        {
            isGround = true;
        }
        else if(isGroundExit)
        {
            isGround = false;
        }
        isGroundEnter = false;
        isGroundStay = false;
        isGroundExit = false;
    }
    
    /// <summary>
    /// Y成分で必要な計算をし、速度を返す。
    /// </summary>
    /// <returns>The y.</returns>
    private float SetY()
    {
        float verticalKey = Input.GetAxis("Vertical");
        float ySpeed = -gravity;
        if(isGround)
        {
            if (verticalKey > 0)
            {
                ySpeed = jumpSpeed;
                jumpPos = transform.position.y;        //ジャンプした位置を記録する
                isJump = true;
            }
            else
            {
                isJump = false;
            }
            jumpTime = 0.0f;
        }
        else if(isJump)
        {
            //上ボタンを押されている。かつ、現在の高さがジャンプした位置から自分の決めた位置より下ならジャンプを継続する
            if (verticalKey > 0 && jumpPos + jumpHeight > transform.position.y)
            {
                ySpeed = jumpSpeed;
                jumpTime += Time.deltaTime;
            }
            else
            {
                isJump = false;
                jumpTime = 0.0f;
            }
        }
        if (isJump)
        {
            ySpeed *= jumpCurve.Evaluate(jumpTime);
        }
        return ySpeed;
    }

    /// <summary>
    /// X成分で必要な計算をし、速度を返す。
    /// </summary>
    /// <returns>The x.</returns>
    private float SetX()
    {
        float xSpeed;
        float horizontalKey = Input.GetAxis("Horizontal");
        
        if (horizontalKey > 0)
        {
            transform.localScale = new Vector3(1, 1, 1);
            isRun = true;

            xSpeed = speed;
            dashTime += Time.deltaTime;
        }
        else if (horizontalKey < 0)
        {
            transform.localScale = new Vector3(-1, 1, 1);
            isRun = true;
            xSpeed = -speed;
            dashTime += Time.deltaTime;
        }
        else
        {
            isRun = false;
            xSpeed = 0.0f;
            dashTime = 0.0f;
        }
        
        if (horizontalKey > 0 && beforeKey < 0)
        {
            dashTime = 0.0f;
        }
        else if (horizontalKey < 0 && beforeKey > 0)
        {
            dashTime = 0.0f;
        }
        
        xSpeed *= dashCurve.Evaluate(dashTime);
        beforeKey = horizontalKey;
        return xSpeed;
    }
    
    /// <summary>
    /// アニメーションを設定する
    /// </summary>
    private void SetAnimation()
    {
        anim.SetBool("jump", isJump);
        anim.SetBool("ground", isGround);
        anim.SetBool("run", isRun);
    }

    #region//接地判定
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.tag == groundTag)
        {
            isGroundEnter = true;
        }
    }

    private void OnTriggerStay2D(Collider2D collision)
    {
        if (collision.tag == groundTag)
        {
            isGroundStay = true;
        }
    }
    
    private void OnTriggerExit2D(Collider2D collision)
    {
        if (collision.tag == groundTag)
        {
            isGroundExit = true;
        }
    }
    #endregion
}

<わからない事、質問等があれば>

このサイトの説明ではよくわからなかったとか、もっと知りたい事などがあれば

自分の Youtubeの動画にコメントで質問していただければ動画でお答えしようと思います。

文章同士のやり取りだと伝わりづらいし、ラリーに時間がかかりそうなので動画で回答します。

↓の動画が回答の一例になります。どの動画でもいいのでご遠慮なくコメントしてください

できたらチャンネル登録よろしくお願いします!



タイトルとURLをコピーしました