Skip to main content

線型代数学

ベクトル

 ベクトルとは、一般的には「大きさと向きをもつ量」であり「矢印で表すことのできる量」である。

image.png

 2次元のベクトルは2つの成分(x, y)があり、3次元の場合は3の成分(x, y, z)がある。Unityでは Vector2Vector3 の構造体があり、それぞれのベクトルを作成ができる。

Vector2 vec2D = new Vector2(vx, vy);     // vxとvyはfloatである
Vector3 vec3D = new Vector3(vx, vy, vz); // vzもfloatである

 ゲームの中で、ベクトルをよく使うものである。例えば、3D空間で、キャラクターの速度が「左向き:秒速2メートル、前方:秒速10メートル」であるとしたら…

// 3D空間だと、x:左右、y:上下、z:前後
Vector3 velocity = new Vector3(-2, 0, 10);

 また、ゲームの場合は、向きと量はもちろんそうであるが、その他に「位置」もベクトルで表現する。

// プレーヤーはシーンの中心点から前方100メートルにいる
Vector3 playerPosition = new Vector3(0, 0, 100);

演算

 やはり、ベクトルは重要なので、ベクトルの演算を学ばないといけない。

加算

 2つのベクトルを足すことができる。その結果は各成分の足し算である:

2次元の場合

image.png

3次元の場合

image.png

Unityでは「+」の演算子で2つのベクトルを足すことができる

Vector3 v = new Vector3(vx, vy, vz); // vx, vyとvzは何かのfloat型の数値
Vector3 w = new Vector3(wx, wy, wz); // wx, wyとwzは何かのfloat型の数値
Vector3 vw = v + w;

 幾何学的に、足し算は2つの矢印を連続し、1つ目のベクトルの始点から2つ目のベクトルの終点までに新しいベクトルを作成するイメージ:

image.png

減算

 引き算は同じ形で行う:

image.png

Vector3 v = new Vector3(vx, vy, vz); // vx, vyとvzは何かのfloat型の数値
Vector3 w = new Vector3(wx, wy, wz); // wx, wyとwzは何かのfloat型の数値
Vector3 vw = v - w;

 幾何学的には、引くベクトルの向きを反転してから連続に繋がる

image.png

練習

加算と減算を使い、2つのオブジェクトを相対的に移動

  • シーンに2つの3Dゲームオブジェクトを追加し、距離的に放してください。
    • 1つ目の名前は「Control」
    • 2つ目の名前は「Follower」
  • スクリプト「VectorTest」を作成し…
    • Follower と Control の相対位置を求める(減算)
    • Control をシーン内で動かしたら、Follower が移動を真似する(加算)
スクリプト:VectorTest
// ベクトル演算の練習」
public class VectorTest : MonoBehaviour
{
    // 移動すべきのオブジェクト
    [SerializeField] private GameObject control;

    // controlを追跡するオブジェクト
    [SerializeField] private GameObject follower;

    // controlとfollowerの相対位置
    private Vector3 relativePosition;
    
    private void Start()
    {
        // 初期位置を取得
        var v = control.transform.position;
        var w = follower.transform.position;
        
        // 相対位置を計算(減算で)
        relativePosition = w - v;
    }

    private void Update()
    {
        // followerの位置が、controlの位置 + 相対位置
        follower.transform.position = 
            control.transform.position + relativePosition;
    }
}

実数との掛け算、割り算

 ベクトルを実数との掛け算ができる。各成分との掛け算になる:

image.png

Unityでは、普通の掛け算演算子「*」で計算できる:

Vector3 v = new Vector3(vx, vy, vz); // vx, vyとvzは何かのfloat型の数値
Vector3 nv = v * n;                  // nは何かの実数(float)である

練習

先ほど作った「VectorTest」を更新し、相対位置の「倍率(scale)」を Inspector で変えられるようにする。

スクリプト:VectorTest
// ベクトル演算の練習」
public class VectorTest : MonoBehaviour
{
    // 移動すべきのオブジェクト
    [SerializeField] private GameObject control;

    // controlを追跡するオブジェクト
    [SerializeField] private GameObject follower;

    // 相対位置の倍率
    [SerializeField] private float scale = 1;
    
    // controlとfollowerの相対位置
    private Vector3 relativePosition;
    
    private void Start()
    {
        // 初期位置を取得
        var v = control.transform.position;
        var w = follower.transform.position;
        
        // 相対位置を計算(減算で)
        relativePosition = w - v;
    }

    private void Update()
    {
        // 倍率を適用する
        Vector3 scalePos = relativePosition * scale;
        
        // followerの位置が、controlの位置 + 相対位置
        follower.transform.position = 
            control.transform.position + scalePos;
    }
}

