====== 🎓 第4章 計算できるクラスを作ろう:Vector2Dクラス ======
===== 🏁 学習目標 =====
* 引数と戻り値を持つメンバ関数を作る
* オブジェクト同士の計算を行う方法を理解する
* sqrt() 関数を使ってベクトルの長さ(距離)を求める
----
===== 💬 ベクトルってなに? =====
ベクトルとは、「大きさ」と「向き」を持った量のことです。
ゲームやCGでは「位置」「速度」「移動方向」など、あらゆる場面で使われます。
ここでは、''(x, y)'' の2次元ベクトルを例に考えます。
Vector2D v1
├─ x = 3
├─ y = 4
├─ add(v2) → v1 + v2 を新しいベクトルとして返す
└─ length() → √(x² + y²)
----
===== 🧮 例題:Vector2Dクラス =====
**目的:** 2つのベクトルを足し算して、新しいベクトルを作ってみよう。
#include
#include // 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
**ポイント**
* ''add()'' は ''Vector2D'' 型を返す → 新しいオブジェクトを返している。
* ''length()'' は ''double'' 型(数値)を返す。
* 戻り値を ''return'' で返すと、結果を別の変数に代入できる。
----
===== 🎮 ゲーム的に考えてみよう =====
ベクトルは **「移動方向」や「速さ」** を表すのに使えます。
たとえば、キャラクターが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
**ヒント**
* 正規化は「自分の長さで割る」 → ''x /= len; y /= len;''
* ただし長さが0のときは割れないので注意!
* 内積の定義:''x1*x2 + y1*y2''
----
===== 🎯 まとめ =====
|< tablewidth 50% >|
|< 50% 50% >|
^ 覚えるポイント ^ 内容 ^
| 引数付きメンバ関数 | 他のオブジェクトを受け取って計算する |
| 戻り値 | 新しい結果を返すことで再利用できる |
| 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クラス」を題材に、**複数のメンバ関数が連携して動作するオブジェクト設計**を学びます。