今回はUnityで位置や方向を求めるStatic変数について見ていきます。
方向を求めることが出来ればキャラクターの前方に弾を飛ばしたい時や、敵を倒した時に敵の上方からアイテムを登場させるといった時に使う事が出来ます。
既にいくつかの記事で方向のStatic変数については解説していますが、それだけについてを取り上げた記事を書いていなかったので書いてみました。
そもそもStatic変数ってなんなのさ?
Static変数についてしばらく説明しますが、飛ばしても大丈夫です。(^_^;)
まずはStatic変数ですが、いったいこれはなんなのでしょうか?
UnityのスクリプトリファレンスでStatic変数という表記になっていたので、この記事のタイトルにもそのまま記載しましたが、日本語に直すと静的変数ですね。
オブジェクト指向プログラミングで言うとそのクラスで定義されている変数です。
オブジェクト指向プログラムではオブジェクトをクラスという形で作成し、クラスをインスタンス化すると同じクラスから個性を持ったインスタンスを大量に生成出来ます。
例えばクラスStaticTestを作成したらフィールドやメソッド等をクラス内に書きますが、そのフィールドやメソッドにアクセスする為にStaticTestクラスをインスタンス化します。
1 2 3 | StaticTest staticTest1 = new StaticTest(); |
StaticTestクラスで静的なフィールドやメソッドとして定義されていない場合はStaticTestクラスをインスタンス化してそのインスタンスのフィールドやメソッドにアクセスする必要があります。
1 2 3 4 | StaticTest staticTest1 = new StaticTest(); staticTest1.Method(); |
ですが個々のインスタンスではなくクラス固有のフィールドやメソッドを持っておけばインスタンス化せずに直接クラスを指定して実行する事が出来ます。
1 2 3 | StaticTest.Method(); |
実際に動作を確認する為StaticTestクラスを作成します。
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 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class StaticTest { // テストの静的フィールド public static int testParam = 7; // 名前 private string name; // 値段 private int price; public StaticTest(string name = "default", int price = 100000) { this.name = name; this.price = price; } public static string StaticMethod() { return "静的メソッドの実行"; } public void InstanceMethod() { Debug.Log("通常のインスタンスメソッド"); } public string GetName() { return name; } public int GetPrice() { return price; } } |
このStaticTestクラスをインスタンス化してインスタンスメソッドの実行と静的メソッドの実行をしてみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class InstantiateStaticTest : MonoBehaviour { // Use this for initialization void Start () { StaticTest staticTest1 = new StaticTest(); StaticTest staticTest2 = new StaticTest("名前", 50); Debug.Log("staticTest1" + staticTest1.GetName() + ":" + staticTest1.GetPrice()); Debug.Log("staticTest2" + staticTest2.GetName() + ":" + staticTest2.GetPrice()); Debug.Log(StaticTest.testParam); Debug.Log(StaticTest.StaticMethod()); } } |
GetNameやGetPriceはインスタンスフィールドの値を返すインスタンスメソッドの実行で、StaticMethodはクラスに保持されているクラスメソッド(静的メソッド)の実行になります。
この例でいくとtestParamが静的変数にあたります。
ここまで説明しておいてなんなんですが・・・・、プログラミングに関しての用語等は書籍や専門サイトを参照して確かな情報を仕入れてください・・・・(^_^;)
Vector3とTransformのStatic変数について
Vector3とTransformのStatic変数を見ていきましょう。
Vector3のStatic変数
Vector3のStatic変数についていくつか見ていきます。
これらの事からVector3のStatic変数はVector3の値を変数で表したものにすぎないということですね。
なので
1 2 3 4 | transform.position += Vector3.forward; transform.position += new Vectorr3(0, 0, 1); |
上記のスクリプトは同じ事をしています。
TransformのStatic変数
TransformのStatic変数についていくつか見ていきましょう。
transformだけでスクリプトを取り付けたゲームオブジェクトのTransformコンポーネントを取得出来ます。
そのゲームオブジェクトのTransformのforward、up、rightなのでそのゲームオブジェクトのローカル軸のその方向を取得する事が出来ます。
シーンビューでオブジェクトのローカル座標を表示するやり方は
の記事の「オブジェクトの操作」項目のところで取り上げているGlobalとLocalボタンの切り替えで出来ます。
Vector3と違ってback、down、leftがありません。
しかし全然問題はなくスクリプトでtransform.forwardの逆方向を使いたい時は
1 2 3 | transform.position += -transform.forward; |
と変数の前にマイナスを付けて逆方向にしてあげるだけです。
up、rightの逆方向を求める時も同じように出来ます。
Vector3とtransformの違い
Vector3とTransformでは同じStatic変数をいくつか持っていますが意味合いが少し違ってきます。
Vector3はUnityの世界での軸(ワールド軸)での絶対的な方向を取得するのに対してTransformはそのゲームオブジェクトの軸(ローカル軸)の方向を取得出来ます。
キャラクターのゲームオブジェクトに以下のスクリプトを取り付けて実行してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class DirectionTest : MonoBehaviour { // Use this for initialization void Start () { Debug.Log("Vector3.forward" + Vector3.forward); Debug.Log("Vector3.up" + Vector3.up); Debug.Log("Vector3.right" + Vector3.right); Debug.Log("transform.forward" + transform.forward); Debug.Log("transform.up" + transform.up); Debug.Log("transform.right" + transform.right); } } |
Vector3とTransformの違いがわかりやすいように比較をしてみます。
キャラクターにはスタンダードアセットのEthanのモデルを使用して確認します。
キャラクターのRotationを0にした時
まずはキャラクターのTransformのRotationのX、Y、Zを0にした時です。
上のようにキャラクターの向いている向き、上方等がワールド軸とローカル軸で一致しています(Ethanを使った場合)。
Unityエディターでプレイボタンを押して確認してみるとコンソールに、
上のように表示され同じVector3の値が表示されているのがわかります。
キャラクターをX軸で90度回転した時
次はキャラクターをX軸で90度回転し、以下のような状態にします。
先ほどとは違ってキャラクターが地面の方向を向いたのでローカル軸の青軸(ローカル軸の前方)が地面の方向を向き、キャラクターの上方はワールド軸の青軸(ワールド軸の前方)を向くようになりました。
この状態でUnityを実行すると、
上のようにtransform.forwardの値がVector3(0, -1, 0)、transform.upの値がVector3(0, 0, 1)となりました。
使い方によってはVector3とTransformは同じ値を返しますが、使い方を間違えると何故かうまく出来ない!ということになるので注意が必要ですね。
Vector3とTransformの違いを画像で確認
Vector3とTransformのStatic変数の方向の違いを見てきたので、画像で確認します。
方向については上のような方向を表しています(矢印の大きさや表示している位置はいいかげんで、そちらの方向を指していることを表しただけです)。
キャラクターの上向きの角度を変えるとtransform.upの方向も変わってきます。
斜め方向も取得してみる
ここまでで上下左右の方向を取得する事が出来ました。
そこで右斜め前方とか左斜め後方とか斜め方向を取得できるようにしていきます。
方向はベクトルなのでキャラクターの右斜め前方を取得するにはキャラクターの前方とキャラクタの右方のベクトルを足してやれば右斜め前方のベクトルが取得出来ます。
つまり
1 2 3 | 右斜め前方のベクトル = transform.forward + transform.right; |
で計算出来ます。
キャラクターのEthanに以下のスクリプトを取り付け確認してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class DirectionTest2 : MonoBehaviour { // Use this for initialization void Start () { Debug.Log("transform.forward" + transform.forward); Debug.Log("transform.right" + transform.right); Debug.Log("右斜め前方のベクトル" + (transform.forward + transform.right)); // それぞれのベクトルを視覚化 Debug.DrawRay(transform.position, transform.forward, Color.blue, Mathf.Infinity); Debug.DrawRay(transform.position, transform.right, Color.red, Mathf.Infinity); Debug.DrawRay(transform.position, transform.forward + transform.right, new Color(1f, 0f, 1f), Mathf.Infinity); } } |
キャラクターの前方とキャラクターの右方のベクトルを足して右斜め前方のベクトルを求めています。
シーンビューで確認しやすいようにそれぞれのベクトルを視覚化しているのがDebug.DrawRayの処理です。
キャラクターの前方を青色、右方を赤色、右斜め前方のベクトルをピンク色にしています。
左側がキャラクターのTransformのRotationのX、Y、Z軸を0にしたもので、右側がY軸を45にしたものです。
キャラクターの右斜め前方のベクトルを計算出来ているのがわかります。
ベクトルを足しただけじゃだめ?
キャラクターの右斜め前方のベクトルはキャラクターの前方と右方のベクトルを足すことで計算出来ました。
しかし計算した右斜め前方のベクトルをそのまま使ってキャラクターの右斜め前に歩かせるスクリプトを作成すると、右斜め前に移動する時だけ歩くスピードが速くなります。
それは前方と右方のベクトルの長さも右斜め前方方向のベクトルに足されてしまっているからです。
先ほどのtransform.forwardとtransform.rightのベクトルを足して求めたピンク色のベクトルの長さは1.414214となります(三平方の定理で斜辺は√2なので)。
移動処理を作る場合は長さが1の方向ベクトルに歩く速さをかけてキャラクターの移動力を計算します。
それは前進や横に移動する時のベクトルの長さは1なので斜め方向に移動する場合も1にしなければなりません。
その為、計算した右斜め前方のベクトルを正規化しベクトルの長さを1にする必要があります。
ベクトルの正規化
ベクトルは長さと方向を持っています。
そのベクトルの長さを1にすることをベクトルの正規化と言います。
長さが1のベクトルは単位ベクトルと呼ばれるみたいですね。(´Д`)
Unityではベクトルの正規化は簡単に計算をする事が出来て、
1 2 3 | 正規化した右斜め前方のベクトル = (transform.forward + transform.right).normalized |
上のように.normalizedを付けるだけです。
.normalizedは元の値を書き換えませんが、Normalizeメソッドを使用すると、
1 2 3 4 5 6 | Vector3 direction = transform.forward + transform.right; Debug.Log(direction); direction.Normalize(); Debug.Log(direction); |
direction変数の値自体を書き換えますので注意が必要です。
新しくスクリプトを作成しキャラクターに取り付けて確認してみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class DirectionTest3 : MonoBehaviour { // Use this for initialization void Start () { Debug.Log("transform.forward" + transform.forward); Debug.Log("transform.right" + transform.right); Debug.Log("右斜め前方のベクトル" + (transform.forward + transform.right).normalized); // それぞれのベクトルを視覚化 Debug.DrawRay(transform.position, transform.forward, Color.blue, Mathf.Infinity); Debug.DrawRay(transform.position, transform.right, Color.red, Mathf.Infinity); Debug.DrawRay(transform.position, (transform.forward + transform.right).normalized, new Color(1f, 0f, 1f), Mathf.Infinity); } } |
右斜め前方のベクトルに.normalizedを付けてベクトルの正規化を行っています。
上のように右斜め前方の長さ1のベクトルが求められるのでキャラクターの移動スクリプトではこのベクトルに歩くスピードをかけて移動させてあげるという感じになります。
ちなみにベクトルの長さを求めたい時は
1 2 3 | Debug.Log((transform.forward + transform.right).magnitude); |
上のように.magnitudeを付けてあげるとそのベクトルの長さを求めることが出来ます。
この.magnitudeを付けたスクリプトはキャラクターの移動スクリプトの記事で使っていて、キャラクターの速度ベクトルの長さを求めていました。
キャラクターの移動スクリプトは現在のキャラクターの位置にキーボードを押した方向を足してそのベクトルの正規化をし、transform.LookAtメソッドでその方向にキャラクターを向かせた後、キャラクターの前方に歩くスピードをかけるというような感じで作成します。
斜め方向はキーボードやゲームパッドの方向キーで取得出来るのでtransform.forwardやtransform.rightを足して斜め方向を計算するということはしなくて済みます。
自分で操作するゲームオブジェクト(キャラクター)以外のゲームオブジェクトの移動をさせたい時等に使えるかもしれません。
終わりに
今回はVector3とTransformのStatic変数のいくつかについて見ていきました。
transform.forwardやtransform.up等を使えばゲームオブジェクトから見た方向を計算する事が出来、それらの変数を使って斜め方向も計算する事が出来ました。
今回は右斜め前方だけを計算してみましたが他の斜め方向も同じように計算出来ます。
上の記事のようなキャラクターから衝撃波を飛ばして攻撃するような機能を作成する時に斜め方向も計算出来ると攻撃バリエーションが増えますね!
と言ったところで本日はお開きとなります。(._.)