行列

記号の意味
ベクトル- 矢印付きの文字
長さ- ベクトルの大きさ
単位ベクトル- 長さ1のベクトル
内積- 2つのベクトルから数値を計算
シータ- 角度を表すギリシャ文字
余弦- 角度から比率を求める関数

概要

複数のベクトルをまとめて扱う行列という数学的構造を使って、回転・拡大縮小などの変換を効率的に表現する方法を学びます。

行列とは何か

ゲーム開発では、オブジェクトの回転・拡大縮小・複数の点の一括変換など、複雑な座標変換を効率的に扱う必要があります。これらを統一的に表現するのが行列で、複数の数を格子状に並べて「変換」の情報を持たせたものです。

2×2行列(2行2列)は次のように書きます:

[abcd]\begin{bmatrix} a & b \\ c & d \end{bmatrix}

この4つの数(a,b,c,da, b, c, d)の配置が「変換」の情報を持ちます。例えば、次の行列は「2倍に拡大する変換」を表します:

[2002]\begin{bmatrix} 2 & 0 \\ 0 & 2 \end{bmatrix}

行列の各要素が表すもの

2×2行列の各要素は、座標軸がどこに移動するかを表します:

[abcd]\begin{bmatrix} a & b \\ c & d \end{bmatrix}

  • 左列 (a,c)(a, c): 右方向 (1,0)(1, 0) がどこに移動するか
  • 右列 (b,d)(b, d): 上方向 (0,1)(0, 1) がどこに移動するか

各要素の意味を整理すると次のようになります:

要素位置意味
aa左上X軸の変換先のX成分
bb右上Y軸の変換先のX成分
cc左下X軸の変換先のY成分
dd右下Y軸の変換先のY成分

つまり、行列を見るだけで「X軸とY軸がどこに移動するか」が分かります。

具体例: 2倍拡大

[2002]\begin{bmatrix} 2 & 0 \\ 0 & 2 \end{bmatrix}

  • X軸 (1,0)(1, 0)(2,0)(2, 0) に変換(横に2倍)
  • Y軸 (0,1)(0, 1)(0,2)(0, 2) に変換(縦に2倍)

具体例: 90度回転

[0110]\begin{bmatrix} 0 & -1 \\ 1 & 0 \end{bmatrix}

  • X軸 (1,0)(1, 0)(0,1)(0, 1) に変換(上向きに)
  • Y軸 (0,1)(0, 1)(1,0)(-1, 0) に変換(左向きに)

行列の役割

行列は「ベクトルをどう変換するか」を記録します:

[abcd][xy]=[ax+bycx+dy]\begin{bmatrix} a & b \\ c & d \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix} = \begin{bmatrix} ax + by \\ cx + dy \end{bmatrix}

  • 左側: 変換行列
  • 中央: 元のベクトル
  • 右側: 変換後のベクトル

例: 2倍拡大

[2002][34]=[2×3+0×40×3+2×4]=[68]\begin{bmatrix} 2 & 0 \\ 0 & 2 \end{bmatrix} \begin{bmatrix} 3 \\ 4 \end{bmatrix} = \begin{bmatrix} 2 \times 3 + 0 \times 4 \\ 0 \times 3 + 2 \times 4 \end{bmatrix} = \begin{bmatrix} 6 \\ 8 \end{bmatrix}

(3,4)(3, 4)(6,8)(6, 8) に変換されました!

実用例

行列を使うと、回転や拡大縮小などの変換を統一的に扱えます。

オブジェクトの回転

JavaScript
// 45度回転の行列
const angle = Math.PI / 4;  // 45度(ラジアン)
const cos = Math.cos(angle);
const sin = Math.sin(angle);

const rotationMatrix = [
  [cos, -sin],
  [sin,  cos]
];

拡大縮小

JavaScript
// X方向に2倍、Y方向に3倍
const scaleMatrix = [
  [2, 0],
  [0, 3]
];

