# 避けゲー

[![image.png](https://bookstack.yz-learning.com/uploads/images/gallery/2025-07/scaled-1680-/Bcpimage.png)](https://bookstack.yz-learning.com/uploads/images/gallery/2025-07/Bcpimage.png)

## 実習課題 避けゲー

### 目的（これを体験で学ぶ）

- ゲームエンジニアリングの基礎を知る
- C++ と Visual Studio で実際に動くゲームを作る流れを体験する
- ソースコード → ビルド → 実行ファイル という開発の基本パイプラインを理解する
- プログラミングの基本（変数／順次・分岐・反復の3つの構造）を使えるようになる
- エラーが出たときの「原因を調べて直す」デバッグの流れを身につける
- DxLib（ゲーム用ライブラリ）を使って画面に描画し、ゲームの構成要素を組み立てる

---

## ゲーム構成（この体験で作る「避けゲー」の要素）

### 実行してみてみよう

<table id="bkmrk--5"><colgroup><col></col><col></col><col></col></colgroup><tbody><tr><td>[![スクリーンショット 2025-08-01 013357.png](https://bookstack.yz-learning.com/uploads/images/gallery/2025-07/scaled-1680-/2025-08-01-013357.png)](https://bookstack.yz-learning.com/uploads/images/gallery/2025-07/scaled-1680-/2025-08-01-013357.png)

</td><td>[![スクリーンショット 2025-08-01 013406.png](https://bookstack.yz-learning.com/uploads/images/gallery/2025-07/scaled-1680-/2025-08-01-013406.png)](https://bookstack.yz-learning.com/uploads/images/gallery/2025-07/scaled-1680-/2025-08-01-013406.png)

</td><td>[![スクリーンショット 2025-08-01 013411.png](https://bookstack.yz-learning.com/uploads/images/gallery/2025-07/scaled-1680-/2025-08-01-013411.png)](https://bookstack.yz-learning.com/uploads/images/gallery/2025-07/scaled-1680-/2025-08-01-013411.png)

</td></tr></tbody></table>

1. タイトル画面（スペースキーでスタート）
2. プレイヤー（左右キーで移動）
3. 敵（上からランダムに落ちてくる）
4. 衝突判定（ぶつかったらゲームオーバー）
5. スコア（何秒生き残ったか）
6. リザルト画面（結果表示）

---

## 実習課題（ゲームをうごかしてみよう）

ここから先は、実習ステップ（やってみよう）

<u>Let's Get Coding!</u>  
[![image.png](https://bookstack.yz-learning.com/uploads/images/gallery/2025-07/scaled-1680-/YsAimage.png)](https://bookstack.yz-learning.com/uploads/images/gallery/2025-07/YsAimage.png)

---

## 課題01：画像を読み込んで表示させよ

- 説明: プレイヤーと敵の画像を正しく読み込まないと表示されない。`<span class="editor-theme-code">playerImage</span>`<span style="white-space: pre-wrap;"> に "image/cat.png"、</span>`<span class="editor-theme-code">enemyImage</span>`<span style="white-space: pre-wrap;"> に "image/snake.png" を読み込む作業を行う。</span>

<table class="align-center" id="bkmrk-%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%81%BF%E7%94%BB%E5%83%8Fcat.pngsnake.p" style="border-width: 1px; border-style: solid;"><caption>読み込み画像</caption><colgroup><col style="width: 240px;"></col><col style="width: 240px;"></col></colgroup><tbody><tr><td class="align-center">[![cat.png](https://bookstack.yz-learning.com/uploads/images/gallery/2025-08/scaled-1680-/cat.png)](https://bookstack.yz-learning.com/uploads/images/gallery/2025-08/cat.png)

cat.png

</td><td class="align-center">[![snake.png](https://bookstack.yz-learning.com/uploads/images/gallery/2025-08/scaled-1680-/snake.png)](https://bookstack.yz-learning.com/uploads/images/gallery/2025-08/scaled-1680-/snake.png)

snake.png

</td></tr></tbody></table>

### 読み込む画像ファイルを指定しよう

##### 36~41行目辺り

```
if( playerImage == -1 )
    playerImage = LoadGraph( "image/XXX.png" );
if( enemyImage == -1 )
    enemyImage = LoadGraph( "image/XXXX.png" );
```

### 注意点

- 画像ファイルがプロジェクト内に存在することを確認する。パスのスペルミスに注意。

### やること

1. `<span class="editor-theme-code">Init()</span>`<span style="white-space: pre-wrap;"> 内の </span>`<span class="editor-theme-code">LoadGraph</span>`<span style="white-space: pre-wrap;"> 呼び出しを上記のように書き換える。</span>
2. ビルド &amp; 実行してプレイヤーと敵の画像が表示されるか確認。
3. 表示されなければファイル名やパスをチェック。

### 確認ポイント

- プレイヤーと敵の画像が画面に表示されているか？
- 画像が表示されない場合、ファイルパスの間違いやファイルの存在を確認したか？

---

## 課題02：プレイヤーの移動速度を設定しよう

### <span style="white-space: pre-wrap;">スピードを表す変数に値を入れよう </span>

##### 23行目辺り

C++（他のどの言語でもそうだけど）プログラミングでは、いろいろなものを****変数****として表現します。  
プレイヤーの位置は、初期位置と、スピード、経過時間などの****変数****から毎フレーム計算されます。

```
//課題02 プレイヤーの移動速度を設定
playerSpeed = ＿＿＿＿＿f; // 例: 150.0f
```

- 左右キーを押したときに動く量が変わる。値を変えても動きを試してみよう。

<p class="callout info">用語と基礎説明</p>

- ****プログラミングの変数と数学の変数の違い****：数学で習う や は「値がわからないもの（代入される記号）」として出てくるけど、プログラムの変数は「値を入れておける箱」で、中の値を変えたり取り出したりできる。  
    例：`<span class="editor-theme-code">playerSpeed</span>`<span style="white-space: pre-wrap;"> という箱に </span>`<span class="editor-theme-code">150.0f</span>`<span style="white-space: pre-wrap;"> を入れておいて、使うときにその中身を取り出して移動に使う。</span>
- ****座標軸****：画面の左上が (0,0) で、右に行くほど X が増え、下に行くほど Y が増える。  
    ```
    (0,0) ┌─────────────> X
          │
          │
          v 
          Y
    ```
- ****ピクセル****：画面を構成する最小の点。位置やサイズはピクセル単位で考える。  
    例：画像が 64x64 ピクセルなら、横幅が64ピクセル、高さが64ピクセル。
- ****画像サイズ****：キャラクター画像の幅と高さ。描画位置は中心を基準にしているので、`<span class="editor-theme-code">player.x - CHARACTER_SIZE/2</span>`<span style="white-space: pre-wrap;"> などで左上を計算している。</span>
- ****移動速度（playerSpeed）****：1秒あたり何ピクセル動くかを表す。`<span class="editor-theme-code">player.x += playerSpeed * deltaTime;</span>`<span style="white-space: pre-wrap;"> で実際の移動距離になる。</span>  
    例：`<span class="editor-theme-code">playerSpeed = 150.0f;</span>`<span style="white-space: pre-wrap;"> なら 1秒で 150 ピクセル移動する（0.5秒なら 75 ピクセル）。</span>

---

## 課題03：敵の落下速度を設定しよう

プレイヤーは動き始めたけど、今度は敵が落ちてきません。  
敵のスピードも、（わざとらしく？）0になってるので、スピードを設定してあげてください。

### <span style="white-space: pre-wrap;">敵の落下速度を設定しよう </span>

##### 26行目辺り？

```
//課題03 敵の落下速度を設定
enemySpeed = ＿＿＿＿＿f; // 例: 150.0f
```

- 敵がどれくらい速く落ちてくるかを調整する。難易度に関係する。
- 今の画面のサイズは横✕縦＝960x640、640ピクセルを2秒で落ちるにはスピードは？みたいに決める

---

## 課題04：敵の生成（出現）タイマーを増やそう

敵がまだ出現しない。。。  
敵は、一定時間ごとに設定されたタイマーによって、定期的に現れる仕組みになっている。  
現状では、タイマーが動いていないのでタイマーを動かして敵を出現させよう！

ここでは、ゲームの進行管理で重要な<u>****フレーム****</u>について学ぶよ！

##### 59行目辺り？

```
//課題04 敵の生成タイマーを加算
enemySpawnTimer = enemySpawnTimer + deltaTime; // ここを書いて敵が定期的に出るようにする
```

- `<span class="editor-theme-code">enemySpawnInterval</span>`<span style="white-space: pre-wrap;"> を超えたら </span>`<span class="editor-theme-code">CreateEnemy()</span>`<span style="white-space: pre-wrap;"> が呼ばれる。</span>

<p class="callout info">仕組みの補足（フレームごとの動きとタイマーの関係）</p>

- ゲームは****「フレーム」****という単位で動いている。1フレームごとに画面を更新する。<u>普通は1秒に60回くらい（60FPS）</u>呼ばれる。
- 毎フレーム、次の順番で処理が行われる：
    1. `<span class="editor-theme-code">Update()</span>`（状態を変える：敵を落とす、タイマーを増やす、入力を読む）
    2. `<span class="editor-theme-code">Draw()</span>`（画面に今の状態を描く）
- `<span class="editor-theme-code">deltaTime</span>`<span style="white-space: pre-wrap;"> は前のフレームからの時間（たとえば 1/60 秒 = 約0.0167）。これを </span>`<span class="editor-theme-code">enemySpawnTimer</span>`<span style="white-space: pre-wrap;"> に足して「時間を数える」。</span>
- `<span class="editor-theme-code">enemySpawnTimer</span>`<span style="white-space: pre-wrap;"> が設定された間隔（</span>`<span class="editor-theme-code">enemySpawnInterval</span>`）を超えたら、その時点で新しい敵が出現する。つまり「何秒たったら出すか」をフレームをまたいで数えている。
- 例：`<span class="editor-theme-code">enemySpawnInterval = 0.5f</span>`<span style="white-space: pre-wrap;"> なら、0.5秒ごとに敵が出る。60FPS なら約30フレームごとに </span>`<span class="editor-theme-code">CreateEnemy()</span>`<span style="white-space: pre-wrap;"> が呼ばれる。</span>

<p class="callout warning"><span style="white-space: pre-wrap;">式 </span>`<span class="editor-theme-code">enemySpawnTimer = enemySpawnTimer + deltaTime</span>`<span style="white-space: pre-wrap;"> について（数学とプログラムの違い）</span></p>

- 数学の式だと左辺と右辺に同じ記号が出てくると変に見えるが、プログラムでは「****今の値に新しい分を足して更新する****」操作として普通に使う。  
    これは「箱の中身を取り出して、そこに時間分を足して、また箱に戻す」処理と考えられる。  
    例：今のタイマーが0.3秒で、`<span class="editor-theme-code">deltaTime</span>`<span style="white-space: pre-wrap;"> が0.0167なら、次のフレームでは 0.3167 秒になる。</span>

---

## 課題05：スコアの更新を有効にしよう

### フレーム間時間(deltaTime)を使ってスコアの変数を変化させよう

##### 93行目辺り

```
//課題05 スコアの値を更新
score = score + ?????; // 生き残った時間がスコアになる
```

<p class="callout info">仕組みの補足（敵の出現タイマーと同じ考え方）</p>

- <span style="white-space: pre-wrap;">スコアも </span>`<span class="editor-theme-code">enemySpawnTimer</span>`<span style="white-space: pre-wrap;"> と同じで、毎フレーム </span>`<span class="editor-theme-code">deltaTime</span>`<span style="white-space: pre-wrap;"> を足して時間を数えている。</span>
- `<span class="editor-theme-code">score += deltaTime;</span>`<span style="white-space: pre-wrap;"> は「生き残った時間」がそのまま点数になる仕組み。60FPSなら1フレームごとに約0.0167ずつ増える。</span>

<p class="callout success">画面表示の豆知識</p>

- <span style="white-space: pre-wrap;">このスコア表示や文字の描画は </span>`<span class="editor-theme-code">DxLib</span>`<span style="white-space: pre-wrap;"> が簡単にしてくれているから少ないコードで書ける。</span>  
    <span style="white-space: pre-wrap;">もし </span>`<span class="editor-theme-code">DirectX</span>`<span style="white-space: pre-wrap;"> 単体で文字を表示しようとすると、フォントを読み込んでテクスチャを作り、描画用の頂点バッファやシェーダを用意する必要があり、かなりコードが長くなる。</span>  
    `<span class="editor-theme-code">DxLib</span>`<span style="white-space: pre-wrap;"> はそういう面倒な下準備を隠してくれていて、</span>`<span class="editor-theme-code">DrawFormatStringToHandle</span>`<span style="white-space: pre-wrap;"> などを呼ぶだけで表示できる。</span>

---

## 課題06：当たり判定をつけよう

### 当たり判定が働いたら敵を消す／終了にする（条件分岐、関数呼び出し）

##### 86行目辺り

当たり判定の処理を呼び出してみよう。当たり判定の処理は、別のところに`<span class="editor-theme-code">IsHit()</span>`として記述されています。  
それを必要なところで呼び出すと、すべての敵と、プレイヤーがぶつかっているかどうかを判定し、その結果を真偽値（true, false)で、知らせてくれます。

```
//課題06 当たり判定
if( 0 )　//ここで当たり判定の処理を呼び出してみよう
{
    ClearEnemies(); // 敵を消してみる（実装済み関数を使う）
    // 課題07 リザルト画面へ移行（下につなげる）
}
```

<p class="callout success">説明：ついに当たり判定です</p>

ゲームにおける当たり判定とは、****画面上のもの同士が「ぶつかったかどうか」****を調べて、****それに応じた反応****（ダメージを受ける／ゲームオーバーになる／アイテムを取るなど）****を起こす****仕組みです。  
<span style="white-space: pre-wrap;"> このプログラムでは、キャラクター（プレイヤー）と敵のぶつかり判定に</span>****「円と円の距離」****を使っています。  
プレイヤーと敵の中心の距離を計算して、それが一定以内（キャラクターのサイズの2乗）ならぶつかったと判断します。これは簡単で速い衝突チェックの方法です。

[![image.png](https://bookstack.yz-learning.com/uploads/images/gallery/2025-07/scaled-1680-/1EBimage.png)](https://bookstack.yz-learning.com/uploads/images/gallery/2025-07/1EBimage.png)

<div drawio-diagram="" id="bkmrk--29">![]()</div>### 条件分岐(if文)と真偽値

- <span style="white-space: pre-wrap;">当たり判定の結果は「ぶつかっているか」「ぶつかっていないか」の </span>****真（true）／偽（false）****<span style="white-space: pre-wrap;"> で表される値（真偽値）として返される。</span>`<span class="editor-theme-code">IsHit()</span>`<span style="white-space: pre-wrap;"> はこれを返す関数で、ぶつかっていれば true を、そうでなければ false を返す。</span>
- `<span class="editor-theme-code">if( IsHit() ) { ... }</span>`<span style="white-space: pre-wrap;"> のように書くと、「ぶつかったときだけ中の処理をする」ことができる。これが </span>****条件分岐****<span style="white-space: pre-wrap;"> で、プログラミングの3つの基本構造（順次・分岐・反復）のひとつで非常に重要。処理を状況に応じて切り替えるための仕組み。</span>
- <span style="white-space: pre-wrap;">ここでは「当たっていたら敵を消す／ゲームオーバーにする」といった反応を </span>`<span class="editor-theme-code">if</span>`<span style="white-space: pre-wrap;"> を使って実現している。</span>

### <span style="white-space: pre-wrap;">サブルーチン（関数）としての </span>`<span class="editor-theme-code">IsHit()</span>`<span style="white-space: pre-wrap;"> の役割</span>

- `<span class="editor-theme-code">IsHit()</span>`<span style="white-space: pre-wrap;"> という関数が別に作られていて、毎フレームの中で呼ばれています。</span>  
    <span style="white-space: pre-wrap;">ゲームの処理は1フレームごとに </span>`<span class="editor-theme-code">Update()</span>`<span style="white-space: pre-wrap;"> → </span>`<span class="editor-theme-code">Draw()</span>`<span style="white-space: pre-wrap;"> の順に動いていて、その </span>`<span class="editor-theme-code">Update()</span>`<span style="white-space: pre-wrap;"> の中で状態チェック（衝突の有無など）を行うために </span>`<span class="editor-theme-code">IsHit()</span>`<span style="white-space: pre-wrap;"> を呼び出します。</span>
- <span style="white-space: pre-wrap;">このように「特定の処理をまとめて名前をつけ、必要なときに呼び出す仕組み」を </span>****サブルーチン****（関数、ルーチン）と呼びます。  
    サブルーチンを使うと、同じ処理を何度も書かずにすみ、プログラムが整理されて読みやすく、直しやすくなります。

---

## 課題07：リザルト画面へ移行する処理を追加しよう

##### ８９行目辺り

```
//課題06 当たり判定
	if( 0 )
	{
		ClearEnemies();
		//課題07 リザルト画面へ移行
		//->ここに移行処理を追加
	}
```

- 衝突後にリザルト用のシーンに変えるコードを追加。
- ****補足****：`<span class="editor-theme-code">GoToResultScene()</span>`<span style="white-space: pre-wrap;"> という関数が用意されている</span>

<p class="callout info">画面遷移と Scene（状態遷移）の考え方</p>

- 多くのゲームは「Scene（シーン）」という単位で画面ごとの役割を分けて管理している。たとえばタイトル画面、プレイ画面、リザルト画面がそれぞれ別の Scene。
- <span style="white-space: pre-wrap;">ある条件が満たされると Scene を切り替える。これを </span>****状態遷移****<span style="white-space: pre-wrap;"> と呼ぶ。たとえば当たり判定で衝突したら「プレイ中」から「リザルト」へ移る、といった具合。</span>
- `<span class="editor-theme-code">GoToResultScene()</span>`<span style="white-space: pre-wrap;"> や </span>`<span class="editor-theme-code">changeScene(...)</span>`<span style="white-space: pre-wrap;"> は状態を変える関数で、現在の Scene の状態を終了して新しい Scene に移る（遷移する）処理をまとめている。</span>
- 状態遷移は画面遷移だけでなく、****キャラクターの状態管理（立っている・走っている・ジャンプ中など）やアニメーションの切り替え****、敵のAIの振る舞いの切り替えなど、ゲームのあらゆる部分で使われている。
- 状態遷移を整理しておくと、「何が起きたら次に何を表示するか／どう振る舞うか」が明確になり、ゲーム全体の流れや作りやすさが向上する。

---

## おまけ

### このゲームの「動く」仕組みのミニ解説

- プログラミングの３つの基本構造
    1. ****順次****：コードは上から下へ動く。初期化 → 毎フレームの処理（Update） → 描画（Draw）と流れる。
    2. ****分岐****：`<span class="editor-theme-code">if</span>`<span style="white-space: pre-wrap;"> で条件によって違うことをする（例：左右キーで動くかどうか）。</span>
    3. ****反復****：`<span class="editor-theme-code">for</span>`<span style="white-space: pre-wrap;"> で敵を全部落とす、全部描く。</span>
- ****変数****：`<span class="editor-theme-code">player.x</span>`<span style="white-space: pre-wrap;"> や </span>`<span class="editor-theme-code">score</span>`<span style="white-space: pre-wrap;"> は変化する値を保存する箱。</span>
- ****deltaTime****：前のフレームからの時間。これを掛けるとどんなPCでも速さが同じになる。
- ****当たり判定****：プレイヤーと敵の距離の2乗を比べてぶつかったか判定している。

### 主な変数の意味（実習で触るもの）

- `<span class="editor-theme-code">player.x</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">player.y</span>`：プレイヤーの位置。画面上のピクセル座標（左上が0,0）。
- `<span class="editor-theme-code">playerSpeed</span>`：プレイヤーが1秒あたり何ピクセル動くか。左右キー入力時に使われる。
- `<span class="editor-theme-code">enemySpeed</span>`：敵が1秒あたり何ピクセル落ちるか。落下の速さ。
- `<span class="editor-theme-code">enemySpawnInterval</span>`：何秒ごとに新しい敵を出すかの間隔。小さいほど敵がたくさん出る。
- `<span class="editor-theme-code">enemySpawnTimer</span>`<span style="white-space: pre-wrap;">：出現までの経過時間をためておくタイマー。これが </span>`<span class="editor-theme-code">enemySpawnInterval</span>`<span style="white-space: pre-wrap;"> を超えると敵が出る。</span>
- `<span class="editor-theme-code">score</span>`：生き残った時間を蓄積した値。時間経過で増える（＝ゲームの得点）。
- `<span class="editor-theme-code">enemies</span>`：敵の位置のリスト。`<span class="editor-theme-code">for</span>`<span style="white-space: pre-wrap;"> ループで全部更新・描画・判定する。</span>
- `<span class="editor-theme-code">CHARACTER_SIZE</span>`：プレイヤー・敵のサイズ（直径）。当たり判定や描画位置の計算に使う。

### フレームレート依存と「距離 = 速さ × 時間」の重要性

- <span style="white-space: pre-wrap;">もし </span>`<span class="editor-theme-code">player.x += playerSpeed;</span>`<span style="white-space: pre-wrap;"> のように </span>****deltaTime を掛けずに****移動処理を書いた場合、1フレームあたり同じ量だけ動くので、フレームレートが高いと速く、低いと遅くなる。  
    例：1秒間に 60 フレームだと 60 × 150 = 9000 ピクセル動くが、30 フレームだと 30 × 150 = 4500 ピクセルしか動かない。動きがマシン依存になってしまう。
- これを防ぐには「距離 = 速さ × 時間」の考え方で、`<span class="editor-theme-code">player.x += playerSpeed * deltaTime;</span>`<span style="white-space: pre-wrap;"> のように、前のフレームからの時間（deltaTime）を使って移動量を調整する。</span>  
    こうすると、どんなフレームレートでも1秒あたり同じ距離だけ動く。
- `<span class="editor-theme-code">deltaTime</span>`<span style="white-space: pre-wrap;"> を使うのはゲームでもっとも基本的なテクニックの一つ。</span>

### なぜこれを使わないと困るのか（詳しい影響）

1. ****動きがマシンによって違う****：高性能なPCではフレームレートが高くなりすぎてキャラクターが速くなり、古いPCでは遅くなる。プレイ体験がバラバラになり、公平なゲームにならない。
2. ****入力の反応や操作感が不安定になる****：フレームが速いとキーを押した瞬間の移動量が大きく変わるため、プレイヤーが思った通りに操作できない。
3. ****物理的な計算が壊れる****：重力や速度の積み重ねなど、時間に依存するシミュレーションはフレームごとに変わると累積誤差やバウンスの違いが発生し、動きがぎこちなくなる。
4. ****再現性がなくなる****：同じ操作をしてもフレームレートによって結果が変わるので、バグの再現や調整が難しくなる。
5. ****フレーム落ちで急に遅くなる現象（スタッタリング）****：一時的にフレームレートが下がると移動距離も一気に減る／飛び跳ねたように見えることがある。

##### 追加の対策例

- ****固定タイムステップ****を使って、物理計算だけ一定の時間間隔で何度も更新し、その間の描画は自由に行う方法。時間の積み残しを管理して見た目と計算を分けることで安定した動きにできる。
- ****補間****を使って描画と物理のズレをなめらかに見せる工夫をする。

---

---

## 発展課題

### ゲームの中で気になるところない？

- プレイヤーが画面の外に出るまで移動可能
    - どうやって、移動を制限するかな？
- 時間があったら試してみよう
    - 敵の見た目の変更
    - 敵の出現間隔の変更
    - 敵の落下位置をプレイヤーキャラクター付近に変更
    - 敵の落下スピードを徐々に上げる