🎓 第4章 計算できるクラスを作ろう:Vector2Dクラス

🏁 学習目標


💬 ベクトルってなに?

ベクトルとは、「大きさ」と「向き」を持った量のことです。 ゲームやCGでは「位置」「速度」「移動方向」など、あらゆる場面で使われます。

ここでは、(x, y) の2次元ベクトルを例に考えます。

Vector2D v1
  ├─ x = 3
  ├─ y = 4
  ├─ add(v2) → v1 + v2 を新しいベクトルとして返す
  └─ length() → √(x² + y²)

🧮 例題:Vector2Dクラス

目的: 2つのベクトルを足し算して、新しいベクトルを作ってみよう。

#include <iostream>
#include <cmath>   // sqrt() 用
using namespace std;
 
class Vector2D {
public:
    double x;
    double y;
 
    // コンストラクタ(中かっこ初期化対応)
    Vector2D(double a, double b) {
        x = a;
        y = b;
    }
 
    // 別のベクトルを足して結果を返す
    Vector2D add(Vector2D other) {
        double nx = x + other.x;
        double ny = y + other.y;
        return Vector2D(nx, ny);  // 新しいベクトルを返す
    }
 
    // ベクトルの長さを求める
    double length() {
        return sqrt(x * x + y * y);
    }
 
    // 表示用
    void show() {
        cout << "(" << x << ", " << y << ")" << endl;
    }
};
 
int main() {
    // 丸かっこ初期化
    Vector2D v1(3, 4);
 
    // 中かっこ { } 初期化(統一初期化)
    Vector2D v2{1, 2};
 
    cout << "v1 = "; v1.show();
    cout << "v2 = "; v2.show();
 
    // v3 = v1 + v2 の結果を作る
    Vector2D v3 = v1.add(v2);
 
    cout << "v3 = v1 + v2 = "; v3.show();
 
    cout << "v3 の長さ = " << v3.length() << endl;
}

実行結果

v1 = (3, 4)
v2 = (1, 2)
v3 = v1 + v2 = (4, 6)
v3 の長さ = 7.2111

ポイント


🎮 ゲーム的に考えてみよう

ベクトルは 「移動方向」や「速さ」 を表すのに使えます。

たとえば、キャラクターが2Dマップ上を動くとき、 その「1フレームごとの移動量」をベクトルとして足し合わせると、移動の軌跡(道筋) が表せます。

スタート位置 pos = (0, 0)

移動1:(+1, +0) → 右へ
移動2:(+0, +1) → 上へ
移動3:(+1, +1) → 斜め右上へ

最終位置 pos = (0,0) + (1,0) + (0,1) + (1,1) = (2,2)

つまり「ベクトルを足す」ということは、 キャラが連続して動いた結果の最終座標を求めることと同じです。

これを理解しておくと、add() の意味が「位置の更新」として自然にイメージできます。


🧩 練習問題(第4問)

ベクトルの減算・内積・正規化を追加してみよう。

// クラス名: Vector2D
// - メンバ変数: x, y
// - コンストラクタ: (x, y) を初期化
// - メンバ関数:
//     1. add(Vector2D v)        … 足し算
//     2. sub(Vector2D v)        … 引き算 (x-v.x, y-v.y)
//     3. dot(Vector2D v)        … 内積を返す (x*v.x + y*v.y)
//     4. normalize()            … 長さを1にするベクトルを返す
//     5. show()                 … 表示
//
// main()で以下を実行せよ:
//   ・v1=(3,4), v2=(1,2)
//   ・v3=v1.sub(v2)
//   ・内積を求める
//   ・正規化した v1' の長さを確認する
//
// 出力例:
// v1 = (3, 4)
// v2 = (1, 2)
// v3 = (2, 2)
// 内積 = 11
// v1' = (0.6, 0.8)
// v1' の長さ = 1

ヒント


🎯 まとめ

覚えるポイント 内容
引数付きメンバ関数 他のオブジェクトを受け取って計算する
戻り値 新しい結果を返すことで再利用できる
sqrt() ベクトルの長さを求める関数(mathヘッダ)
丸かっこ/中かっこ初期化 どちらもOK(中かっこは統一初期化)
ゲームとの関係 ベクトルの加算は「キャラの移動の軌跡」を表す

💡 補足:クラス内に書くメンバ関数(インライン定義)

これまでの例では、クラスの中で 関数の宣言実装 をまとめて書いてきました。

class Player {
public:
    void move() {
        // ここに動作を書く
    }
};

このように クラスの中で関数の中身を書く ことを 「インライン定義(inline定義)」と呼びます。

一方で、クラスの外に実装を書くこともできます。

class Player {
public:
    void move();   // ここでは宣言だけ
};
 
// クラスの外で実装
void Player::move() {
    // 動作の中身
}

💬 「Player::move()」の意味

Player::move()::(ダブルコロン)は 「スコープ解決演算子(scope resolution operator)」と呼ばれます。

これは、

「move関数は、Playerクラスに属するメンバ関数ですよ」

という意味を持っています。

もし :: をつけなかったら、 ただの「グローバル関数(どのクラスにも属さない関数)」として扱われてしまいます。

// NG例:クラス外の関数として扱われる
void move() {
    cout << "これはPlayerとは関係ない関数です。" << endl;
}

クラス外定義の正しい構文をもう一度まとめると:

戻り値の型 クラス名::関数名(引数リスト) {
    // 実装
}

例:

void Player::move(Vector2D dir) {
    pos = pos.add(dir);
}

このように書くことで、 「Playerクラスの move関数」として正しく認識されます。

記述場所 特徴
クラスの中で書く 短い処理向け。簡潔で見やすい。
クラスの外で書く 長い処理やファイル分割(.h と .cpp)に向いている。

📘 次回のステップ → 第5章では「Playerクラス」を題材に、複数のメンバ関数が連携して動作するオブジェクト設計を学びます。