さて、前回まででとりあえずの足場とキャラクターの走るモーションを作成しました。↑の動画でも解説しています。わからない、うまくいかない事があったら質問される前に、一回、動画の方で手順を確認してください
この記事は本のように順を追って解説しています。この記事は途中のページになります。
この記事を見ていて、現在の状況がわからない場合や忘れてしまった事などが出てきたら↓のリンクから目次ページへ飛べますので立ち戻って見てください。
<移動処理を作る前に!>
今までの解説で現在↓のような状態になっています。アニメーションができているだけの状態ですね。
ここで、移動処理を作る前に注意点があります。
以前、Cubeを動かした時にTransform.positionで動かしていました。
↓のようなプログラムだったと思います。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class test : MonoBehaviour
{
void Start()
{
}
void Update()
{
transform.position += Vector3.right;
}
}
一見これを応用すれば移動処理を作れそうです。
が、ちょっと待ってください!
実はUnityには移動処理というのは何種類もあって、それぞれ一長一短の動きをします。かと言って、全ての移動方法を説明していては頭が痛くなってしまうので、1つの移動方法と何故、この方法を使用するのか、そのメリットとデメリットについて解説しようと思います。
ちなみにですが、別にtransform.positionでも動かせます。ですが適切ではないです。
これが、Unityの怖いところで、動くんですよ。何種類もある移動方法のどれを使用しても動きます。でも適切ではない。×ではないが△の移動方法がたくさん存在します。
△じゃダメなのかという話ですが、バグりやすくなったり、重くなったりします。
重くなる場合が一番厄介で、少しずつ、少しずつ重さを蓄積していって、ある日突然カクカクしだすので、こういう△の手法を取ってしまうと原因を非常に特定しづらくなります。原因が1つではなく蓄積が原因ですので。
ググると移動方法って色々出てきますが、何が適切なのか十分に注意しましょう。
<移動方法について>
Unityで物体を移動させようとした時、大きく分けると2パターンに分けることができます。
・Transform操作・・・位置情報を直接制御する方法です(ただ移動するだけ)
・物理エンジン操作・・・物理演算でオブジェクトの位置を制御する方法です(オブジェクトを動かした時周囲の状況により様々な計算が入る)
基本この2パターンの中から移動方法を選ぶことになります。
特に自分からプログラムで指定しない場合、デフォルトの状態ではTransform操作になります。
今回は、移動の際に当たり判定を使って地面に接地したいです。その為、当たり判定は利用したいです。
ですが、2Dアクションでよく利用されるアクション類は大抵物理法則を無視することが多いです。例えば2段ジャンプとか空中ダッシュとか物理法則もクソもありません。
そのため、様々なアクションをしたい場合、物理演算は使用したくありません。
今回のポイントをまとめると
と、なります。
当たり判定を利用する場合、物理エンジンを利用しなければなりません。ですが、物理的挙動は無視する移動を作っていこうと思います。
今回の2Dアクションゲームでは有効な方法ですが、別種類のゲームを作る時にこの方法をそのまま使うのは△になる可能性が高いので、注意してください。
ちなみに、当たり判定を持っている状態でTransform系の移動をした場合とても重くなってしまいます。そのため、当たり判定をつけた時点でほぼほぼTransform系はNGと考えてもらっていいです。
<物理的挙動を無視した物理エンジン操作>
改めて、書き出すとイカついタイトルになっていますが、とりあえず解説していきたいと思います。
Rigidbody2D.velocity
この方法を利用する場合、まず、移動させたいゲームオブジェクトにRigidobodyかRigidbody2Dを追加してください。今回は2Dアクションなので、Rigidbody2Dを追加します。前回から来た人はすでについていると思います。
このRigidbodyというコンポーネントはアタッチしたゲームオブジェクトとその子オブジェクトを物理演算で動かせるようにするコンポーネントです。
前回のスクリプトから今、↓のようなスクリプトがプレイヤーにアタッチされていると思います。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Player : MonoBehaviour { private Animator anim = null; void Start() { anim = GetComponent<Animator>(); } void Update() { float horizontalKey = Input.GetAxis("Horizontal"); if (horizontalKey > 0) { transform.localScale = new Vector3(1, 1, 1); anim.SetBool("run", true); } else if (horizontalKey < 0) { transform.localScale = new Vector3(-1, 1, 1); anim.SetBool("run", true); } else { anim.SetBool("run", false); } } }
ここに、右ボタンか左ボタンを押された時移動する処理を追加したいと思います。
まずは、Rigidbody2Dのインスタンスを取得します。
private Animator anim = null; private Rigidbody2D rb = null; void Start() { anim = GetComponent<Animator>(); rb = GetComponent<Rigidbody2D>(); }
このRigidbody2Dを使うことによってゲームオブジェクトが物理エンジンによる操作を行うことができます。が、素直に物理エンジンを使用すると2Dアクション特有の物理法則を無視することができないので変則的な使い方をします。
どうするかと言いますと、Rigidbody2D.velocityを利用します。
このvelocityというのはRigidbody2Dの変数になります。何を表しているかというと「速度」を表しています。
velocityの型はVector2です。Vector3はx,y,zでしたので、一個少ないこれはxとyだけになります。2D用というわけですね。
ということで、velocity > (x , y) で x が横方向の速度を表します。 y が縦方向の速度を表します。
本当はvelocityを直接いじることは推奨されていません。というのも、物理エンジンがたくさんいろいろな計算を行って、物理法則になるべく近い形にするように計算した結果をこの変数に入れているからです。
つまり、この変数を直接触るということは物理演算の計算結果を捨てて、新たに数値を書き換えることに該当します。
計算結果を捨てるのは勿体無い気がしますが、物理的な計算を極力やらせないようにすればOKです。物理的な計算は当たり判定さえ取れてればいいので、余計な事をしなければ大丈夫です。
計算結果は捨て去られますが当たり判定の利用はできるのでちょうどいい感じだと言えます。
スクリプトからキャラクターに速度を与えよう
では、左ボタンか右ボタンを押された時velocityを変更して速度を与えてあげましょう。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Player : MonoBehaviour { //インスペクターで設定する public float speed; //プライベート変数 private Animator anim = null; private Rigidbody2D rb = null; void Start() { //コンポーネントのインスタンスを捕まえる anim = GetComponent<Animator>(); rb = GetComponent<Rigidbody2D>(); } void Update() { //キー入力されたら行動する float horizontalKey = Input.GetAxis("Horizontal"); float xSpeed = 0.0f; if (horizontalKey > 0) { transform.localScale = new Vector3(1, 1, 1); anim.SetBool("run", true); xSpeed = speed; } else if (horizontalKey < 0) { transform.localScale = new Vector3(-1, 1, 1); anim.SetBool("run", true); xSpeed = -speed; } else { anim.SetBool("run", false); xSpeed = 0.0f; } rb.velocity = new Vector2(xSpeed, rb.velocity.y); } }
さて、↑のようなスクリプトを書きました。
public float speed;
で、インスペクターに走る速さを調節できるようにします。
↓のようにインスペクターに数字が出ていれば走る速さを調整しやすいです。
float xSpeed = 0.0f;
この変数は横方向の速さを表します。プラスなら右方向、マイナスなら左方向になります。
xSpeed = speed;
これで、インスペクターで調節したスピードを入れます。
xSpeed = -speed;
反対方向はマイナスをつけて上げる事で表現できます。
右ボタンも左ボタンも押されていない時、止まって欲しいので
xSpeed = 0.0f;
で、X軸の速度を0にします。
そして最終的に算出された速度を
rb.velocity = new Vector2(xSpeed, rb.velocity.y);
でvelocity(速さ)代入しています。
この時、上に上昇したり、下に落ちたりする速度は維持したいので、velocityのY軸方向の速さをそのまま代入しています。
なぜ、このように回りくどいやり方をするかと言うと、途中でスピードを変更しやすいからです。後々、横方向のスピードを変化させる処理が出てきた時(暴風が吹いているなど)に処理を追加しやすいです。
そのため、一旦変数に入れて、最後に代入するという手法を取っています。
(余談)ゆっくり加速する表現
ちなみに余談ですが、
xSpeed = Input.GetAxis("Horizontal") * speed;
で移動を表現することも可能です。この場合はゆっくり加速しながら移動します。今回はパッと移動して欲しいのでこの手法は見送りました。こちらがお好みの方はこの方法でもいいかなと思います。
回転を固定する
さて、では再生してみましょう。speedが0になっていると思うのでインスペクターを調節しながら動かしてみてください。
動きましたが、コケてしまいました。
そういった時は、回転しないように動きを固定しましょう。
インスペクターのRigidbody2Dを見てください。
ConstraintsのFreeze RotationのZにチェックをいれていください。これで回転しなくなります。
動かしてもコケなくなりました。これで横方向の移動は完了です。
なんか滑っているように見えますが、とりあえず適当で大丈夫です。あとで調整できるので、本番用の絵を適用したらちゃんと調整しましょう。そのためにspeedをpublicにしました。
<まとめ>
最後にvelocityを触るメリットデメリットを解説して終わりにします。
今回は簡単な2Dアクションだから、velocityを直接いじっているという事を忘れずに!
移動はそのゲームそれぞれにあった方法があるのです。
何かうまくいかない事があった場合は↓の記事を参考にしてみてください
最低限↓の動画の要件を満たしていない質問は受けかねるので、ご理解ください。
また、筆者も間違えることはありますので、何か間違っている点などありましたら、動画コメント欄にでも書いていただけるとありがたいです。
さて、今回は横方向の移動をしたので、次回はジャンプと行きたいところなんですが、その前に接地判定の取り方について解説していきたいと思います。