Unity 2Dアクションの作り方【プログラムを整理しよう】

今までで移動、ジャンプ、アニメーション、カメラをプレイヤーに追従と様々なものを作ってきました。プログラムが汚くなってきたのでそろそろ整理整頓していきましょう。↑の動画でも解説しています。わからない、うまくいかない事があったら質問される前に、一回、動画の方で手順を確認してください

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

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

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

クリックすると展開します。
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 float jumpLimitTime;//ジャンプ制限時間
     public GroundCheck ground; //接地判定
     public GroundCheck head;//頭ぶつけた判定
     public AnimationCurve dashCurve;
     public AnimationCurve jumpCurve;

     //プライベート変数
     private Animator anim = null;
     private Rigidbody2D rb = null;
     private bool isGround = false;
     private bool isJump = false;
     private bool isHead = false; 
     private float jumpPos = 0.0f;
     private float dashTime, jumpTime;
     private float beforeKey;

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

     void FixedUpdate()
     {
          //接地判定を得る
          isGround = ground.IsGround();
          isHead = head.IsGround(); 

          //キー入力されたら行動する
          float horizontalKey = Input.GetAxis("Horizontal");
          float xSpeed = 0.0f;
          float ySpeed = -gravity;
          float verticalKey = Input.GetAxis("Vertical");

          if (isGround)
          {
              if (verticalKey > 0)
              {
                  ySpeed = jumpSpeed;
                  jumpPos = transform.position.y; //ジャンプした位置を記録する
                  isJump = true;
                  jumpTime = 0.0f;
              }
              else
              {
                  isJump = false;
              }
          }
          else if (isJump)
          {
              //上方向キーを押しているか
       bool pushUpKey = verticalKey > 0;
       //現在の高さが飛べる高さより下か
       bool canHeight = jumpPos + jumpHeight > transform.position.y;
       //ジャンプ時間が長くなりすぎてないか
       bool canTime = jumpLimitTime > jumpTime;

       if (pushUpKey && canHeight && canTime && !isHead)
              {
                  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);
              dashTime += Time.deltaTime;
              xSpeed = speed;
          }
          else if (horizontalKey < 0)
          {
              transform.localScale = new Vector3(-1, 1, 1);
              anim.SetBool("run", true);
              dashTime += Time.deltaTime;
              xSpeed = -speed;
          }
          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;
          }
          beforeKey = horizontalKey;

          //アニメーションカーブを速度に適用
          xSpeed *= dashCurve.Evaluate(dashTime);
          if (isJump)
          {
              ySpeed *= jumpCurve.Evaluate(jumpTime);
          }
          anim.SetBool("jump", isJump);
          anim.SetBool("ground", isGround);
          rb.velocity = new Vector2(xSpeed, ySpeed);
      }
 }

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

public class GroundCheck : MonoBehaviour
{
     private string groundTag = "Ground";
     private bool isGround = false; 
     private bool isGroundEnter, isGroundStay, isGroundExit;

     //接地判定を返すメソッド
   //物理判定の更新毎に呼ぶ必要がある
     public bool IsGround()
     {    
          if (isGroundEnter || isGroundStay)
          {
              isGround = true;
          }
          else if (isGroundExit)
          {
              isGround = false;
          }

          isGroundEnter = false;
          isGroundStay = false;
          isGroundExit = false;
          return isGround;
     }

     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 float jumpLimitTime;
[Header("接地判定")] public GroundCheck ground;
[Header("頭をぶつけた判定")] public GroundCheck head;
[Header("ダッシュの速さ表現")] public AnimationCurve dashCurve;
[Header("ジャンプの速さ表現")] public AnimationCurve jumpCurve;

すると

add header attribute

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

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

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

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

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

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

この[○○]変数とする書き方はAttributeと呼ばれる手法で変数や関数に属性を付与する書き方です。

○○の中にはいろんな命令を書けます。C#の機能ですが、Unityで使用する場合、Unity側に働きかける命令を書くことができます。

<メソッド化しよう>

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

クリックすると展開します
     void FixedUpdate()
     {
          //接地判定を得る
          isGround = ground.IsGround();
          isHead = head.IsGround(); 

          //キー入力されたら行動する
          float horizontalKey = Input.GetAxis("Horizontal");
          float xSpeed = 0.0f;
          float ySpeed = -gravity;
          float verticalKey = Input.GetAxis("Vertical");

          if (isGround)
          {
              if (verticalKey > 0)
              {
                  ySpeed = jumpSpeed;
                  jumpPos = transform.position.y; //ジャンプした位置を記録する
                  isJump = true;
                  jumpTime = 0.0f;
              }
              else
              {
                  isJump = false;
              }
          }
          else if (isJump)
          {
       //上方向キーを押しているか
       bool pushUpKey = verticalKey > 0;
       //現在の高さが飛べる高さより下か
       bool canHeight = jumpPos + jumpHeight > transform.position.y;
       //ジャンプ時間が長くなりすぎてないか
       bool canTime = jumpLimitTime > jumpTime;

       if (pushUpKey && canHeight && canTime && !isHead)
              {
                  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);
              dashTime += Time.deltaTime;
              xSpeed = speed;
          }
          else if (horizontalKey < 0)
          {
              transform.localScale = new Vector3(-1, 1, 1);
              anim.SetBool("run", true);
              dashTime += Time.deltaTime;
              xSpeed = -speed;
          }
          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;
          }
          beforeKey = horizontalKey;