ベクトルの長さ

場合により、ベクトルの長さを求めたいときもある。これには「ピタゴラスの定理」(三平方の定理)を利用する。ピタゴラスの定理を思い出しましょう。直角三角形があれば…

image.png

c 辺の長さは:

image.png

つまり…

image.png

これはベクトルの成分とよく似ているので:

image.png

これができる!

image.png

ここで || v || はベクトルの「ノルム」といわれ、ベクトルの長さを表している。Unityでは、Vectorの便利な「magnitude」がある:

Vector2 p = new Vector2(3, 4);
float len = p.magnitude; // len は p の長さ(5)である

単位ベクトル

 ベクトルの長さを「1」にすると、そのベクトルは単位円の中に入り、各成分がsinとcosを一致させることができる。その他にも、ベクトルの長さを調整する前に、まず長さを1に戻さないといけないが必要である。その場合は単位ベクトルを求めないといけない。

image.png

 単位ベクトルを求めるのは、ベクトルをノルムで割るだけ:

image.png

Unityでは「normalized」というプロパティーで単位ベクトルを求めることができる。

// 任意の長さのベクトル v があるとして:
Vector3 v = new Vector3(30, 12, -15);

// uはvと同じ向き(方向)であるが、長さは「1」である(単位ベクトル)
Vector3 u = v.normalized;

ベクトル同士の”掛け算”(内積と外積)

 ベクトル同士の掛け算はできないが、2つの特別な関数がある。内積(dot product)と外積(cross product)。それぞれは限られた使い方があるが、必要な時にとても便利である。

内積

 内積の定義は以下の通りになる:

image.png

image.png

 ある2つのベクトル「v」と「w」の内積は、それぞれのベクトルのノルム(長さ)とその2つのベクトルの間の角度のコサインの掛け算である。答えは実数である。

 これは何に便利かというと、2つのベクトルの間の角度を求めたいなら、内積を使えば良いでしょう。まず、vとwを単位ベクトルにすると、長さは1になるので、式は単純になる:

image.png

そして、角度を求めるのは:

image.png

Unityで内積の計算を実装されている:

 

Vector3 v = new Vector3(vx, vy, vz);
Vector3 w = new Vector3(wx, wy, wz);
float dot = Vector3.Dot(v, w); // 内積

// 角度を求めたいなら
float radians = Mathf.Acos(dot);

// そして
float angle = radians * Mathf.Rad2Deg;
練習

警備員の視野を利用するゲーム

  • 警備員は「視野」があり、前方±角度しか見えない
  • 泥棒が視野の中に入ってれば→発見!
警備員のスクリプト
// 警備員の前に泥棒がいるかいないか確認する
public class GuardMan : MonoBehaviour
{
    // 泥棒への向きが欲しいので、泥棒が必要
    [SerializeField]
    private GameObject thief;
    
    // 視野(度)
    [SerializeField]
    private float viewAngle;

    // 色を変えるため
    private MeshRenderer rend;
    
    private void Start()
    {
	    rend = GetComponent<MeshRenderer>();
    }

    private void Update()
    {
        // 警備員の前向きのベクトル
        Vector3 v = transform.forward; // 青い矢印のこと

        // 泥棒への向き
        Vector3 w = thief.transform.position - transform.position;

        // 単位ベクトルにする
        w.Normalize();

        // 内積で角度のコサインを求める
        float dot = Vector3.Dot(v, w);
        
        // 内積からラジアン角度を求める
        float radian = Mathf.Acos(dot);
        
        // 度に変換
        float angle = radian * Mathf.Rad2Deg;
        
        // 警備員と泥棒の角度が視野よりも小さければ、発見!
        if (angle < viewAngle)
            rend.material.color = Color.red;
        
        // 見えない
        else
            rend.material.color = Color.green;
    }
}

 

外積

2つのベクトル外積の式は以下の通りである:

image.png

そして、Unityでも計算を実装されている:

Vector3 v = new Vector3(vx, vy, vz);
Vector3 w = new Vector3(wx, wy, wz);
Vector3 cross  = Vector3.Cross(v, w); // 外積の結果は新しいベクトルである

 式よりも、外積の幾何学的な意味は重要。2つの3Dベクトル「v」と「w」は平面を定義すし、それで「v」と「w」の外積の結果は:

  • 新しい3Dベクトルである
  • このベクトル「v」と「w」で定義された平面を直交する
  • また、長さは

image.png

image.png

従って、2つのベクトルがあれば、そのベクトルで作られた「面」を直交するベクトルを求めることができる。面の「法線ベクトル」、または3D空間の傾きの方向を求めることができる!

行列