今回はUnityの2D横スクロールアクションでプレイヤーキャラクターをジャンプさせる実装をしていきます。
これまでに2D横スクロール用のマップ&キャラクターを用意して、簡単な移動ができる所までを実装しています。
※詳細は本文内で再度紹介します。
ジャンプの操作もできる様になれば、2Dに必要なメイン操作がほぼできあがる事になります。実際にキャラクターを操作してジャンプできる様になると、一気にゲームをしている!という雰囲気が出て、ゲームにのめりこむ事ができる様になります。
コンテンツ
2Dのプレイヤーキャラクターをジャンプをさせるために必要な準備
2D横スクロールアクションでプレイヤーキャラクターをジャンプさせるためには、マップとキャラクターのアセットが必要になります。
アセットはUnityのアセットストアを活用しても良いですし、自分で作成してもOKです。
今回はアセットストアで人気の2Dアセットを使用して、既にマップの作成、キャラクターの作成や横移動の操作まではできる様にしてあります。コードも既に作成しているものを元にジャンプ処理を実装していきます。
※詳細は以下を参考にして下さい。
ジャンプ処理の実装
PCの場合、ジャンプは「space」キーを押して操作できる様にしようと思います。
ジャンプの実装はバグが発生しやすい箇所でもあるので、バグも潰しながら実装していきましょう。
ジャンプ処理用のコードを追加
まずは「Unityを使って2Dキャラクターを横移動させよう!」で紹介したC#スクリプトに以下の処理を追加します。
- ジャンプ力(変数名:jumpingPower):public変数にしてUnityエディタから設定できる様にする
※jumpingPowerの値はとりあえず300~400位だといい感じだと思います。 - スペース押下時のジャンプ処理:キーボードのspaceが押下されたら、キャラクターに物理処理を追加(Vector2.upを使って上方向に物理効果を与える)
※ちなみにspaceの入力判定には「Input.GetKeyDown」を使用します。「Input.GetKey」というメソッドもありますが、このメソッドを使用するとspaceを押している間は何度もジャンプして!という命令を与えてしまう事になります。
詳しくはコードを見た方が分かるかもしれません。以下が追加するコードになります。
■C#スクリプトに追加する変数
1 2 3 4 |
public class PlayerControl : MonoBehaviour { public float jumpingPower; } |
■C#スクリプト(Updateに追加する処理)
1 2 3 4 5 6 7 8 |
void Update() { //Spaceが押された場合 if (Input.GetKeyDown("space")) { rb2d.AddForce(Vector2.up * jumpingPower); } } |
ジャンプできる事を確認する
さて、実行してみましょう!
一応ちゃんとジャンプはできていますね!
ただ、2回目のジャンプがちょっとおかしい。。。2段ジャンプができてしまっています。つまり、バグが発生しています。これはジャンプの実装でよくあるバグです。
このバグを修正していきましょう。
ジャンプ時のバグの修正
ジャンプの実装ではバグが発生しやすいです。
よくあるバグが上記で発生している「ジャンプ中にもう一度ジャンプできてしまう」というものです。
このバグを取り除くために「プレイヤーキャラクターが地面に付いている時だけジャンプができる」という処理を追加で実装する必要があります。
ここで追加する処理は初見だとちょっと難しく思えるかもしれませんので、何度か確認をしてみて下さい。
接地判定用の線の追加
さて、地面に付いている時だけジャンプできる様にするためには、どんな判定処理が必要になるでしょうか?
オーソドックスで多くのゲーム開発者が活用している方法が「見えない線」を使用するというものです。
キャラクターの足元辺りから線を引いて、その線と地面が着地している場合だけジャンプできる様にする訳です。
※線というのはいわば座標です。座標同士が交わった場合の特定処理によく使用します。
変数の準備
まずはプレイヤーの足元から接地判定用の線を引きます。
ここで以下の様な変数を定義します。
Vector3 left_SP:始点1※プレイヤーの右足(こちらから見て左)辺り
Vector3 right_SP:始点2※プレイヤーの左足(こちらから見て右)辺り
Vector3 EP:終点
線の描画(デバッグ用)
また、見えない線のままだとデバッグできないため、一旦見える様にDebug.DrawLineという描画デバッグメソッドを使用します。
Debug.DrawLine(始点,終点);
として記述すれば簡単に使用できます。
デバッグ用のコード
コードを書き換えて実行してみましょう!Updateの中に以下の様なコードを追記してみて下さい。
■C#スクリプト
1 2 3 4 5 6 7 8 |
void Update() { Vector3 left_SP = transform.position - Vector3.right * 0.3f; Vector3 right_SP = transform.position + Vector3.right * 0.3f; Vector3 EP = transform.position - Vector3.up * 0.1f; Debug.DrawLine(left_SP, EP); Debug.DrawLine(right_SP, EP); } |
実行してみる
実行してSceneビューを見てみると、以下の様にプレイヤーキャラクターの上に線(下向きの三角の様な線)が描画されます。
※GameビューではなくてSceneビューです。
ただ、この線は地面の接地判定に使用する(地面と線が交わった場合だけジャンプを可能にする)のに、線が地面に接地できてないですよね。
ちゃんとプレイヤーキャラクターの足元を始点にして描画をしなければなりません。
実はこの線の描画はプレイヤーキャラクターの中心から算出されます。今はキャラ画像の中心から線が描画されている訳です。
プレイヤーキャラクターのPivotを変更
現在は用意したキャラ画像の中心が線の始点になっているため、接地判定用の線を描画しても、地面と線が接地しません。
そのため、プレイヤーキャラクターの足元に線の始点をずらす事で、地面と接地判定ができる様にします。
まず、アセットのキャラ画像を全て選択します。
この状態で、Inspectorウィンドウ>Pivotを選択します。
Pivotが全体Centerになっていると思うので、これをBottomに変更します。
■Inspectorウィンドウ>Pivotを「Bottom」に変更
以下の様なメッセージが出力されたら「Apply」を選択します。
これで、描画線の位置が正しくなったはずなので、プレイヤーキャラクターと地面との接地判定が可能になりました。
さっそく実行!とその前に、もう一つ修正しなければなりません。
Pivotを変更した事により、Box Coliderの位置がずれてしまっています。これを修正しましょう。
「Box Colider」のEdit機能を使用して、Coliderの位置を修正します。
実行してみる
ではこの状態で再度実行してみましょう。
プレイヤーキャラクターの足元から接地判定用の線が描画(デバッグ描画)されている事が分かります。これで接地判定の処理を加えればジャンプ時のバグを回避できます。
接地判定処理の実装
これでプレイヤーキャラクターを自分で操作する事ができる様になりました。2D横スクロールアクションの核となる部分はもうできたと言っても過言ではありません。
実際に接地判定する処理を追加しましょう。
先程、接地判定用の線を描画しましたが、この処理はIsCollisionというメソッドの中で行う様に変更を行います。そして、地面と接地判定を行った結果、地面に接地していれば、ジャンプを行うという処理を実装します。
■追加する変数
- (bool)jumpFlg :地面との接地判定の結果を保持するフラグ
- (LayerMask )CollisionLayer:地面のレイヤー(厳密にはタイルマップにレイヤーを貼る)
※レイヤーの詳細はこの後で説明します。
■追加する処理
- jumpメソッド:spaceボタンを押した場合、かつjumpFlgがTrueの場合に呼び出され、プレイヤーキャラクターがジャンプする。地面に接地するまではjumpFlgはfalseとなる。
- IsCollisionメソッド:地面と線が当たっているかを判定する。接地判定用の線と地面のレイヤーがぶつかった場合は接地していると判断する。
レイヤーの準備
上記の処理を実装するためには、まずはレイヤーを準備する必要があります。
タイルマップにレイヤーを設定する事で、このレイヤーとプレイヤーキャラクターが接地した場合を地面に接地した状態と判断する様に実装します。
以下の①~③の操作でタイルマップにレイヤーを設定していきましょう。
①以下の手順でレイヤーを作成します。
タイルマップ(Tilemap)オブジェクトを選択>
InspectorウィンドウでLayerを選択>
Add Layerを選択
②User Layer欄にレイヤー名を設定します。
③Layerで追加したレイヤーを選択します。
これで地面(タイルマップ)に接地判定用のレイヤーを設定できました。
サンプルC#スクリプトと設定
接地判定の方針と、レイヤー設定ができたので、C#スクリプトも以下の様なものを作成しました。
※一部デバッグ用の処理が残っています。必要に応じて使用して下さい。
接地判定の方針については上述していますが、IsCollisionメソッドの中で、「Physics2D.Linecast」というメソッド使い接地判定用の線とレイヤーがぶつかった場合に、Trueを返却するという処理を実装しています。
※Physics2D.Linecastの詳細については公式リファレンスを参考にして下さい。
URL:https://docs.unity3d.com/ja/current/ScriptReference/Physics2D.Linecast.html
■C#スクリプト
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerControl : MonoBehaviour { private Rigidbody2D rb2d; private float x_val; private float speed; public float inputSpeed; public float jumpingPower; public LayerMask CollisionLayer; private bool jumpFlg = false; void Start() { rb2d = GetComponent<Rigidbody2D>(); } void Update() { //←→が押された場合 x_val = Input.GetAxis("Horizontal"); jumpFlg = IsCollision(); //Spaceが押された場合 Debug.Log(jumpFlg); if (Input.GetKeyDown("space") && jumpFlg) { jump(); } } void FixedUpdate() { //待機 if ( x_val == 0) { speed = 0; } //右に移動 else if ( x_val > 0) { speed = inputSpeed; transform.localScale = new Vector3(1,1,1); } //左に移動 else if ( x_val < 0) { speed = inputSpeed * -1; transform.localScale = new Vector3(-1,1,1); } // キャラクターを移動 Vextor2(x軸スピード、y軸スピード(元のまま)) rb2d.velocity = new Vector2(speed, rb2d.velocity.y); } void jump() { rb2d.AddForce(Vector2.up * jumpingPower); jumpFlg = false; } bool IsCollision() { Vector3 left_SP = transform.position - Vector3.right * 0.2f; Vector3 right_SP = transform.position + Vector3.right * 0.2f; Vector3 EP = transform.position - Vector3.up * 0.1f; //Debug.DrawLine(left_SP, EP); //Debug.DrawLine(right_SP, EP); return Physics2D.Linecast(left_SP,EP,CollisionLayer) || Physics2D.Linecast(right_SP,EP,CollisionLayer); } } |
これだけで終わりではありません。
接地判定用のレイヤー変数(public)を用意しているので、対象となるレイヤー(先ほど作成したタイルマップに設定したレイヤー)を、Unityエディタで設定しましょう。
実行結果
今度はしっかりと1回だけジャンプできる様になっています。
まとめ
今回はUnityで2Dの横スクロールアクションゲームを作る時の、プレイヤーキャラクターのジャンプを実装してきました。
内容をおさらいします。
- ジャンプ処理を実装(spaceを押下したらジャンプする)
- バグの原因を確認
- プレイヤーキャラクターの足元から接地判定用の線を描画する
※コライダーの位置調整などが必要 - 地面(タイルマップ)にレイヤーを設定
- 接地判定用の線とレイヤーがぶつかった場合は接地していると判断
- プレイヤーキャラクターと地面が接地した場合のみジャンプする
ジャンプの実装をするだけでもちょっと大変だなと思ったかもしれませんが、2Dアクションを作る場合には基本的な内容になるので、しっかりと覚えておきましょう!