# ゲームエンジニア科体験入学

ゲームエンジニア科体験入学の説明ページ

# へびゲーム

## 体験入学会プログラミング体験教材

### はじめに

本日は体験入学会にお越しいただきありがとうございます。  
プログラミング初心者向けの体験授業として、C++を使った「スネークゲーム」を完成させます。  
<span style="white-space: pre-wrap;">コード中の </span>`<span class="editor-theme-code">TODO</span>`<span style="white-space: pre-wrap;"> マークに沿って穴埋めし、動くゲームを作りましょう。</span>

## ＨＥＢＩＧＡＭＥを動かしてみよう

##### まずはいったん遊んでみよう

- スペースキーでスタート
- キーボードのアローキー（⬆️⬇️⬅️➡️）で方向転換
- エサを食べると成長するよ！
- 自分の体を食べちゃったり、壁に当たるとゲームオーバー
- スペースキーでリスタート

---

### Step 1: 背景色を設定しよう（TODO ＃1 ） 62行目付近

<span style="white-space: pre-wrap;">ゲーム画面の最初に呼ばれる </span>`<span class="editor-theme-code">Stage</span>`<span style="white-space: pre-wrap;"> クラスのコンストラクタ(初期化処理)内で、</span>  
`<span class="editor-theme-code">SetBackgroundColor</span>`<span style="white-space: pre-wrap;"> 関数を使って画面の背景色を設定します。</span>

```cpp
// TODO ＃1: ここに背景色を設定するRGBを入れてください
SetBackgroundColor( ____, ____, ____ );
```

- 各値は 0〜255 の範囲で指定します。
- 見やすい色（例：空色など）を選びましょう。

#### ヒント

`<span class="editor-theme-code">SetBackgroundColor(132, 255, 255);</span>`<span style="white-space: pre-wrap;"> は淡いシアン（空色）です。</span>

---

### Step 2: ステージを描画しよう（TODO ＃2） 148行目付近

`<span class="editor-theme-code">Stage::Draw()</span>`<span style="white-space: pre-wrap;"> 内で、先ほど作ったグリッド状ステージを描画する関数を呼び出します。</span>

```cpp
void Stage::Draw()
{
    // TODO #2: ここにステージ描画関数を呼び出すコードを追加してください
    ____();
    
    if (!snake || !food)
        return;
    // 以下、蛇とフード、スコア描画...
}
```

#### ヒント

`<span class="editor-theme-code">DrawStageGrid()</span>`<span style="white-space: pre-wrap;"> を呼び出します。</span>

---

### Step 3: スネークを作成しよう（TODO ＃3） 63行目付近

<span style="white-space: pre-wrap;">コンストラクタ内で </span>`<span class="editor-theme-code">snake</span>`<span style="white-space: pre-wrap;"> ポインタに新しい </span>`<span class="editor-theme-code">Snake</span>`<span style="white-space: pre-wrap;"> オブジェクトを生成します。</span>

```cpp
// TODO ＃3: Snakeオブジェクトを生成しよう
snake = ____;
```

#### ヒント

`<span class="editor-theme-code">new Snake()</span>`<span style="white-space: pre-wrap;"> でインスタンス化します。</span>

---

### Step 3.5: 移動タイマーを設定しよう（TODO ＃3．５） 19行目付近

スネークの移動間隔を決めるタイマーを設定します。

```cpp
// TODO ＃3.5: スネークの移動間隔を設定しよう
SetMoveTimer( ____ );
```

#### ヒント

<span style="white-space: pre-wrap;">定数 </span>`<span class="editor-theme-code">MOVETIME</span>`<span style="white-space: pre-wrap;"> を使いましょう。</span>

---

### Step 4: フードを作成しよう（TODO ＃4） 64行目付近

