====== 🧭 第6.5章 縮小表示のステージ+当たり判定(完全版+イニシャライザ解説付き) ====== ===== 🧩 メンバイニシャライザとは? ===== クラスのメンバ変数を、**コンストラクタのかっこの後に「:」を使って初期化する方法**です。\\ 代入と違い、オブジェクトの生成時に同時に値を設定できるため、\\ __定数・参照・クラス型メンバ__ などを正しく初期化できます。\\ class Player { public: string name; int hp; double speed; // (例1)メンバイニシャライザを使った初期化 Player(string n, int h, double s) : name(n), hp(h), speed(s) { // コンストラクタ本体 } // (例2)通常の代入初期化 //最近割と非推奨(生成後に代入するため) Player(string n, int h, double s) { name = n; hp = h; speed = s; } }; ---- ===== 🧭 書き方のまとめ ===== |< tablewidth 60% >| |< 60% 20% 40% 40% >| ^ 項目 ^ 書き方 ^ 説明 ^ | 構文 | : 変数(初期値), 変数(初期値) | クラスのメンバを生成時に同時初期化する | | 記述位置 | コンストラクタの ''()'' の後 | ''{ }'' の前に書く | | 利点 | 代入より高速・確実 | 特に ''const'' メンバや参照メンバで必須 | | 初期化順 | 宣言順に実行される | ソースコード内の並び順に依存しない | ---- ===== 🏁 章のゴール ===== * 実際の距離を縮小して「双六(すごろく)」形式で表示できるようにする * Player(P)と Enemy(E)を左右から動かし、Enterキーで1ステップずつ進める * 双六の下に現在の位置を表示する * 実距離で「距離 ≤ 半径合計」のときに接触(HIT)を判定する ---- ===== Step 1. 基本クラスを作ろう ===== 🎯 **目的**: * C++でクラスを使って、PlayerとEnemyを動かす基本構造を作る。 * Vector2Dで位置・速度を扱えるようにする。 🧠 **考え方** * PlayerとEnemyはほぼ同じ構造なので、共通の仕組みをクラス化する。 * update関数で座標を更新し、pos = pos + vel * DT という形で動かす。 🛠 **コード** #include #include #include #include #include #include // system("cls") #include // numeric_limits using namespace std; const int BOARD_CELLS = 28; const double SCALE = 10.0; const double DT = 1.0; class Vector2D { public: double x, y; Vector2D(double ax=0, double ay=0) : x(ax), y(ay) {} Vector2D add(const Vector2D& v) const { return Vector2D(x + v.x, y + v.y); } static double distance(const Vector2D& a, const Vector2D& b) { double dx = a.x - b.x, dy = a.y - b.y; return sqrt(dx*dx + dy*dy); } }; class Player { public: string name; Vector2D pos, vel; double radius; Player(string n, Vector2D p, Vector2D v, double r) : name(n), pos(p), vel(v), radius(r) {} void update() { pos = pos.add(Vector2D(vel.x * DT, vel.y * DT)); } }; class Enemy { public: string name; Vector2D pos, vel; double radius; Enemy(string n, Vector2D p, Vector2D v, double r) : name(n), pos(p), vel(v), radius(r) {} void update() { pos = pos.add(Vector2D(vel.x * DT, vel.y * DT)); } }; 🔍 **確認** * クラスの定義が正しいか確認する。 * まだ実行しても何も起きないが、エラーが出なければOK。 ---- ===== Step 2. 縮小変換を作ろう(toCell関数) ===== 🎯 **目的**: * 実距離(worldX)を「マス番号(セル番号)」に変換する。 🧠 **考え方** * 1マス = SCALE(例: 10.0)と決める。 * 実距離 ÷ SCALE でマス番号に変換できる。 * 例:実距離 35 → 35 / 10 = 3.5 → 四捨五入で4マス目 🛠 **コード** int toCell(double worldX) { int idx = static_cast(worldX / SCALE + 0.5); if (idx < 0) idx = 0; if (idx >= BOARD_CELLS) idx = BOARD_CELLS - 1; return idx; } 🔍 **確認** * worldX=0, 9.9, 10.0, 20.1 などを試してマス番号を確認。 ---- ===== Step 3. 双六ラインを作ろう ===== 🎯 **目的**: * PlayerとEnemyの位置を「|P|_|_|E|」のように文字で表す。 🧠 **考え方** * vector でマスを作り、PとEを配置する。 * 同じマスに来たら「X」と表示する。 🛠 **コード** string buildSugorokuLine(int pCell, int eCell) { vector cells(BOARD_CELLS, '_'); if (pCell == eCell) cells[pCell] = 'X'; else { cells[pCell] = 'P'; cells[eCell] = 'E'; } string line; for (char c : cells) { line += '|'; line += c; } line += '|'; return line; } 🔍 **確認** * buildSugorokuLine(0,3) → |P|_|_|E| ---- ===== Step 4. 位置ラベルを表示しよう ===== 🎯 **目的**: * 双六の下に「Enemy: E(距離)」「Player: P(距離)」を表示する。 🛠 **コード** void render(const Player& p, const Enemy& e) { int pCell = toCell(p.pos.x); int eCell = toCell(e.pos.x); string line = buildSugorokuLine(pCell, eCell); cout << line << "\n"; cout << " Enemy: E(" << e.pos.x << ")\n"; cout << " Player: P(" << p.pos.x << ")\n"; } 🔍 **確認** * PlayerとEnemyの位置がきちんと表示されるか。 ---- ===== Step 5. Enterキーで1ステップ進もう ===== 🎯 **目的**: * Enterを押すたびに動きを1ステップ進める。 🛠 **コード** int main() { Player pl("Player", Vector2D(0,0), Vector2D(3,0), 2.0); Enemy en("Enemy", Vector2D(270,0), Vector2D(-2,0), 2.0); for (int step = 0; step < 200; ++step) { system("cls"); cout << "=== Step " << step << " ===\n"; render(pl, en); double d = Vector2D::distance(pl.pos, en.pos); cout << " distance = " << d << " / threshold (rP+rE) = " << (pl.radius + en.radius) << "\n"; if (d <= (pl.radius + en.radius)) { cout << ">>> HIT! 接触しました。\n"; break; } cout << "\nEnterで次へ..."; cin.ignore(numeric_limits::max(), '\n'); pl.update(); en.update(); } return 0; } 🔍 **確認** * Enterで1ステップ進み、HIT判定が出るまで動く。 ---- ===== Step 6. 距離でヒット判定を関数化しよう ===== 🎯 **目的**: * 判定ロジックを関数として分ける。 🛠 **コード** bool hitTest(const Player& p, const Enemy& e) { double d = Vector2D::distance(p.pos, e.pos); return d <= (p.radius + e.radius); } 🔍 **確認** * 近づいた時に「HIT!」が表示される。 ---- ===== Step 7. パラメータ実験 ===== 🎯 **目的**: * 各パラメータを変えて挙動を観察する。 🧠 **観察のポイント** * 速度が速いとどうなる? * 半径が大きいと早く当たる? * SCALEを変えると画面の縮尺がどう変わる? 💬 **メモ例** * 速度を上げると、画面上の動きが速くなりすぐに接触した。 * SCALEを10→5にするとマップが長くなった。 ---- ===== ✅ 最終出力例 ===== === Step 12 === |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|P|_|_|_|_|_|_|_|E| Enemy: E(162.0) Player: P(162.0) distance = 0 / threshold (rP+rE) = 4 >>> HIT! 接触しました。 ---- ===== 🧾 まとめ ===== * 実際の距離を縮小して画面に表示できるようになった。 * PlayerとEnemyをクラスで表し、座標更新・距離計算を行った。 * 当たり判定は「距離 ≤ 半径合計」で表せる。 * Enterキーで1ステップずつ進むゲームループを体験した。