複数点の一括変換

JavaScript
// 三角形の3つの頂点を一度に回転
const triangle = [
  { x: 0, y: 1 },
  { x: 1, y: 0 },
  { x: -1, y: 0 }
];

const rotated = triangle.map(point => 
  transformVector(rotationMatrix, point)
);

デモ: 行列による変換の可視化

下のデモで、行列がベクトルをどう変換するか観察しましょう!

ポイント

青い破線は元のベクトル (3, 0) と (0, 3)、赤い実線は変換後のベクトルを示します。

変換行列
X方向の倍率
2.00
0.00
0.00
Y方向の倍率
1.50
×
x
3.00
y
0.00
=
x'
6.00
y'
0.00

例: ベクトル (3, 0) を変換した場合

x' = 2.00 × 3 + 0.00 × 0 = 6.00
y' = 0.00 × 3 + 1.50 × 0 = 0.00

JavaScript実装

2×2行列クラスの基本構造

JavaScript
/**
 * 2×2行列クラス
 */
class Matrix2x2 {
  constructor(a, b, c, d) {
    // [[a, b], [c, d]] の形式で保存
    this.values = [
      [a, b],
      [c, d]
    ];
  }
  
  /**
   * 行列とベクトルの積
   */
  transform(vector) {
    const [[a, b], [c, d]] = this.values;
    return {
      x: a * vector.x + b * vector.y,
      y: c * vector.x + d * vector.y
    };
  }
  
  /**
   * 単位行列(何も変換しない)
   */
  static identity() {
    return new Matrix2x2(1, 0, 0, 1);
  }
  
  /**
   * 拡大縮小行列
   */
  static scale(sx, sy) {
    return new Matrix2x2(sx, 0, 0, sy);
  }
  
  /**
   * 回転行列
   */
  static rotation(angle) {
    const cos = Math.cos(angle);
    const sin = Math.sin(angle);
    return new Matrix2x2(cos, -sin, sin, cos);
  }
}

// 使用例1: 2倍拡大
const scaleMatrix = Matrix2x2.scale(2, 2);
const point = { x: 3, y: 4 };
const scaled = scaleMatrix.transform(point);
console.log(scaled);  // { x: 6, y: 8 }

// 使用例2: 45度回転
const rotMatrix = Matrix2x2.rotation(Math.PI / 4);
const rotated = rotMatrix.transform(point);
console.log(rotated);  // 回転後の座標

ゲームでの実装例

JavaScript
// キャラクターの姿勢を行列で管理
class GameObject {
  constructor(x, y) {
    this.position = { x, y };
    this.transform = Matrix2x2.identity();  // 初期姿勢
  }
  
  /**
   * オブジェクトを回転
   */
  rotate(angle) {
    const rotation = Matrix2x2.rotation(angle);
    // 現在の変換に回転を合成(次章で学習)
    this.transform = rotation;
  }
  
  /**
   * ローカル座標をワールド座標に変換
   */
  localToWorld(localPoint) {
    // 変換行列を適用
    const transformed = this.transform.transform(localPoint);
    
    // 位置を加算
    return {
      x: transformed.x + this.position.x,
      y: transformed.y + this.position.y
    };
  }
}

// 使用例
const player = new GameObject(100, 100);
player.rotate(Math.PI / 4);  // 45度回転

// プレイヤーの前方1マスの座標
const forward = { x: 0, y: 1 };
const worldPos = player.localToWorld(forward);
console.log(worldPos);  // ワールド座標

行列の重要性

行列を使うと以下のような操作が簡単になります:

  • 回転・拡大縮小: 1つの行列で表現
  • 複数変換の合成: 次章で学ぶ行列の積
  • 効率的な計算: 複数点を一度に変換
  • コードの整理: 変換ロジックを1箇所に集約

次章では、行列同士の演算(積、加算など)を学びます。