ユニティちゃんの2D横スクロールアクション(その3:ジャンプ)【チュートリアル】

06/16追記
書き直しました。
複数の機能をいきなり入れることよりとりあえず動くものを作ります。

ども、ゴコーです。

前回は移動処理に関してでした。
今回はジャンプを実装してみたいと思います。

ジャンプは何ができたら成功でしょうか?

  • ボタンを押した瞬間上向きの力を加える
  • 地面に着地している時はジャンプできる

シンプルに行きます。要素の追加は動くものができてから。
初心者の鉄則です。多分。

UnityChanController.cs

[csharp]

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(BoxCollider2D))]
public class UnityChanController : MonoBehaviour {

public Rigidbody2D cRigidbody2D
{
get
{
if(!_cRigidbody2D)
_cRigidbody2D = rigidbody2D;
return _cRigidbody2D;
}
}
Rigidbody2D _cRigidbody2D;

public Transform cTransform
{
get
{
if(!_cTransform)
_cTransform = transform;
return _cTransform;
}
}
Transform _cTransform;

public float moveSpeed = 5;
public float jumpForce = 1000;

void FixedUpdate()
{
Move();
Jump();
}

void Move()
{
if((cTransform.localScale.x > 0 && Input.GetAxisRaw("Horizontal") < 0)
|| (cTransform.localScale.x < 0 && Input.GetAxisRaw("Horizontal") > 0))
{
Vector2 temp = cTransform.localScale;
temp.x *= -1;
cTransform.localScale = temp;
}
cRigidbody2D.velocity = new Vector2(moveSpeed * Input.GetAxisRaw("Horizontal"),
cRigidbody2D.velocity.y);
}

void Jump()
{
if(Input.GetButtonDown("Jump"))
{
cRigidbody2D.AddForce(Vector2.up * jumpForce);
}
}
}

[/csharp]


修正前の記事のものを参照された人は申し訳ありませんが、上記のスクリプトをコピペするなりしてから読み進めてください。
申し訳ありません。


まずは

  • ボタンを押した瞬間上向きの力を加える

をできる様にします。

[csharp]

void Jump()
{
if(Input.GetButtonDown("Jump"))
{
cRigidbody2D.AddForce(Vector2.up * jumpForce);
}
}

[/csharp]

前回の記事でも書いたようにInputクラスは入力を管理するクラスでした。
GetButton()とは予め登録してあるボタンを呼ぶメソッドです。

uni-tuto-3-0

ボタンの登録、削除は自由にできます。
UnityEditor左上のProject setting > Input を選択して下さい。

uni-tuto-3-1

InputManagerというものが表示されました。
いくつか開いてみると「Jump」という項目があります。
Positive Button、Negative Button が登録してあるキーを表しています。

今回の場合、Jumpボタンはスペースキーで反応する、ということが分かりました。

[csharp]

cRigidbody2D.AddForce(Vector2.up * jumpForce);

[/csharp]

Rigidbody2Dのメソッドの1つ AddForceとは力を加える、ということです。まんまです。
今回の場合、対象は自分自身ですね。
Vector2.upで上向き(y)を指定し、そこにjumpForceをかけることでジャンプをします。

ちなみに、1000という数字は大きすぎますのでこのままだと飛びすぎです。
良い感じにインスペクターから変更してみて下さい。
ジャンプの降りてくる速度はRigidbody2DのGravityScaleからプレーヤーにかかる重力を変更することで早くすることもできます。

さて、調整は終わりましたか?

これでジャンプの動きは付けられましたが、ボタンを押せば何度でもジャンプできてしまいます。
水中での動きには使えるかもしれませんが今回の主旨とは外れるので修正しましょう。

uni-tuto-3-2

地面を識別するためにタグ、というオブジェクトを識別する手段を使います。
Cubeを選択し、インスペクターウィンドウのTagからAddTagを選択してください。

uni-tuto-3-3

Tagsから「Ground」を追加してください。その後、CubeのタグをGroundに設定します。
全てのゲームオブジェクトにはタグを付けることが可能で、今回はプレーヤーが触れた相手が何なのか、ということを識別する為に使います。
今後のパートでも使うことになる機能なので覚えておいてください。


では、地面に触れたときジャンプ可能にするスクリプトを書いてみましょう。

[csharp]

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(BoxCollider2D))]
public class UnityChanController : MonoBehaviour {

public Rigidbody2D cRigidbody2D
{
get
{
if(!_cRigidbody2D)
_cRigidbody2D = rigidbody2D;
return _cRigidbody2D;
}
}
Rigidbody2D _cRigidbody2D;

public Transform cTransform
{
get
{
if(!_cTransform)
_cTransform = transform;
return _cTransform;
}
}
Transform _cTransform;

public float moveSpeed = 5;
public float jumpForce = 1000;

bool isGrounded;

void FixedUpdate()
{
Move();
Jump();
}

void Move()
{
if((cTransform.localScale.x > 0 && Input.GetAxisRaw("Horizontal") < 0)
|| (cTransform.localScale.x < 0 && Input.GetAxisRaw("Horizontal") > 0))
{
Vector2 temp = cTransform.localScale;
temp.x *= -1;
cTransform.localScale = temp;
}
cRigidbody2D.velocity = new Vector2(moveSpeed * Input.GetAxisRaw("Horizontal"),
cRigidbody2D.velocity.y);
}

void Jump()
{
if(isGrounded && Input.GetButtonDown("Jump"))
{
isGrounded = false;
cRigidbody2D.AddForce(Vector2.up * jumpForce);
}
}

void OnCollisionEnter2D(Collision2D col)
{
if(col.gameObject.tag == "Ground")
isGrounded = true;
}
}

