Skip to main content

避けゲー

体験実習 実習課題 避けゲー

準備目的(これを体験で学ぶ)

    • Game.zipゲームエンジニアリングの基礎を知る
    • C++ を解凍し、Visual Studio 2022で実際に動くゲームを作る流れを体験する
    • ソースコード で付属→ ビルド → 実行ファイル という開発ソリューショ基本パイプライを理解する
    • プロジェクトグラミングの基本(変数/順次・分岐・反復の3つの構造)開く。使えるようになる
    • image フォルダエラーが出たときの「原因を調べて直す」デバッグの流れを身つける
    • DxLib(ゲーム用ラトル用・ブラザルト用・猫用・蛇用)を使って画面に描画し、ゲーム画像構成要素配置す組み立て

    ゲーム構成例:title.png, result.png, cat.png, snake.pngこの体験で作る「避けゲー」の要素


課題01:実行してみてみよう

スクリーンショット 2025-08-01 013357.png

スクリーンショット 2025-08-01 013406.png

スクリーンショット 2025-08-01 013411.png

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

実習課題(ゲームをうごかてみよう

ファイル:PlayScene.cppここから先は、実習ステップ(やってみよう)

Let's Get Coding!
image.png


void TitleScene::Init()
{
    isFinished = false;

    // ToDo: 背景

課題01:画像を一度だけロ読み込んで表示させよ

  • 説明: プレイヤドするコードを追加しよう if ( bgImage == -1 ) { bgImage = LoadGraph( "______" ); // ← と敵の画像ファイル名入力 } }
  • 穴埋め正しく読み込まないと表示されない。"______"playerImage"image/cat.png"、"image/title.png"enemyImage に "image/snake.png" 入れて F5 で確認読み込む作業を行う

課題02:修正前(元のコード) 36~41行目辺り


if( playerImage == -1 )

//#課題01 LoadGraph関数を使って画像を読み込む

playerImage = LoadGraph( "image/xxx.XXX.png" );

if( enemyImage == -1 )

//#課題01 LoadGraph関数を使って画像を読み込む

enemyImage = LoadGraph( "image/xxxx.XXXX.png" );

注意点

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

やること

  1. Init() 内の LoadGraph 呼び出しを上記のように書き換える。
  2. ビルド & 実行してプレイヤーと敵の画像が表示されるか確認。
  3. 表示されなければファイル名やパスをチェック。

確認ポイント

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

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

スピードを表す変数に値を入れよう 

23行目辺り

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

//課題02 プレイヤーの移動速度を設定
playerSpeed = _____f; // 例: 150.0f
  • 左右キーを押したときに動く量が変わる。値を変えても動きを試してみよう。

用語と基礎説明

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


//課題02 プレイヤーの移動速度を設定

playerSpeed = 0.0f;


//課題03 敵の落下速度を設定

enemySpeed = 0.0f;


//課題04 敵の生成タイマーを加算

enemySpawnTimer = enemySpawnTimer + deltaTime;

//課題05 スコアの値を更新

score += deltaTime;


課題03:プレイ画面の初期設定をしよう

ファイル:PlayScene.cpp

void PlayScene::Init()
{
    isFinished = false;
    enemies.clear();

    // ToDo: プレイヤー初期位置を画面中央にセット(Y座標は地面から半分上)
    player.x = WINDOW_WIDTH / 2;
    player.y = GROUND_Y - CHARACTER_SIZE / 2;

    // ToDo: プレイヤーの移動速度を設定しよう
    playerSpeed = _____f;    // 例:150.0f

    // ToDo: 敵の落下速度を設定しよう

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

敵の落下速度を設定しよう  

26行目辺り?
//課題03 敵の落下速度を設定
enemySpeed = _____f; // 例: 150.0f

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

課題04:敵の生成(出現)タイマーを増やそう

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

ここでは、ゲームの進行管理で重要なフレームについて学ぶよ!

// ToDo:課題04 敵の出現間隔を設定しよう
    enemySpawnInterval = _____f; // 例:0.5f

    // ToDo: 敵生成タイマー初期化を加算
enemySpawnTimer = 0.0f;enemySpawnTimer + deltaTime; // ToDo: スコア初ここを書いて敵が定化
    score = 0.0;

    // ToDo: 画像とフォントを一度だけロード的に出るようにするコードを追加しよう
    if ( playerImage == -1 ) playerImage = LoadGraph( "______" );  // cat.png
    if ( enemyImage  == -1 ) enemyImage  = LoadGraph( "______" );  // snake.png
    if ( scoreFont   == -1 ) scoreFont   = CreateFontToHandle( "メイリオ", 64, 0, DX_FONTTYPE_NORMAL );
}
  • enemySpawnInterval を超えたら CreateEnemy() が呼ばれる。

仕組みの補足(フレームごとの動きとタイマーの関係)

  • ゲームは穴埋め例「フレーム」という単位で動いている。1フレームごとに画面を更新する。普通は1秒に60回くらい(60FPS)呼ばれる。
  • 毎フレーム、次の順番で処理が行われる
      1. playerSpeed = 150.0f;Update()(状態を変える:敵を落とす、タイマーを増やす、入力を読む)
      2. enemySpeedDraw()(画面に今の状態を描く)
    • deltaTime は前のフレームからの時間(たとえば 1/60 秒 = 150.0f;約0.0167)。これを enemySpawnTimer に足して「時間を数える」。
    • enemySpawnTimer が設定された間隔(enemySpawnInterval)を超えたら、その時点で新しい敵が出現する。つまり「何秒たったら出すか」をフレームをまたいで数えている。
    • 例:enemySpawnInterval = 0.5f;5f
    •  なら、0.5秒ごとに敵が出る。60FPS なら約30フレームごとに LoadGraph("image/cat.png"CreateEnemy() /が呼ばれる。

    LoadGraph("image/snake.png")enemySpawnTimer = enemySpawnTimer + deltaTime

 について(数学とプログラムの違い)

  • 数学の式だと左辺と右辺に同じ記号が出てくると変に見えるが、プログラムでは「今の値に新しい分を足して更新する」操作として普通に使う。
    これは「箱の中身を取り出して、そこに時間分を足して、また箱に戻す」処理と考えられる。
    例:今のタイマーが0.3秒で、deltaTime が0.0167なら、次のフレームでは 0.3167 秒になる。

課題04:プ05:スコアの更新を有効にしよう

イヤム間時間(deltaTime)左右に動かそ使ってスコアの変数を変化させよ

ファイル:PlayScene.cpp

93行目辺り
void//課題05 PlayScene::Update(floatスコアの値を更新
deltaTime)score {= score + ?????; // ToDo: 左右キーで player.x を更新しよう
    if ( CheckHitKey( KEY_INPUT_LEFT ) )
    {
        player.x -= _____f * deltaTime;   // playerSpeed を使おう
    }
    else if ( CheckHitKey( KEY_INPUT_RIGHT ) )
    {
        player.x += _____f * deltaTime;
    }

    // (以降は生き残った時間がスコアや敵生成ど…)
}

仕組みの補足(敵の出現タイマーと同じ考え方)

  • 穴埋めスコアも playerSpeedenemySpawnTimer と同じで、毎フレーム deltaTime足して時間を数えている。
  • score += deltaTime; は「生き残った時間」がそのまま点数になる仕組み。60FPSなら1フレームごとに約0.0167ずつ増える。

画面表示の豆知識

  • このスコア表示や文字の描画は DxLib が簡単にしてくれているから少ないコードで書ける。
    もし DirectX 単体で文字を表示しようとすると、フォントを読み込んでテクスチャを確認り、描画用の頂点バッファやシェーダを用意する必要があり、かなりコードが長くなる。
    DxLib はそういう面倒な下準備を隠してくれていて、DrawFormatStringToHandle などを呼ぶだけで表示できる

課題05:敵を定期的に生成しよう

ファイル:PlayScene.cpp (同じ Update 内)

// 一定時間後、敵生成
    // ToDo: タイマーに経過時間を加算
    enemySpawnTimer += Scene::DeltaTime();  // または deltaTime

    // ToDo: タイマーが間隔を超えたら CreateEnemy() を呼び出し、タイマーをリセット
    if ( enemySpawnTimer > enemySpawnInterval )
    {
        CreateEnemy();
    }
  • 確認:F5 で蛇(enemy)が落ちてくるかチェック。

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

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

86行目辺り

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

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

ファイル:PlayScene.cpp

bool PlayScene::IsHit()
{
    for ( auto& e : enemies )
    {
        float dx = player.x - e.x;
        float dy = player.y - e.y;

        // ToDo: 衝突判定を完成させよう距離の2乗 <= 半径の2乗下につなげる
        if ( (dx*dx) + (dy*dy) <= _____ )
        {
            return true;
        }
    }
    return false;
}

説明:ついに当たり判定です

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

image.png

条件分岐(if文)と真偽値

  • 当たり判定の結果は「ぶつかっているか」「ぶつかっていないか」の 真(true)/偽(false) で表される値(真偽値)として返される。voidIsHit() PlayScene::Update(floatはこれを返す関数で、ぶつかっていれば deltaTime)true {を、そうでなければ //false …(敵更新・描画の直前) if (を返す。
  • if( IsHit() ) { isFinished = true; // リザルトシーンへ... } }
  • のように書くと、「ぶつかったときだけ中の処理をする」ことができる。これが 穴埋め条件分岐 で、プログラミングの3つの基本構造(順次・分岐・反復)のひとつで非常に重要。処理を状況に応じて切り替えるための仕組み。
  • ここでは「当たっていたら敵を消す/ゲームオーバーにする」といった反応を CHARACTER_SIZE * CHARACTER_SIZEif入れ使っ確認実現している

サブルーチン(関数)としての IsHit() の役割

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

###課

題07:リザルト画面の背景へ移行する処理表示追加しよう
ファイル:ResultScene.cpp

89行目辺り
void ResultScene::Init()
{
    isFinished = false;	//課題06 ToDo:当たり判定
	背景画像をロードif( if ( bgImage == -10 )
	{
		bgImage = LoadGraph( "______" ClearEnemies();
		//課題07 たとえばリザルト画面へ移行
		"image/result.png"
    }//->ここに移行処理を追加
	}
  • 衝突後にリザルト用のシーンに変えるコードを追加。
  • 穴埋め補足"image/result.png"GoToResultScene() という関数が用意されている

画面遷移と Scene(状態遷移)の考え方

  • 多くのゲームは「Scene(シーン)」という単位で画面ごとの役割指定分けて管理、スペースキーでている。たとえばタイトルに戻画面、プレイ画面、リザルト画面がそれぞれ別の Scene。
  • 条件が満たされると Scene を切り替える。れを 状態遷移 を確認呼ぶ。たとえば当たり判定で衝突したら「プレイ中」から「リザルト」へ移る、といった具合

発展課題

  • BGMを追加GoToResultScene() や changeScene(...) は状態を変える関数で、現在の Scene の状態を終了して新しい Scene に移(遷移する)処理をまとめている。
  • ハイスコアをファイルに書き出してみる
  • プレイヤ状態遷移は画面遷移だけでなく、キャラクターの状態管理(立っている・走っている・ジャンプ中など)やアニメーション切り替え、敵のAIの振る舞いの切り替えなど、ゲームのあらゆる部分で使われている。
  • 状態遷移を整理しておくと、「何が起きたら次に何を表示するか/どう振る舞うか」が明確になり、ゲーム全体の流れや作りやすさが向上する。

発展課題

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

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