          //アニメーションカーブを速度に適用
          xSpeed *= dashCurve.Evaluate(dashTime);
          if (isJump)
          {
              ySpeed *= jumpCurve.Evaluate(jumpTime);
          }
          anim.SetBool("jump", isJump);
          anim.SetBool("ground", isGround);
          rb.velocity = new Vector2(xSpeed, ySpeed);
      }

んー。一つのメソッドの中にいろんな機能を書きすぎですね。

まとめられるものはまとめてしまいましょう。各種機能をメソッドにしてしまいます。

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

          if (isGround)
          {
              if (verticalKey > 0)
              {
                  ySpeed = jumpSpeed;
                  jumpPos = transform.position.y; //ジャンプした位置を記録する
                  isJump = true;
                  jumpTime = 0.0f;
              }
              else
              {
                  isJump = false;
              }
          }
          else if (isJump)
          {
              //上方向キーを押しているか
       bool pushUpKey = verticalKey > 0;
       //現在の高さが飛べる高さより下か
       bool canHeight = jumpPos + jumpHeight > transform.position.y;
       //ジャンプ時間が長くなりすぎてないか
       bool canTime = jumpLimitTime > jumpTime;

       if (pushUpKey && canHeight && canTime && !isHead)
              {
                  ySpeed = jumpSpeed;
                  jumpTime += Time.deltaTime;
              }
              else
              {
                  isJump = false;
                  jumpTime = 0.0f;
              }
          }

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

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

/// <summary>
/// Y成分で必要な計算をし、速度を返す。
/// </summary>
/// <returns>Y軸の速さ</returns>
private float GetYSpeed()
{
     float verticalKey = Input.GetAxis("Vertical");
     float ySpeed = -gravity;

     if (isGround)
     {
         if (verticalKey > 0)
         {
             ySpeed = jumpSpeed;
             jumpPos = transform.position.y; //ジャンプした位置を記録する
             isJump = true;
             jumpTime = 0.0f;
         }
         else
         {
             isJump = false;
         }
     }
     else if (isJump)
     {
         //上方向キーを押しているか
     bool pushUpKey = verticalKey > 0;
     //現在の高さが飛べる高さより下か
     bool canHeight = jumpPos + jumpHeight > transform.position.y;
     //ジャンプ時間が長くなりすぎてないか
     bool canTime = jumpLimitTime > jumpTime;

     if (pushUpKey && canHeight && canTime && !isHead)
         {
             ySpeed = jumpSpeed;
             jumpTime += Time.deltaTime;
         }
         else
         {
             isJump = false;
             jumpTime = 0.0f;
         }
     }

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

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

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

sammary

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

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

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

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

Xも同じようにしたいですが、Xの計算する際に走っているかどうかの判定を行っています。

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

private bool isRun = false;

これを新たに定義した後X方向の速度計算をメソッド化すると↓のようになります。

/// <summary>
/// X成分で必要な計算をし、速度を返す。
/// </summary>
/// <returns>X軸の速さ</returns>
private float GetXSpeed()
{
     float horizontalKey = Input.GetAxis("Horizontal");
     float xSpeed = 0.0f;
     if (horizontalKey > 0)
     {
         transform.localScale = new Vector3(1, 1, 1);
        isRun = true;
         dashTime += Time.deltaTime;
         xSpeed = speed;
     }
     else if (horizontalKey < 0)
     {
         transform.localScale = new Vector3(-1, 1, 1);
        isRun = true;
         dashTime += Time.deltaTime;
         xSpeed = -speed;
     }
     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;
     }

     beforeKey = horizontalKey;
     xSpeed *= dashCurve.Evaluate(dashTime);
     beforeKey = horizontalKey;
     return xSpeed;
}

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

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

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

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

