====== 🧭 第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ステップずつ進むゲームループを体験した。