====== 🎓 第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クラス」を題材に、**複数のメンバ関数が連携して動作するオブジェクト設計**を学びます。