2018/12/17 バージョン:Unity2018.1
こんにちは、ちきなるです。
今回は、Unityでオブジェクトを移動させる方法をまとめようと思います。
普段私が使っている方法をまとめるので、抜けていることもあるかもしれませんが、ご了承ください。
オブジェクトを移動させる方法
Unityでオブジェクトを移動させるには、大きく2つの方法があります。
1つは、直接座標を変更する方法。
1つは、オブジェクトに力を加えて移動させる方法。
2つの方法を使ってオブジェクトを移動させます。
座標の変更による移動
オブジェクトは、transform(トランスフォーム)クラスを持っています。
transformの中には、様々な情報が格納されています。
※位置、回転、大きさなどを持っています。
その中にオブジェクトの現在の座標を示す、position(ポジション)という変数を持っています。
このpositionを変更することでオブジェクトを移動させることができます。
this.transform.position = new Vector3(1, 1, 1);
このように特定の場所へ瞬時に移動させることができます。
ですが、このままでは瞬間移動にしか見えません。
なので、もっと短い距離を連続で移動させることで、オブジェクトが移動しているように見せることができます。
そういう時に便利な関数が、
transform.Translate
です。
これは、Vectorを与えると、現在位置に与えたVectorを足した位置に移動します。
例えば、現在位置(1, 1, 1)のオブジェクトに(1, 0, 0)を与えると
(2, 1, 1)に移動します。
これを使うことで、連続してオブジェクトの移動を行うことができます。
private void FixedUpdate() { this.transform.Translate(Vector3.right * 0.1f); }
this:スクリプトが付いているオブジェクト
transform:座標などの情報を持っているクラス
Translate:引数が(Vector3)の関数。現在位置に引数で受け取ったVector3を足した位置に移動する
Vector3.right:右方向のベクトル(1, 0, 0)他にもleftとかup、downなどがあります。
こんな感じで、移動しているように見せることができます。
これで、オブジェクトを移動させることができます。が、欠点もあります。
柱にぶつかった時、跳ね返るようにしたい時、Translateで移動させると、下の動画の様に停止してしまいます。
※多分柱に食い込んで戻ってを繰り返している?正直予想外でした。壁の中を突き進むと思っていました。
こんな感じで、Translateでの移動は、物理演算と相性が良くありません。
物理演算の影響下に置かないオブジェクト(Rigidbodyを付けないオブジェクト)の移動には、問題ないが、物理演算の影響を受けるオブジェクトでは、使用しない方が良いです。
では、どうやって物理演算の影響下で移動を行うかというと、Rigidbodyを使います。
※Rigidbodyを付けたオブジェクトのpositionを変更したい場合は、Rigidbody.positionで変更するようにしましょう。
まとめ
オブジェクトのtransformクラスのpositionの値を変更するとオブジェクトの位置を変更することができます。
this.transform.position = new Vector3(1f, 1f, 1f);
又は
Vector3 vec3; vec3.x = 1f; vec3.y = 1f; vec3.z = 1f; this.transform.position = vec3;
※transform.positionは、個別にx,y,zの値を変更することができません。一度Vector3変数に値を入れてから、Vector3変数をtransform.positionに代入する事で個別に値を変更することができます。
Vector3 vec3; vec3 = this.transform.position; vec3.x = 1f; this.transform.position = vec3;
現在位置から特定の方向へ移動させたい場合は、transform.Translateを使用すると、移動できます。
this.transform.Translate(Vector3.right * 0.1f);
Translateは、オブジェクトの現在位置に、引数で渡したVector分移動してくれる関数です。
右方向に進み続けるなど、連続して移動させたい時に便利です。
物理演算を行う必要が無いオブジェクトに使用する。
もし、Rigidbodyを追加したオブジェクトを移動させたい時は、transform.positionではなく、Rigidbodyのpositionで変更する。
Unity公式のスクリプトリファレンス
力を加えての移動
オブジェクトにRigidbody(リジッドボディ)を追加することで、力を加えて移動させることができます。
オブジェクトを選択した状態で、
Inspectorビューの
Add Component→Physics→Rigidbody
で追加することができます。
※2Dの場合は、Physics 2D→Rigidbody 2Dです。
Rigidbodyは様々な機能が用意されています。今回は、私が良く使う
AddForce
Velocity
について書いていこうと思います。
AddForce
AddForceは、オブジェクトに力を加えて移動させます。
Rigidbody rgd; rgd = this.GetComponent<Rigidbody>(); rgd.AddForce(10f, 0, 0);
Rigidbodyは、オブジェクトに追加するので、一度
GetComponentでRigidbodyを取得してから使います。
※GetComponentは重いので、何度も使う場合は、Start関数などで変数に入れるのが良い。
後は、RigidbodyのAddForceを使えば移動できます。
基本的に、FixedUpdate関数の中で、継続的に呼びことで、加速させるように使います。
※AddForceを呼び出す毎に追加で力が加えられるので、徐々に加速していきます。
GetComponent:オブジェクトに設定されているコンポーネントを取得します。存在しない場合は、nullを返します。
Start:スクリプトが有効な時、初めて呼び出されるUpdateの前に呼び出される関数。スクリプトの存続期間内に1度だけ呼び出されます。
ざっくりいうと、オブジェクトが有効な時、初めに1度だけ動く関数。
FixedUpdate:固定フレイムレートで呼び出される関数。Rigidbodyを使う場合は、Updateでは無くFixedUpdate内で使用する。
Unity公式のスクリプトリファレンス
using UnityEngine; public class Move : MonoBehaviour { private Rigidbody rgd; private void Start() { rgd = this.GetComponent<Rigidbody>(); } private void FixedUpdate() { rgd.AddForce(10f, 0, 0); } }
ForceModeについて
AddForceでは、ForceModeで力の加え方を変更することができます。
・Force
・Impulse
・Acceleration
・VelocityChange
上の4つのForceModeがあります。
下の2つ、AccelerationとVelocityChangeは、私は使ったことが無いので、今回は省略します。
ざっくりいうと、質量を無視して力を加えられます。大質量なオブジェクトを高速で移動させる時などに使用するそうです。
Force
オブジェクトに対して第一引数で指定した力を加えるモード。
細かく言及すると、加速度だの質量だのややこしくなって良くわからないので、ざっくりいうと、
第一引数で指定された力でオブジェクトを押すと思って下さい。
オブジェクトを動かしたい時は、とりあえずこれを使っていれば、大体何とかなります。
Impulse
インスタントフォースインパルスをオブジェクトに追加します。
※かっこいいから言ってみただけです。
Forceより難しいので、ざっくり説明すると、
瞬間的に力を加えて、Forceより素早く加速できます。
一気に加速するので、吹き飛ばす時などに使用します。
使いどころが難しいですが、勢いよく吹き飛ばしたい時に使います。
私は、ジャンプさせる時などに使っています。
まとめ
オブジェクトを自然な形で移動させたい時は、AddForceを使用すると良い。
通常は、ForceModeをForceで使用し、
瞬間的に移動(吹き飛ばす)したい時は、ForceModeをImpulseにすると良い。
Unity公式のスクリプトリファレンス
Velocity
Velocityは、オブジェクトの速度を保持している変数です。
Velocityを変更することで、オブジェクトの速度を直接書き換えることができます。
非現実的な動きになりやすいので、直接変更することは推奨されていません。
私は、物理的な抵抗を無視して一定速度で移動させたい時や、オブジェクトを確実に停止させたい時などに使用しています。特に停止させたい時は、一旦Velocityを(0, 0, 0)に変更して停止させたりします。
private Rigidbody rgd; private void Start() { rgd = this.GetComponent<Rigidbody>(); } private void FixedUpdate() { rgd.Velocity = Vector3.right * 10f; } }
停止させたい時は、
rgd.Velocity = Vector3.zero;
まとめ
Velocityは、オブジェクトの速度を保持した変数なので、値を変更すると、直接オブジェクトの速度を変更することができます。
オブジェクトを停止させる時に便利です。
その他、一定の高さでジャンプさせたい時などに使えます。
※ボタンを押した時間でジャンプの高さを変更させたい時などには、使いにくいかもしれません。
Unity公式のスクリプトリファレンス
おまけ
Unity 1週間ゲームジャムで、アクションゲームを作った時に書いたスクリプトを貼っておきます。
2Dゲームなので、Rigidbody 2Dを使用しています。
※アニメーションの切り替え部分は削除しています。ややこしくなりそうなので。
見る場合は、参考程度に考えてください。
短時間で書いたので、もっと良い書き方や、気づいていない不具合があるかもしれないので、ご利用は計画的に。
後、変数名は気にしないで下さい。一人で作っていると雑になってしまいます。
using UnityEngine; public class Player : MonoBehaviour { public float fForce = 10f; private bool bMove = false; //ジャンプの頂点フラグ private bool bUp = false; //ジャンプフラグ private bool bJump = false; //ジャンプ時間 private float fJtime; //Rigidbody2D private Rigidbody2D rgd; // Use this for initialization void Start() { //Rigidbody 2Dの取得 rgd = this.GetComponent<Rigidbody2D>(); } private void FixedUpdate() { bool bM = false; //→キーを押した時 if (Input.GetKey(KeyCode.RightArrow)) { bM = true; //キャラクターを右方向に向ける transform.localRotation = Quaternion.Euler (new Vector3(0, 0, transform.rotation.eulerAngles.z)); } //←キーを押された時 else if (Input.GetKey(KeyCode.LeftArrow)) { bM = true; //キャラクターを左方向に向ける transform.localRotation = Quaternion.Euler (new Vector3(0, 180, transform.rotation.eulerAngles.z)); } if (bM) { if (bJump) { rgd.AddForce(this.transform.right * (fForce / 2)); } else { rgd.AddForce(this.transform.right * fForce); } } //↑キーを押された時かつジャンプしていない時 if (Input.GetKey(KeyCode.UpArrow) && !bUp) { //ジャンプ時間 fJtime += Time.deltaTime; //ジャンプの初め if (!bJump) { bJump = true; } //ジャンプさせる rgd.AddForce(Vector2.up, ForceMode2D.Impulse); } //↑キーが離された時又は一定以上ジャンプした時 if (Input.GetKeyUp(KeyCode.UpArrow) || fJtime > 0.1f) { fJtime = 0f; bUp = true; } //状態が切り替わった時 if (bM != bMove) { bMove = bM; //移動も対空もしていない時 if (!bM && !bJump) { rgd.velocity = Vector2.zero; } } } //地面に着地した時 private void OnCollisionEnter2D(Collision2D collision) { //足が地面に接触した時 if (collision.gameObject.tag == "ground" && (collision.otherCollider.tag == "leg")) { //ジャンプフラグをリセット bUp = false; //対空フラグをリセット bJump = false; } } }
補足
オブジェクトの回転時、transform.rotation.eulerAngles.zで、z軸を指定しているのは、z軸だけ自動で回転する(重力などで倒れる時)ので、その対策です。
RigidbodyのFreeze RotationのZにチェックを入れるのであれば、0を入れるので問題ないと思います。
補足2
一番下のOnCollisionEnter2D関数は、ジャンプ中に地面に着地したかどうかの判定をしています。オブジェクトの足元にcollider 2Dだけのゲームオブジェクトを追加して、タグをlegにして判定しています。
補足3
this.transform.rightは、x軸のプラス方向を持っている変数です。回転を考慮してくれるので、オブジェクトの正面を取得する時に便利です。
コメント