<span style="white-space: pre-wrap;">コンストラクタ内で </span>`<span class="editor-theme-code">food</span>`<span style="white-space: pre-wrap;"> ポインタに新しい </span>`<span class="editor-theme-code">Food</span>`<span style="white-space: pre-wrap;"> オブジェクトを生成します。</span>

```cpp
// TODO ＃4: Foodオブジェクトを生成しよう
food = ____;
```

#### ヒント

`<span class="editor-theme-code">new Food()</span>`<span style="white-space: pre-wrap;"> を使います。</span>

---

### Step 5: キーボード入力を処理しよう（TODO ＃5）120行目付近

`<span class="editor-theme-code">Stage::Update()</span>`<span style="white-space: pre-wrap;"> 内で矢印キーを押したときにスネークの進行方向を変更します。</span>

```cpp
if (Input::IsKeyDown(KEY_INPUT_UP))
    snake->SetDirection( ____ );
if (Input::IsKeyDown(KEY_INPUT_DOWN))
    snake->SetDirection( ____ );
if (Input::IsKeyDown(KEY_INPUT_LEFT))
    snake->SetDirection( ____ );
if (Input::IsKeyDown(KEY_INPUT_RIGHT))
    snake->SetDirection( ____ );
```

#### ヒント

`<span class="editor-theme-code">Direction::UP</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">DOWN</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">LEFT</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">RIGHT</span>`<span style="white-space: pre-wrap;"> を使います。</span>

---

### Step 6: ヘビを伸ばそう（TODO ＃6） 131行目付近

スネークがフードを食べたら、体を伸ばすメソッドを呼び出します。

```cpp
if (snake->GetHeadPos() == food->GetPosition()) {
    // TODO ＃6: ヘビを伸ばす関数を呼び出そう
    snake->____();
    food->SpawnRandom();
    // スコア加算へ（TODO #8）
}
```

#### ヒント

`<span class="editor-theme-code">Snake</span>`<span style="white-space: pre-wrap;"> クラスの </span>`<span class="editor-theme-code">Grow()</span>`<span style="white-space: pre-wrap;"> メソッドを呼び出します。</span>

---

### Step 7: スコアを描画しよう（TODO ＃7） 155行目付近

`<span class="editor-theme-code">Stage::Draw()</span>`<span style="white-space: pre-wrap;"> 内で画面上にスコアを表示します。</span>

```cpp
// TODO ＃7: スコアを描画しよう
DrawScore();
```

#### ヒント

<span style="white-space: pre-wrap;">すでに用意した </span>`<span class="editor-theme-code">DrawScore()</span>`<span style="white-space: pre-wrap;"> を呼び出します。</span>

---

### Step 8: スコアを加算しよう（TODO ＃8） 136行目付近

フードを食べたときにスコアを増加させます。

```cpp
if (snake->GetHeadPos() == food->GetPosition()) {
    // ヘビを伸ばす
    // TODO ＃8: スコアを加算しよう
    Score::AddScore( ____ );
    food->SpawnRandom();
}
```

#### ヒント

<span style="white-space: pre-wrap;">1 回のフード取得につき </span>`<span class="editor-theme-code">1</span>`<span style="white-space: pre-wrap;"> 点加算しましょう。</span>

---

### Step 9: 壁との当たり判定をしよう（TODO ＃9） 110行目付近

`<span class="editor-theme-code">Stage::Update()</span>`<span style="white-space: pre-wrap;"> 内でスネークがステージ外に出たらゲームオーバーにします。</span>

```cpp
ivec3 pos = snake->GetBody()[0].GetPosition();
// TODO ＃9: 壁に当たったかチェックして、当たったら死亡処理を呼ぼう
if ( CheckHitWall( vec3(pos.x, 0, pos.z) ) ) {
    snake->SetDeath();
}
```

#### ヒント

`<span class="editor-theme-code">CheckHitWall()</span>`<span style="white-space: pre-wrap;"> を使って判定します。</span>

---

### Step 10: フードをスポーンしよう（TODO ＃4.5） 92行目付近