[/csharp]


着地しているかの判定を取るための変数(isGrounded)を1つ追加しました。

ジャンプを実行するとき、変更前のものだと「Jumpボタンを押されたか」ということだけをジャンプの条件にしていましたが、
今回は着地しているかも条件に加えて

[csharp]

if(isGrounded && Input.GetButtonDown("Jump"))

[/csharp]

に変更しました。


Tips
「&&」演算子は if 文の中などで複数の条件を満たしているかチェックしたいときに記述するものです。

便利ですが、あまりやり過ぎると読みにくくなるので注意しましょう。


実際に着地判定を取る為のメソッドは

OnCollisionEnter2D()

といいます。Collision2D を引数に取ります。
よくOnTriggerEnter2D()などの Collider2D と打ち間違える人がいるので気を付けましょう。

上記2つのメソッドは2つのコライダーを持つオブジェクトがぶつかったときに、少なくとも片方にRigidbody2Dがあれば呼ばれます。
そして、ぶつかった相手(の持つ情報)に応じた処理をできる、というものになります。

※そもそも引数や、書いてあることが分からない人は今は気にしなくても構いません。
なんとなくで進められるのでいずれ理解して下さい。

今回はぶつかった相手のタグを調べ、タグが「Ground」ならば着地フラグ「isGrounded」を立てています。

[csharp]

void OnCollisionEnter2D(Collision2D col)
{
if(col.gameObject.tag == "Ground")
isGrounded = true;
}

[/csharp]

今回使っているのは衝突した瞬間呼ばれる OnCollisionEnter2D() でしたが、
他にも衝突している間呼ばれ続ける OnCollisionStay2D()や、離れた瞬間呼ばれる OnCollisionExit2D() などがあります。


何度もジャンプ→減速を繰り返していると、時々反応しないことがあることに気づきましたか?

これは、入力がFixedUpdate()の中で行われるから起こる現象です。

Update()は毎フレーム(1秒間に大体60くらい)実行されます。しかし、処理落ちなどが起こってしまうとそのフレームでは処理が飛ばされます。
一方、FixedUpdate()は0.◌秒毎のような一定の時間で実行されます。僕ら人間からすると大変短いスパンですが、Update()と比べれば穴だらけです。

そんな処理の仕方が違う二つの更新ですが、一般的には
Rigidbodyみたいに処理落ちによって挙動がおかしくなっては困るものはFixedUpdate()に、
ボタン入力のような見過ごされては困るものはUpdate()に書かれます。

ジャンプ処理はボタンが押されたフレームに処理を開始するものでした。
押された瞬間がFixedUpdate()の穴にはまっていて反応がなかった、ということです。
左右への移動処理は「押された瞬間」ではなく、「押している間」行われるものだったため違和感がなかったんですね。

では修正しましょう。
ボタン入力は全てUpdate()で読み取るようにします。

[csharp]

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(BoxCollider2D))]
public class UnityChanController : MonoBehaviour {

public Rigidbody2D cRigidbody2D
{
get
{
if(!_cRigidbody2D)
_cRigidbody2D = rigidbody2D;
return _cRigidbody2D;
}
}
Rigidbody2D _cRigidbody2D;

public Transform cTransform
{
get
{
if(!_cTransform)
_cTransform = transform;
return _cTransform;
}
}
Transform _cTransform;

public float moveSpeed = 5;
public float jumpForce = 1000;

float InputHorValue;

bool isGrounded;
bool canJump;

void Update()
{
InputCheck();
}

void InputCheck()
{
InputHorValue = Input.GetAxisRaw("Horizontal");
if(isGrounded && Input.GetButtonDown("Jump")) canJump = true;
}

void FixedUpdate()
{
Move();
Jump();
}

void Move()
{
if((cTransform.localScale.x > 0 && InputHorValue < 0)
|| (cTransform.localScale.x < 0 && InputHorValue > 0))
{
Vector2 temp = cTransform.localScale;
temp.x *= -1;
cTransform.localScale = temp;
}
cRigidbody2D.velocity = new Vector2(moveSpeed * InputHorValue,
cRigidbody2D.velocity.y);
}

void Jump()
{
if(canJump)
{
canJump = false;
isGrounded = false;
cRigidbody2D.AddForce(Vector2.up * jumpForce);
}
}

void OnCollisionEnter2D(Collision2D col)
{
if(col.gameObject.tag == "Ground")
isGrounded = true;
}
}

[/csharp]

良い感じですね。
ついでに横方向の入力も Update()で取るように修正するために変数(InputHorValue)を追加して、
FixedUpdate()で使うようにしました。

入力をUpdate()で取るのは非常に大事です。
同じ値が複数のメソッドで使われることもありますし、今回のように入力があった瞬間だけ処理を行うみたいな内容だと入力を拾えないのは致命的だからです。


これで入力に応じたジャンプができるようになりました。

次回はいよいよアニメーションの切り替えをやります。

では、また。

記事修正に振り回された人本当にすみませんでしたm(_ _)m

Comments

comments

スポンサーリンク
336*280px
336*280px

フォローする

スポンサーリンク
336*280px

コメント

  1. micro より:

    AnimatorのApply Root Motionという項目のチェックを外したらできました。
    コメントの返信ありがとうございました。^^