void FixedUpdate()
{
     //接地判定を得る
     isGround = ground.IsGround();
     isHead = head.IsGround(); 

     //各種座標軸の速度を求める
     float xSpeed = GetXSpeed();
     float ySpeed = GetYSpeed();

     //アニメーションを適用
     SetAnimation();

     //移動速度を設定
     rb.velocity = new Vector2(xSpeed, ySpeed);
}
スポンサーリンク

<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 float jumpLimitTime;
     [Header("接地判定")] public GroundCheck ground;
     [Header("天井判定")] public GroundCheck head;
     [Header("ダッシュの速さ表現")] public AnimationCurve dashCurve;
     [Header("ジャンプの速さ表現")] public AnimationCurve jumpCurve;
     #endregion

     #region//プライベート変数 
     private Animator anim = null; 
     private Rigidbody2D rb = null; 
     private bool isGround = false; 
     private bool isJump = false; 
     private bool isRun = false; 
     private bool isHead = false; 
     private float jumpPos = 0.0f; 
     private float dashTime = 0.0f;
     private float jumpTime = 0.0f;
     private float beforeKey = 0.0f;
     #endregion 

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

     void FixedUpdate() 
     {
          //接地判定を得る
          isGround = ground.IsGround();
          isHead = head.IsGround(); 

          //各種座標軸の速度を求める
          float xSpeed = GetXSpeed();
          float ySpeed = GetYSpeed();

          //アニメーションを適用
          SetAnimation();

          //移動速度を設定
          rb.velocity = new Vector2(xSpeed, ySpeed);
     } 

     /// <summary> 
     /// Y成分で必要な計算をし、速度を返す。 
     /// </summary> 
     /// <returns>Y軸の速さ</returns> 
     private float GetYSpeed() 
     {
          float verticalKey = Input.GetAxis("Vertical");
          float ySpeed = -gravity;

          if (isGround)
          {
              if (verticalKey > 0)
              {
                  ySpeed = jumpSpeed;
                  jumpPos = transform.position.y; //ジャンプした位置を記録する
                  isJump = true;
                  jumpTime = 0.0f;
              }
              else
              {
                  isJump = false;
              }
          }
          else if (isJump)
          {
             //上方向キーを押しているか
       bool pushUpKey = verticalKey > 0;
       //現在の高さが飛べる高さより下か
       bool canHeight = jumpPos + jumpHeight > transform.position.y;
       //ジャンプ時間が長くなりすぎてないか
       bool canTime = jumpLimitTime > jumpTime;

       if (pushUpKey && canHeight && canTime && !isHead)
              {
                  ySpeed = jumpSpeed;
                  jumpTime += Time.deltaTime;
              }
              else
              {
                  isJump = false;
                  jumpTime = 0.0f;
              }
          }

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

          return ySpeed;
     }

     /// <summary> 
     /// X成分で必要な計算をし、速度を返す。 
     /// </summary> 
     /// <returns>X軸の速さ</returns> 
     private float GetXSpeed() 
     {
          float horizontalKey = Input.GetAxis("Horizontal");
          float xSpeed = 0.0f;

          if (horizontalKey > 0)
          {
              transform.localScale = new Vector3(1, 1, 1);
              isRun = true;
              dashTime += Time.deltaTime;
              xSpeed = speed;
          }
          else if (horizontalKey < 0)
          {
              transform.localScale = new Vector3(-1, 1, 1);
              isRun = true;
              dashTime += Time.deltaTime;
              xSpeed = -speed;
          }
          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;
          }

          beforeKey = horizontalKey;
          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); 
     }
 }

何かうまくいかない事があった場合は↓の記事を参考にしてみてください

最低限↓の動画の要件を満たしていない質問は受けかねるので、ご理解ください。

また、筆者も間違えることはありますので、何か間違っている点などありましたら、動画コメント欄にでも書いていただけるとありがたいです。


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