ステージ読み込み後にフードをランダムな位置に配置します。

```cpp
if (food) {
    // TODO ＃4.5: フードをランダムにスポーンさせよう
    food->____();
}
```

#### ヒント

`<span class="editor-theme-code">SpawnRandom()</span>`<span style="white-space: pre-wrap;"> メソッドを呼び出します。</span>

---

### Step 11: フードを更新しよう（TODO ＃11） 142行目付近

`<span class="editor-theme-code">Stage::Update()</span>`<span style="white-space: pre-wrap;"> 内でフードのアニメーション更新を呼び出します。</span>

```cpp
// TODO ＃11: フードの更新処理を呼び出そう
food->____();
```

#### ヒント

`<span class="editor-theme-code">Update()</span>`<span style="white-space: pre-wrap;"> メソッドを呼び出します。</span>

---

以上でスネークゲームの基本機能が完成します！  
コードをビルドして、動作を確認してみましょう。1 つずつクリアしながら進めてください。

### Step 12: ゲームオーバー時のシーン切り替え（TODO ＃12） 117行目付近

スネークが死亡したときに、別のシーンへ切り替えます。

```
if (!snake->IsAlive())
{
    // TODO ＃12: 死亡時にシーンを切り替えるコードを追加しよう
    SceneManager::ChangeScene();
}
```

#### ヒント

- `<span class="editor-theme-code">SceneManager</span>`<span style="white-space: pre-wrap;"> クラスの </span>`<span class="editor-theme-code">ChangeScene()</span>`<span style="white-space: pre-wrap;"> メソッドを呼び出します。</span>
- 遷移先のシーンはあらかじめ設定されていることを確認してください。

以上で、全 12 ステップの穴埋め教材が完成です！ コードをビルドし、ゲームオーバー時にも正しくシーンが切り替わるか確認してみましょう。 コードをビルドして、動作を確認してみましょう。1 つずつクリアしながら進めてください。

##### URL

https://github.com/youetsux/HEBIGAME\_TAIKEN.git

# キャラクターAIを作ってみよう

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

## 今日の目標

#### 今日の体験実習では、２Ｄボンバーマン風ゲームのキャラクターAIのプログラムを作りながら、****C++によるゲーム開発の実際の雰囲気****を体験してほしいと思います。

- 開発環境
    - Windows11
    - Visual Studio 2022
- プログラミング言語
    - C++
- 使用ライブラリ
    - DxLib

### C++によるプログラム開発の流れ

1. ****Build****<span style="white-space: pre-wrap;"> → ソースをビルドして実行</span>
2. ****Edit****<span style="white-space: pre-wrap;"> → 指定の行番号の定義を『書き換え』</span>
3. ****Test****<span style="white-space: pre-wrap;"> → 動作を確認し、発見・改善</span>

> ****POINT****: 小さな変更を加え、すぐにビルド＆実行。これを繰り返すのが開発サイクル！

## ゲームAIの種類

1. キャラクターAI
    - キャラクターAIは文字通り、****NPCを動かすために利用****される
    - 格闘ゲームの敵キャラクターや、RPGで主人公を支援する仲間の動作を決める
2. メタAI
    - ****ゲーム進行を補助****する
    - ****一定の条件を満たしたらイベントを開始****
    - ****ステージに応じて敵やアイテムの配置を変更****したりと、ゲーム画面の裏で色々な処理を行う
3. ナビゲーションAI
    - キャラクターAIやメタAIに対し、****各種情報を提供して動作をサポート****する
    - 敵キャラクターに主人公の位置や進行方向、障害物に関する情報を与える
    - ****この仕組みがないと、キャラクターAIは正確な動作を行えない****

## その１．キャラクターを選ぼう

ゲームに登場するキャラクターは、その種類によっていろいろなパラメータを持っています。  
プログラミング内でそれらを表現するときは、すべて数値や文字などの値として表現します。  
これらのゲーム中で利用する値に名前をつけたものを「変数」と呼びます。

