【初心者Unity】オブジェクトをキー入力で移動させる方法
▶
【Unity】3Dアクションゲームを作ろう!#7 ステージの作成(Skybox・落下判定)
▶
【Unity】3Dアクションゲームを作ろう!#8 ステージの作成(スイッチ・扉)
▶
【Unity】3Dアクションゲームを作ろう!#9 プレイヤーのHP管理
▶
【初心者Unity】JsonUtilityクラスでJSONを扱う方法
▶
【初心者Unity】スクリプトからコンポーネントを追加する方法
1.はじめに
本記事では初心者向けに、キー入力でオブジェクトを移動させる方法を紹介します。
Unity においてオブジェクトを移動させる方法はさまざまありますが、今回は下記の2パターンを紹介します。
① transform.position で移動
② Rigidbody で移動
移動はいずれの方法も、下記のキー操作により行えるように作成します。
Wキー | 前方移動 |
---|---|
Sキー | 後方移動 |
Dキー | 右移動 |
Aキー | 左移動 |
2.準備
新規シーンを作って床(3D Object > Plane)とカプセル(3D Object > Capsule)を配置しておきます。
床は見分けがつくようにマテリアルで色を変更していますが、特に変える必要はありません。
カプセルの名前を「Player」とします。
スクリプトを1つ作成して Player にアタッチしておきます。
ここでは PlayerScript という名前で作成しました。
(関連記事)
UnityとC#の連携方法
さらに Rigidbodyコンポーネントを追加しておきます。
勝手に回転したり倒れたりしないように、Rigidbodyコンポーネントの Constraints で回転を制限しておきます。
(関連記事)
はじめに知っておくべきRigidbodyコンポーネントの概要
Rigidbody – Unity マニュアル(公式)
3.transform.position で移動させる方法
using UnityEngine; public class PlayerScript : MonoBehaviour { float speed = 3.0f; void Update() { // Wキー(前方移動) if (Input.GetKey(KeyCode.W)) { transform.position += speed * transform.forward * Time.deltaTime; } // Sキー(後方移動) if (Input.GetKey(KeyCode.S)) { transform.position -= speed * transform.forward * Time.deltaTime; } // Dキー(右移動) if (Input.GetKey(KeyCode.D)) { transform.position += speed * transform.right * Time.deltaTime; } // Aキー(左移動) if (Input.GetKey(KeyCode.A)) { transform.position -= speed * transform.right * Time.deltaTime; } } }
シーンを再生して、WASDキーで前後左右に動けることを確認しましょう。
WASD の入力は「Input.GetKey」を使って判定しています。
入力に対応するように、transform.position に対して前後左右方向への変更を加えています。
transform.position は現在のプレイヤーの3D座標を表す Vector3型のデータです。
tranform.forward はプレイヤーから見た正面方向の単位ベクトルなので、Wキーを押している間は前方への移動、Sキーを押している間は後方への移動が行われています。
左右についても同様で、tranform.right はプレイヤーから見た右方向の単位ベクトルなので、Dキーを押している間は右への移動、Aキーを押している間は左への移動が行われています。
特に必須ではありませんが、speed の値を変えることで移動速度が変更できます。
(関連記事)
はじめに知っておくべきTransformコンポーネントの概要
オブジェクトの向き(ベクトル)の取得
キーの入力を判定しよう!(GetKey)
Time.deltaTimeの基本的な使い方
4.Rigidbody で移動させる方法
using UnityEngine; public class PlayerScript : MonoBehaviour { Rigidbody rb; float speed = 3.0f; void Start() { rb = GetComponent<Rigidbody>(); } void Update() { // Wキー(前方移動) if (Input.GetKey(KeyCode.W)) { rb.velocity = transform.forward * speed; } // Sキー(後方移動) if (Input.GetKey(KeyCode.S)) { rb.velocity = - transform.forward * speed; } // Dキー(右移動) if (Input.GetKey(KeyCode.D)) { rb.velocity = transform.right * speed; } // Aキー(左移動) if (Input.GetKey(KeyCode.A)) { rb.velocity = -transform.right * speed; } } }
シーンを再して、WASDキーで前後左右に動けることを確認しましょう。
transform.position で作成したときと同様に動けているはずです。
Rigidbody を使った移動方法の中でもいくつか実装方法がありますが、今回紹介したのは速度(velocity)を直接書き換える方法です。
このほかには力を加えるメソッド(AddForce)を使って移動させる方法が一般的に使われます。
今回紹介した velocity を書き換える方法はコードの記述は非常に楽で、簡単に等速運動が実現できるといったメリットがある一方、
たとえば重力などの他の物理演算と組み合わせる場合には実装が少し難しくなるといった問題もあります。
まずはvelocityから使い始めて、用途に合わせてAddForceなども使いこなせると、より実装の幅が広がると思います。
5.transform.position と Rigidbody、どっちを使えばいいの?
今回2つの実装方法を紹介しましたが、どちらも動きはほとんど同じでソースコードの記述量も大差がなく、どちらを使えばいいのか疑問に思われるかもしれません。
先に結論を言ってしまうと、「正解はない」と言えるのですが、ここでは使い分けの基準となる違いを1つ紹介します。
まずは transform.position の場合、speed の値が 3.0f などあまり大きくない値の時には特に問題が無いのですが、
20.0f など値を大きくしたうえで壁に衝突してみるとどうなるでしょうか。
これはよくよく考えてみると当たり前とも言えるのですが、transform.position での実装の場合は、オブジェクトの位置を書き換えることで移動を実現しています。
1フレームごとにワープをしていると言い換えることもできます。
speed の値が小さいうちは1フレームで壁をすり抜けるほどの移動ではないので問題となりませんが、
値が大きくなって1フレームで壁の先の位置まで移動するような状況では、このようなすり抜けが発生します。
ではRigidbody の場合はどうでしょうか。
実はこちらも、speed の値を極端に大きくするとすり抜けが発生することはあるのですが、
transform.position と比べるとその限界値はかなり余裕があるはずです。
これは、位置の書き換えではなく速度の書き換えであるという仕組みの違いを意識するとイメージしやすいかと思います。
transform.position がワープのような移動であったのに対して、速度の書き換えは現実世界に近い連続的な移動と言えます。
ただし、上述の通り speed を大きくしすぎるとすり抜けてしまうのは、やはり現実世界とプログラムの世界の違いとも言えるところです。
では完ぺきな対処方法がないのか?と気になるかと思います。
完ぺきと言い切るのは恐れ多いのでここでは避けますが、下記のような対応を行うことでよほどのことが無い限りすり抜けを回避することが可能です。
Rigidbody の Collision Detection を Continuous に設定する。
こちらの設定であれば、speed を上げてもすり抜けることはなくなるはずです。
(transform.positionの場合は相変わらずすり抜けます。)
まとめると使い分けの1つの基準として、
かなり移動速度が速かったり薄い壁を扱いたいケースですり抜けが気になる場合には transform.position は避け、Rigidbody や他の方法で実装した方が良いことが多い、と言えます。
(関連記事)
Rigidbody(物理演算)の使い方①
6.おわりに
キー入力と移動の組み合わせの初歩的な実装を紹介しました。
それぞれが非常に大事な機能であり、今回のような組み合わせもよくあるケースであると思いますので、
超基礎的な実装パターンとして覚えておくときっと役に立つと思います。