変数の例：

- プレイヤーキャラ（Player)
    - 位置 (Position)
    - 移動速度 (Speed)
    - ジャンプ力 (JumpPower)
    - 攻撃力 (AttackPower)
    - 所持アイテム ( Items)
    - プレイヤーの画像 (PlayerImage)
    - ・・・

体験用プログラムでも同様に、敵キャラクターをEnemyという変数で表しています。  
Enemyはその中に更に細かい設定用のパラメータ（変数を持っています）。  
その中の、表示画像を表す変数を変更してみましょう。

### Enemyの表示画像の変更

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

> ****行番号：74 行目を見て書き換え****

```
42  if (isGraphic) {
 43      //#01 敵の画像を選択する！  
 44      std::string enemyImage = GetEnemyImage(KABOCHA);
 45      enemyImage_ = LoadGraph(enemyImage.c_str());
 46  }
```

- ****やること****<span style="white-space: pre-wrap;">：行74 の </span>`<span class="editor-theme-code">KABOCHA</span>`<span style="white-space: pre-wrap;"> を </span>`<span class="editor-theme-code">OTAKU</span>`<span style="white-space: pre-wrap;"> / </span>`<span class="editor-theme-code">NEKO</span>`<span style="white-space: pre-wrap;"> / </span>`<span class="editor-theme-code">OBAKE</span>`<span style="white-space: pre-wrap;"> に変更</span>
- ****学ぶ****：変数と配列アクセス（enum→配列インデックス）

## その２．初期出現位置を変更してみよう

### 初期出現位置の設定

初期位置をステージの右下に設定してみよう！  
キャラクターは、座標系に設置されます。（座標も****変数****で表すよ！）  
2Dならxy平面、3Dならxyz空間に(10,20)とか(40,50,1)のように設定されます。

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

  
座標は、x座標とy座標の値を持っています。  
初期位置は、ちょっとした計算によって以下のように設定されています。

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

- 実際のx座標 ＝ ｘ方向に何マス目？ ✕ キャラ幅
- 実際のy座標 ＝ ｙ方向に何マス目？ ✕ キャラ高さ
    - （一番左は０マス目って数えることに注意！）

x,ｙ方向に何マス目は２６行目辺りで設定されているよ！

### 書き換えてみよう

> ****行番号：26 行目を見て書き換え****

```
24  //#02 敵の初期出現位置を変更してみよう
 25  //ステージの右下に設定するには？
 26  const Pointf INIT_POS{ 5, 5 };  // #02
 
 //２６行目を書き換えると、６６行目の計算に反映される
 66  pos_ = { INIT_POS.x * CHA_WIDTH, INIT_POS.y * CHA_HEIGHT };
```

- ****やること****<span style="white-space: pre-wrap;">：行26 の </span>`<span class="editor-theme-code">5, 5</span>`<span style="white-space: pre-wrap;"> を </span>`<span class="editor-theme-code">書き換える</span>`
    - ステージの大きさは、どんな変数で表されているかな？（`<span class="editor-theme-code">STAGE_WIDTH, STAGE_HEIGHT）</span>`
    - 一番外側は壁だから、計算すると。。。
- ****学ぶこと****：定数、算術演算（掛け算）、座標→ピクセル変換

## その３．初期進行方向を変更してみよう

### 次の目標

次の目標として、ステージの右下から、ステージの左端まで移動させたい  
Enemy飲む気はどのように決められているのか見てみよう。（どうせ方向も変数なんでしょ！）

### 方向を表す変数

初期方向はINIT\_DIRという変数に設定されています。

![image.png](https://bookstack.yz-learning.com/uploads/images/gallery/2025-07/scaled-1680-/E75image.png)<table id="bkmrk--7"><colgroup><col style="width: 240px;"></col><col style="width: 240px;"></col></colgroup><tbody><tr><td>[![image.png](https://bookstack.yz-learning.com/uploads/images/gallery/2025-07/scaled-1680-/HjZimage.png)](https://bookstack.yz-learning.com/uploads/images/gallery/2025-07/HjZimage.png)

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

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

- ****やること****<span style="white-space: pre-wrap;">：行30 の </span>`<span class="editor-theme-code">UP</span>`<span style="white-space: pre-wrap;"> を変更して、左向きにする</span>
- ****学ぶこと****：if 文による条件分岐

## その４．速度を与えて動かそう

### ゲームの移動処理の基本

ゲームのキャラクタは、<span style="color: rgb(224, 62, 45);">方向</span>を示す****ベクトル****と、****スピード****、****移動時間（フレーム間時間）****で、次のフレームの位置を計算します。  
これは、小学生の時に習った、

$\\text{移動距離} = \\text{速度} \\times \\text{時間}$  
  
の式に方向がついたものを使います。って言っても、  
  
[![image.png](https://bookstack.yz-learning.com/uploads/images/gallery/2025-07/scaled-1680-/cULimage.png)](https://bookstack.yz-learning.com/uploads/images/gallery/2025-07/cULimage.png)

移動に、方向が加わるだけであとは小学校で習ったのと一緒です。  
上の図でいうと、  
****AからBに１フレームで距離１６移動している****  
****スピードは？****  
****下の図のようにx軸方向、ｙ軸方向にはどのぐらい移動してるかな？****  
みたいな話です。

![ベクトル演算を使ってゲームキャラクターを移動する方法｜Mizeee](https://assets.st-note.com/img/1714820191066-cXDJAEFoYV.png)### ゲームのフレーム（画面更新）の仕組み

****ゲームは1秒間に何回画面を更新している？****  
<span style="white-space: pre-wrap;">一般的には </span>****60FPS (Frame Per Second)****<span style="white-space: pre-wrap;"> が標準です。FPSとは「1秒あたりのフレーム数」を表し、フレームとは「画面を1回描き直すこと」です。</span>

- ****フレーム更新の流れ****
    1. キーボードやゲームパッドからの入力を取得
    2. ゲーム内のキャラクターや敵などの状態を計算（移動や当たり判定）
    3. 画面に描画する（スプライトや3DモデルをGPUへ指示）
    4. GPUがフレームバッファに描いた結果をディスプレイに表示
- ****ΔTime（デルタタイム）****  
    フレーム間の時間差（秒）を取得し、移動距離やアニメーションを滑らかにします。  
    例：1秒間に60回更新するなら、ΔTime = 1/60 ≒ 0.0167秒。
- ****なぜ60FPS？****
    - 人間の目がスムーズに映像と認識するには、30FPS以上あればOKですが、60FPSにするとより滑らかに感じられます。
    - ハードウェアやゲームの快適さを考慮したバランスです。

### 方向とフレーム間時間を考慮した移動処理

![image.png](https://bookstack.yz-learning.com/uploads/images/gallery/2025-07/scaled-1680-/H0jimage.png)１フレームごとにこんな感じに、計算していきます。

### 速度の変数を変更する

向きが決まったら、その方向にEnemyを進めてみよう。  
現在は、（わざとらしく）スピード設定が０になっている。

> ****行番号：16 行目を見て書き換え****

```
14  //#04 敵の移動速度を変更してみよう
 15  //ちょうどよい速さはどのぐらいかな？
 16  const float SPEED = 0.0f;  // #04
```

- ****やること****<span style="white-space: pre-wrap;">：行16 の </span>`<span class="editor-theme-code">0.0f</span>`<span style="white-space: pre-wrap;"> を変更する</span>
- ****学ぶこと****：フレームの原理、時間差分（ΔTime）による移動量計算

## その５．キャラクターAI（移動処理の追加）

### 移動パターンを考える

ゲームのキャラクタは、ゲーム内で得た情報を使っていろいろな情報を使って、その場面場面に適した動きを切り替えます（****状態遷移****）これらを自動的に行うのがAIの仕事になります。

- ****状態管理****：`<span class="editor-theme-code">enemyState_</span>`<span style="white-space: pre-wrap;"> による生存／死亡フェーズ切替</span>
- ****判定ロジック****：`<span class="editor-theme-code">isHitWa``ll()</span>`<span style="white-space: pre-wrap;"> や </span>`<span class="editor-theme-code">isHitPlayer()</span>`<span style="white-space: pre-wrap;"> で環境を認識</span>
- ****行動関数****：`<span class="editor-theme-code">TurnRight()</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">ChasePlayer()</span>`<span style="white-space: pre-wrap;"> など“何をするか”を切り出し</span>
- ****更新サイクル****：`<span class="editor-theme-code">UpdateEnemyAlive()</span>`<span style="white-space: pre-wrap;"> → 移動 or ターン → アニメーション更新</span>

簡単な移動のパターンを考えてみよう

- 往復運動
- 外周を回る
- ランダムに動く

現在のEnemyは、プレイヤーの情報などを知る手段がないので、自分ができる範囲（自分の位置、ステージの情報）を使ってできる移動はこのような感じになると思います。

### 往復運動

Enemyは、移動していて外周にあたった、という情報のみを知ることができます。  
****isHitWall()****が  
 ****true(真）のとき ：壁にぶつかった****  
 ****false(偽）のとき：ぶつかってない****

前に出てきた****条件分岐の処理****を応用することで、往復運動を実現できます。

1. ****移動方向****<span style="white-space: pre-wrap;"> </span>`<span class="editor-theme-code">forward_</span>`<span style="white-space: pre-wrap;"> を持ち、その方向に毎フレーム進む</span>
2. ****壁判定****：`<span class="editor-theme-code">isHitWall()</span>`<span style="white-space: pre-wrap;"> が true になると「壁に当たった」と認識</span>
3. ****反転（180度ターン）****：現在の方向を逆向きに変更
4. ****継続移動****：逆向きのまま動き続け、再び壁に当たるまで直進

つまり、右に進んで壁にぶつかったら左に進み、左の壁にぶつかったらまた右に進む、をくり返すことで****往復運動****を実現します。

### 反転の処理

> ****行番号：206行目を見て書き換え****

```c++
// #05 方向転換ロジック
void Enemy::Trurn180()
{
	if (forward_ == UP)
		forward_ = NONE;
	else if (forward_ == LEFT)
		forward_ = NONE;
	else if (forward_ == DOWN)
		forward_ = NONE;
	else if (forward_ == RIGHT)
		forward_ = NONE;

}
```

- ****やること****<span style="white-space: pre-wrap;">：206–217 行目の </span>`<span class="editor-theme-code">NONE</span>`<span style="white-space: pre-wrap;"> に反転方向を書く！</span>
- ****学ぶ****：関数定義、if-else による分岐ロジック

> ****行番号：53 行目を見て有効化****

```
51  //#06 敵の進行方向を変える関数を実装しよう
 52  // 外周を回ってみよう！壁に当たったら方向転換。
 53  if (isHitWall_) { TurnRight(); }  // #06
```

- ****やること****<span style="white-space: pre-wrap;">：コメントアウトされている場合は外し、壁判定後に </span>`<span class="editor-theme-code">Turn180()</span>`<span style="white-space: pre-wrap;"> が呼ばれるようにしよう</span>
- ****学ぶ****：ループ内判定、AI の行動サイクル（判定→行動→移動）

### 応用

同様に、TurnLeftかTurnRightを使って、外周を回り続けるには？

# 避けゲー

[![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. ****フレーム落ちで急に遅くなる現象（スタッタリング）****：一時的にフレームレートが下がると移動距離も一気に減る／飛び跳ねたように見えることがある。

##### 追加の対策例

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

---

---

## 発展課題

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

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