Skip to main content

3D避けゲームを作ってみよう

体験入学用教材(30~40分)

3D避けゲームを完成させよう

今回は、3本のレーンを左右に移動しながら、前から来る敵を避ける3Dゲーム作り完成させます。

完成しプログラムのすべてを最初から書くのではなく、用意されているプログラムTODO部分を順番、少しずつ穴埋めをしていき完成させます。

1か所直し

1つの課題が終わるびに必ず

  1. プログラムを書く
  2. ビルドしてする
  3. 実行し、する
  4. ゲーム画面の変化を確認する

という流れで進めしょう


今日の目標

今日の体験では、次のことを学びます。

  1. プログラムから3Dモデルを読み込む方法
  2. 数学の式を使って、移動の速完成を求める方法
  3. 速さ × 時間で、そのフレームの移動距離を求める方法
  4. キー入力でキャラクターを動かす方法
  5. 時間に応じてスコアを増やす方法
  6. 敵に当たったときにゲームオーバーにする方法

今日作るゲーム

このゲームではを完成させると、次のようなことが起こできるようになります。

  • プレイヤー、敵、背景3Dで画面に表示される
  • 道路READYのカウントダウン後にゲーム前へ流れる
  • 敵が前から来始ま
  • 左右キーで3本のプレイヤーがレーン移動でき
  • プレイ時間がたつとに応じてスコアが増える
  • 敵に当たるとゲームオーバーになる

今日の作業では、ゲームづくりでよく使う次の考え方にも触れます。

  • 変数
  • 関数呼び出し
  • Init()Update()Draw()
  • フレームとFPS
  • deltaTime
  • 入力処理
  • 条件分岐
  • 距離・時間・速さ
  • 当たり判定

作業を始める前に

Visual StudioでPlayScene_Exercise.cppを開いてください。

ファイルを開いたら、Ctrl + Fで次の文字を検索します。

TODO

今回修正する場所には、TODO 01からTODO 06までの番号が付いています。

【画像挿入①】完成1つのTODOが終わったら、必ずビルドてから実行してください。
エラーが出ゲーム画面場合は、次スクリーンショットTODOへ進まず、今書いた場所入れる

例:プレイヤー、道路、敵、スコアが見えている画面確認します。

エラーが出たときに確認すること

  • 英字の大文字と小文字が合っているか
  • "を消していないか
  • ()の数が合っているか
  • 行の最後に;が付いているか
  • 全角文字をコードの中へ入力していないか

1.ゲームは変数と関数で動いている

変数は、ゲームの情報を覚える箱

プログラムの中心 PlayScene、ゲームに必要な情報を変数に保存します。

今回いちばん注目する変数は、数値や状態に名前を付けて覚えておくためPlayScene です。

このゲームでは、たとえば次の情報を変数で管理しています。

変数覚えている情報
PlayScenestateTimer_ カウントダウンなどの残り時間
score_現在のスコア
scrollSpeed_道路や敵が流れる速さ
state_READY、プレイ中、ゲーム全体オーバー中など状態
backgroundHandle_読み込んだ背景画像を使うための番号

画像や3Dモデルそのものを、変数の小さな箱へ入れているわけではありません。

画像やモデルを読み込むと、それを使うための番号や情報が作られます。プログラムは、その番号を変数に覚えておき、必要なときに画像やモデルを表示します。

変数の値が変化すると、ゲーム画面にも変化が起こります。

  • 位置の値が変わると、キャラクターが移動する
  • stateTimer_が減ると、カウントダウンが
  • score_が増えると、画面の得点が増える
  • プレイヤーの状態が変わると、走る・倒れるなどのアニメーションが切り替わる

関数は、処理書くまとめた命令

台本関数は、何らか役目処理をまとめて名前を付けたものです。

たとえば、次のような命令を関数として用意できます。

Jump();

このコードは、Jumpという名前の関数を呼び出しています。

関数を呼び出すときは、基本的次の形で書きます。

関数名();

関数によっては、丸かっこの中へ情報を渡します。

LoadModel("ファイルの場所");

丸かっこの中へ渡す情報を引数と呼びます。

今回のプログラムでも、関数を使って次のような命令を出します。

player_->SetRunState();
stage_->StartScroll();
enemies_->SetSpawning(true);

つまり PlayScene を読むと、

  • いつゲームが始まるのかモデルを読み込む
  • 背景画像を読み込む
  • プレイヤーがどうを左へ移くのかさせる
  • プレイヤーを右へ移動させる
  • 敵との当たり判定を調べる

2.ゲームは処理を繰り返して動いている

ゲームの画面は、1枚の画像を表示して終わりではありません。

入力を調べ、キャラクターの位置やスコアを更新し、新しい画面を描く処理を何度も繰り返しています。

今回使うPlaySceneには、大きく分けて次の3つの処理あります。

関数役割実行される回数
Init()モデルや画像なを準備する最初に1回
Update()入力、計算、当たり判定などを行ゲーム中に何度も
Draw()現在のゲーム画面を描くゲーム中に何度も

流れを図にすると、次のようになります。

ゲーム開始
    ↓
  Init()
    ↓
┌──────────────┐
│   Update()   │
│      ↓       │
│    Draw()    │
└──────┬───────┘
       │
       └─ 何度も繰り返す

この繰り返しをゲームループと呼びます。

1フレームとは

Update()でゲームの状態を更新し、Draw()で画面を描く1回分を、1フレームと考えます。

1秒間に何フレーム処理したかを表す値がFPSです。

FPSは、Frames Per Secondの略です。

$$ \mathrm{FPS}=\text{1秒間に処理するフレーム数} $$

たとえば60FPSなら、1秒間に約60回、更新と描画を行います。

ゲームは1フレームごとに少しずつ状態を変えるため、キャラクターが滑らかに動いて見えます。


3.TODO 01:モデルと背景を読み込もう

探す場所:PlayScene_Exercise.cpp157行目あたりにあるTODO 01

この課題で変わること

現在のプログラムでは、モデルと背景のファイル名が仮の名前になっています。

正しいファイル名を指定すると、プレイヤー、敵、背景がゲーム画面に表示されます。

Init()を探そう

TODO 01Init()の中にあります。

Init()は、ゲームを始める前に必要なものを準備する場所です。

次の3つの関数が並んでいます。

LoadPlayerModel("Assets/XXX.mv1");
LoadEnemyModel("Assets/XXXXXXX.mv1");
LoadBackgroundImage("Assets/XXXXXXXX.png");

それぞれの関数名を日本語で考えてみましょう。

関数読み込むもの
LoadPlayerModelプレイヤーの3Dモデル
LoadEnemyModel敵の3Dモデル
LoadBackgroundImage背景画像

.mv1は3Dモデルのファイル、.pngは画像ファイルです。

作業

  1. Assetsフォルダの中を確認する
  2. プレイヤーとして使うモデルのファイル名を探す
  3. 敵として使うモデルのファイル名を探す
  4. 背景として使う画像のファイル名を探す
  5. XXXなどの仮の部分を、見つけたファイル名へ書き換える
  6. Assets/や拡張子まで含めて、正確に入力する

書くときの注意

文字列は"で囲みます。

"Assets/ファイル名.拡張子"

次の部分は消さなつゲようにしてください。

  • 最初と最後の"
  • Assets/
  • モデルの.mv1
  • 画像の.png
  • 行末の;

動作確認

ビルドして実行します。

次の3つが表示されるか確認してください。

  • プレイヤムオーバーになるのか
  • 背景

この時点では、READYの表示止まったままでも問題ありません。次の課題でカウントダウンを動します。


4.TODO 02:カウントダウンタイマーを動かそう

探す場所:PlayScene_Exercise.cppの395行目あたりにあるTODO 02

この課題で変わること

現在は、READYのカウントダウンが止まっています。

残り時間をフレームごとに少しずつ減らすことで、カウントダウンが進み、ゲームが始まるようになります。


今日の進め方deltaTimeとは

今日は、次の順番で進めます。

  1. プレイヤーのモデルを表示する
  2. 移動の速さを計算する
  3. パソコンが1フレームで進む距離計算処理する
  4. 左右キーでレーン移動させる
  5. スコアを増やす
  6. 敵に当たったらゲームオーバーにする

大事
1問終わるごとに、ビルド → 実行 → 動作確認をしてください。


課題01 プレイヤーを表示しよう

学ぶこと

  • ファイルパス
  • 文字列
  • 3Dモデルの読み込み

説明

ゲームの中の3Dモデル時間は、プログラムの中いつも完全直接入っているわけで同じとりません。

プログラ

前のフレームから今回のフレームまでに経過した時間をファイルの場所を指定deltaTimeに保存して読み込みます。

$$ \Delta t=\text{前のフレームから今回までに経過しとえば時間} $$

60FPS付近なら1フイヤモデル時間およそ次のファイルです。

$$ \Delta t \approx \frac{1}{60} \approx 0.0167\ \text{秒} $$

30FPS付近なら、次のようになります。

$$ \Delta t \approx \frac{1}{30} \approx 0.0333\ \text{秒} $$

FPS1フレームのおよその時間
60FPS約0.0167秒
30FPS約0.0333秒

30FPSでは1回に進む時間が大きく、60FPSでは1回に進む時間が小さくなります。しかし、1秒間に積み重ねると、どちらも約1秒になります。


タイマーを減らす考え方

現在の残り時間を(T_{\mathrm{now}})、次のフレームの残り時間を(T_{\mathrm{next}})とします。

毎フレーム、経過した時間だけ残り時間を減らします。

$$ T_{\mathrm{next}}

T_{\mathrm{now}}

\Delta t $$

プログラムでは、次の変数が対応しています。

数式プログラム
(T)stateTimer_
(\Delta t)deltaTime

作業

TODO 02を探します。

現在は、stateTimer_へ同じstateTimer_を代入しているため、値が変わりません。

Assets/run.mv1stateTimer_ = stateTimer_;

穴埋め問題

右辺を、次の空欄意味、プレイヤーのファイル名を入れなる式へ変更してください。

constexpr現在の残り時間 const char* PLAYER_MODEL_PATH =
    "Assets/______.mv1";1フレームの経過時間

ヒント

使用する変数は次の2つです。

  • 残り時間:runstateTimer_
  • 1フレームの経過時間:deltaTime

想定解答

constexpr const char* PLAYER_MODEL_PATH =
    "Assets/run.mv1";

動作確認

  • ビルドして実行しま

    次の順番で変化するか確認してください。

    1. READYの数字が減る
    2. GOが表示され
    3. プレイヤーの3Dモデル表示されか確認
    4. 道路が動き始める
    5. 敵が出現する

5.関数を呼ぶと、複数のものが動き始める

カウントダウンが終了すると、プログラムはStartGameという関数を呼びます。

StartGameの中では、プレイヤー、道路、敵へそれぞれ命令を出しています。

StartGame
├─ プレイヤーを走る状態にする
├─ 道路のスクロールを始める
└─ 敵の出現を始める

1つの関数を呼ぶことで、ゲーム開始に必要な複数の処理をまとめて実行できます。

このように、複雑なゲームでも、処理を小さな関数へ分けることで分かりやすく整理できます。


6.TODO 03:左右キーの入力を完成させよう

【画像挿探す場所:PlayScene_Exercise.cppの105行目あたりにあるTODO 03

この課題で変わること

左矢印キーと右矢印キーを押したとき、プレイヤーへレーン移動の命令を出せるようにします。

②】力処理とif

Update()はゲーム中に何度も呼ばれます。

その中でキーの状態を調べると、プレイヤーが表示キーを押したタイミングに反応できます。

if (条件)
{
    条件が成立したときの処理
}

ifは、「もし条件が成立したら、中の処理を実行する」という命令です。

次のコードは、左矢印キーが押されたゲーム画面かを調べています。

if (Input::IsKeyDown(KEY_INPUT_LEFT))
{
    // 左へ移動する命令
}

右矢印キーは、else ifで調べています。

else if (Input::IsKeyDown(KEY_INPUT_RIGHT))
{
    // 右へ移動する命令
}

課題02

作業

1レ

TODO 03-1TODO 03-2の空いている場所へ、プレイヤン分を移動させる関数呼び出しを書きます。

使用する関数名は次のとおりです。

方向使用する関数名
MovePlayerLeft
MovePlayerRight

関数呼び出しの書き方を思い出してください。

関数名();

左側には左移動の関数、右側には右移動の関数を使います。

動作確認

ビルドして実行します。

この時点では、キー入力の処理は完成しますが、プレイヤーの移動速度0.0fになっています。

そのため、左右キーを押しても、まだプレイヤーが動かないことがあります。

これは失敗ではありません。

キー入力の命令は完成
        +
移動速度はまだ0
        ↓
移動する距離が0

次の課題で移動速度を計算すると、実際にレーン移動するようになります。


7.TODO 04:レーン移動の速さを計算しよう

探す場所:PlayScene_Exercise.cppの26行目あたりにあるTODO 04

この課題で変わること

現在のプレイヤー移動速度は0.0fです。

レーン間の距離と、移動にかける時間から必要な速さを計算し、左右キーでプレイヤーが動くようにします。


距離・時間・速さの関係

一定の速さで移動するとき、距離、時間、速さには次の関係があります。

$$ d = vt $$

記号意味
(d)距離
(v)速さ
(t)時間

今回は速さ(v)を求めたいので、式を次の形にします。

$$ v=\frac{d}{t} $$

つまり、

$$ \text{速さ}

\frac{\text{距離}}{\text{時間}} $$

です。


ミニワーク:必要な速さを求めよう

学ぶこと

  • 距離
  • 時間
  • 速さ
  • 定数
  • 割り算

説明

このゲームでは、プレイヤーは3本のレーンを移動します。

レーンの位置は次のようになっています。

$$ 左 = -12, \quad 中央 = 0, \quad 右 = 12 $$

つまり、中央レーンから隣のレーンまでの距離は 12 12.0です。

今回は、0.20秒でちょうど1レーン移動0.20秒で終わらたいとします。

ここで使う次のは次ですへ数値を入れて、必要な速さを計算してください

$$ 距離v

= 速さ \times 時間 $$

この式を、速さについて変形すると

$$ 速さ = \frac{距離}{時間} $$

になります。

今回の数値を代入すると、

$$ 速さ =

\frac{12.0}{0.20}

= 60.0

\boxed{\qquad\qquad} $$

計算できたら、次の空欄へ記入してください。

必要な速さ:________

数値ではなく変数を使って式を書く

プログラムには、距離なり時間がすでに変数として用意されています。

つまり、1秒間に60進む速さにすれば、0.20秒で1レーン分移動できます。

【画像挿入③】レーンと距離の図を入れる

推奨画像ファイル:lane_speed_diagram.png

穴埋め問題

constexpr float LANE_DISTANCE = 12.0f;
constexpr float LANE_MOVE_TIME = 0.20f;
//
速さ= ÷
内容変数名
1レーン分の距離 LANE_DISTANCE
1レーンの移動時間 LANE_MOVE_TIME

TODO 04では、次の空欄へ変数名を入れます。

constexpr float PLAYER_MOVE_SPEED =
    ________ / ________;

ヒント

数式の

    $$

  • v=\frac{d}{t} $$

    と同じ順番になるように考えてください。

    計算結果の数字を直接書くのではなく、距離LANE_DISTANCE

  • 時間を表す変数を使って式を書きます。

    LANE_MOVE_TIME

想定解答

constexpr float PLAYER_MOVE_SPEED =
    LANE_DISTANCE / LANE_MOVE_TIME;

確認

  • ビルドして実行する
  • まだ左右キーは未完成なので、この時点では見た目の変化がなくてもよい
  • ただし、ビルドが通ることを確認する

課題03

1フレームで進む距離を求めよう

学ぶこと

  • deltaTime
  • 掛け算
  • 毎フレームの移動距離

説明

速さが決まっても、プレイヤムは、1回だを一度に1レーン分移動させるわ画面を描いて終わりではありません。
1秒間に何十回も、次の処理をくり返しています。

入力を調べる
↓
ゲームの状態を更新する
↓
画面を描画する

この1回分を1フレームと呼びます。

実際には、フレームごとに経過た時間ずつ位置 deltaTime とい変えています。

1フレームで進む距離は、次の式で求められます。

$$ 移動距離\Delta =x=v\Delta 速さ \times 時間t $$

プログラムでは、

$$

moveDistance=playerMoveSpeed_ $$

です。

たとえば、

$$ playerMoveSpeed_ = 60.0, \quad deltaTime = 0.016 $$

なら、

$$ moveDistance = 60.0 \times 0.016 = 0.96 $$

となります。

つまり、

記号 意味
(\timesDelta deltaTimex) そのフレームで0.96だけ進む距離
(v)プレイヤーの速さ
(\Delta t)1フレームの経過時間

この細かい位置更新や、レーン中央で止める処理は完成済みです。

今回は、ゲームの仕様から速さを計算することなり注目します。

動作確認

【画像挿入④】1フレームごとの移動距離の図を入れるビルドして実行します。

推奨画像ファイル:frame_move_diagram.png左右矢印キーを押し、次のことを確認してください。

穴埋め問題

UpdatePlayerLaneMove(
    ______ * ______
);

ヒント

  • 速さは playerMoveSpeed_プレイヤーが左右のレーンへ移動する
  • 時間は deltaTime1回の入力で隣のレーンへ移動する

想定解答

  • UpdatePlayerLaneMove( playerMoveSpeed_ * deltaTime ); 0.20秒
  • 補足

    この中の詳しい内部処理、たとえば

      で移動が終わる
    • レーンの中央付近ぴったりまる処理
    • 2回素早く押すと2レーン移動する処理

    は、今回は完成済みです。
    今日は「速さ × 時間で距離を出している」という考え方だけ押さえましょう。

    確認

    • ビルドして実行する
    • この時点ではまだ左右入力が未完成なので、見た目の変化がなくてもよい
    • ただし、移動距離の計算式が書けたことを確認する

    課題048.TODO 左右キーでプレイヤーを動かそう

    学ぶこと

    • キー入力
    • if
    • 関数呼び出し

    説明

    今度は、左右キーが押されたとき05:時間、レーン移動の命令を出します。

    今回使う命令は次の2つです。

    MovePlayerLeft();
    MovePlayerRight();
    

    穴埋め問題

    if (Input::IsKeyDown(KEY_INPUT_LEFT))
    {
        ______();
    }
    else if (Input::IsKeyDown(KEY_INPUT_RIGHT))
    {
        ______();
    }
    

    ヒント

    • 左へ動かす: MovePlayerLeft
    • 右へ動かす: MovePlayerRight

    想定解答

    if (Input::IsKeyDown(KEY_INPUT_LEFT))
    {
        MovePlayerLeft();
    }
    else if (Input::IsKeyDown(KEY_INPUT_RIGHT))
    {
        MovePlayerRight();
    }
    

    確認

    • ビルドし応じ実行する
    • 左右キーでプレイヤーがレーン移動するか確認する
    • 素早く2回押すと、2レーン移動できるか確認する

    【画像挿入⑤】左右移動しているゲーム画面


    課題05 スコアを増やそう

    探す場所:PlayScene_Exercise.cppの129行目あたりにあるTODO 05

    学ぶこの課題で変わること

    • 変数
    • 足し算
    • 時間経過

    説明

    ゲーム中現在は、時間がscore_へ同じscore_を代入しているつほどめ、スコアが増えません。

    1秒あたりの得点とdeltaTimeを使って、プレイ時間に応じてスコアを増やします。

    1秒あたりの得点

    プログラムには、次の定数が用意されています。

    constexpr float SCORE_PER_SECOND = 100.0f;
    

    これは、「1秒間プレイすると100点増える」という意味です。

    今回ただし、Update()1秒に1回ではなく1秒あたり100点ずつ間に何度も呼ばれます。

    そこで、1フレームでえるようにしていやす得点を次の式で求めます。

    $$ \Delta S=r\Delta t $$

    =100$$

    記号意味プログラム
    (\Delta S)今回増やす得点これから計算する値
    (r)1秒あたりの得点 SCORE_PER_SECOND
    (\Delta t)1フレームで増える点数は、

    の経過時間
    deltaTime

    $$ 今回増える得点 = 1秒あたり現在得点 \times deltaTime $$

    です。

    そのため、スコアを(S_{\mathrm{now}})、次更新式はスコアを(S_{\mathrm{next}})とすると、次のようになります。

    $$ score_S_{\mathrm{next}}

    = score_

    S_{\mathrm{now}} + SCORE_PER_SECONDr\Delta \times deltaTimet $$


    穴埋め問題作業

    TODO 05を探します。

    現在は次のコードになっています。

    score_ = score_;
    

    次の形になるように空欄を考えてください。

    score_ =
        score_ + ________ * ________;
    

    ヒント

    使うものは次の2つです。

    • 1秒あたりの得点SCORE_PER_SECOND
    • 今回1フレームの経過した時間deltaTime

    想定解答

    score_ = score_ + SCORE_PER_SECOND * deltaTime;
    

    変数名は、すぐ上のコードや表から探してください。

    動作確認

    • ビルドして実行しま

    • 次のことを確認してください。

      • プレイ中、スコアが少しずつ増えていくか確認する

      【画像挿入⑥】READY中はスコアが増え

    • ゲーム画面開始後にスコアが増える
    • 約1秒で、およそ100点増える

    なぜ毎フレーム1点ではいけないのか

    次のように毎フレーム固定で1点増やすとします。

    1フレームごとに1点追加
    

    60FPSなら1秒に約60点、30FPSなら1秒に約30点になります。

    これではパソコンの処理速度によって得点の増え方が変わります。

    deltaTimeを使うことで、FPSが変化しても、実際に経過した時間に合わせて得点を増やせます。


    9.TODO 06:敵との当たり判定を有効にしよう

    探す場所:PlayScene_Exercise.cppの134行目あたりにあるTODO 06


    この課題06で変わること

    現在は、プレイヤーが敵に当たったら触れてもゲームオーバーになりません。

    当たり判定を調べる関数を条件に使い、敵に当たったときだけ死亡処理を実行します。


    trueとfalse

    条件が成立していることをtrue、成立していないことをfalseで表します。

    if (false)
    {
        // この中は実行されない
    }
    

    現在のプログラムは条件が常にfalseなので、敵に当たっても中の処理へ入りません。

    戻り値

    関数は、処理を行ったあとに結果を返すことがあります。

    今回使う当たり判定の関数名はIsHitです。

    この関数は、当たり判定の結果を次のどちらかで返します。

    戻り値意味
    true敵に当たっている
    false敵に当たっていない

    作業

    TODO 06を探します。

    if (false)
    

    falseの部分を、当たり判定を調べる関数呼び出しへ変更してください。

    使用する関数名は次のとおりです。

    IsHit
    

    関数を呼び出すときは、関数名の後ろに丸かっこを付けます。

    条件部分には、関数が返したtrueまたはfalseが使われます。

    当たったときに実行される処理

    条件がtrueになると、次の処理が実行されます。

    敵に当たる
        ↓
    死亡アニメーションへ切り替える
        ↓
    道路の動きを止める
        ↓
    敵の出現を止める
        ↓
    ゲームオーバー画面へ移る
    

    動作確認

    ビルドして実行します。

    次のことを確認してください。

    1. 敵へプレイヤーを当てる
    2. プレイヤーのアニメーションが変化する
    3. 道路が停止する
    4. 敵の出現が停止する
    5. ゲームオーバー画面へ移る

    10.ゲーム全体の流れを確認しよう

    今回完成させた処理を、ゲームループへ当てはめて確認します。

    学ぶInit()で行ったこと

    • 条件分岐プレイヤーモデルを読み込む
    • 真偽値敵モデルを読み込む
    • 背景画像を読み込む
    • ゲームに必要なオブジェクトを準備する

    Update()で行ったこと

    • カウントダウンを減らす
    • 左右キーの入力を調べる
    • プレイヤーへ移動の命令を出す
    • 時間に応じてスコアを増やす
    • 敵との当たり判定を調べる
    • ゲームの状態を切り替える

    説明Draw()で行っていること

    • 背景を描く

    このプロジェクトでは、道路、草、木、プレイヤー、、スコアなどは、PlayScene::Draw()のあに別の描画処理が自動的に表示します。

    そのため、今回はDraw()のコードを変更していません。


    11.完成チェック

    次の項目をすべて確認してください。

    •  プレイヤーが当たったかどうかは、IsHit() という関数で調べます。

      • 当たってい表示されとき: true
      • 当たっていないとき: false敵が表示される
    • つまり、

      背景が表示される
    • if
    • (IsHit())
    • は、**「もし敵に当たっていたら」**という意味です。

      穴埋め問題

      if (______)
      {
          LoadDeadMotion();
          ChangeToGameOver();
          return;
      }
      

      READYのカウント

      IsHit()
      

      想定解答

      if (IsHit())
      {
          LoadDeadMotion();
          ChangeToGameOver();
          return;
      }
      

      確認

        ダウンが進む
      • ビルドして実行 カウントダウン後にゲームが始まる
      •  左矢印キーで左へ移動する
      •  右矢印キーで右へ移動する
      •  プレイヤーが約0.20秒で隣のレーンへ移動する
      •  ゲーム中にスコアが増える
      •  敵に当たるとゲームオーバーになるか確認する

      12.今回学んだこと

      変数

      【画像挿ゲームの位置、時間、得点、状態などを覚えておくために使います。

      関数

      処理をまとめた命令です。関数を呼ぶことで、モデルの読み込みやプレイヤーの移動などを実行できます。

      ゲームループ

      最初にInit()を実行し、その後はUpdate()Draw()を繰り返します。

      フレームとFPS

      ゲームは1フレームずつ少しずつ状態を変えています。FPSは、1秒間に処理するフレーム数です。

      deltaTime

      前のフレームから今回のフレームまでに経過した時間です。

      タイマー、移動、スコアなどを実際の時間に合わせて動かすために使います。

      ⑦】力処理

      Update()の中でキー入力を調べ、押されたキーに応じて関数を呼びます。

      距離・時間・速さ

      $$ v=\frac{d}{t} $$

      という関係を使い、ゲームの仕様からプレイヤーの移動速度を求めました。

      当たり判定

      敵に当たっているかを関数で調べ、その結果がtrueのときだけゲームオーバー直前、またはゲームオーバー画面


      まとめ

      今回の体験では、完成済みのゲームに穴埋め処理実行ながら、次のことを学びました。

      • 3Dモデルの読み込み
      • 数式を使った移動速度の計算
      • deltaTime を使った1フレームの移動距離
      • キー入力によるレーン移動
      • スコアの更新
      • 当たり判定によるゲームオーバー

      今日の特に大切な式は、次の2つです。

      1. 速さを求める式

      $$ 速さ = \frac{距離}{時間} $$

      2. 1フレームで進む距離を求める式

      $$ 移動距離 = 速さ \times 時間 $$

      ゲームプログラムでは、こうした数学の式が、そのままキャラクターの動きになります。


      最後の自由調整

      時間に余裕があれば、次の値を変えて遊んでみましょう。

      constexpr float LANE_MOVE_TIME   = 0.20f;
      constexpr float SCORE_PER_SECOND = 100.0f;
      

      試してみること

      • LANE_MOVE_TIME を小さくすると、レーン移動はどう変わるか
      • LANE_MOVE_TIME を大きくすると、操作感はどう変わるか
      • SCORE_PER_SECOND を大きくすると、スコアはどう変わるか

      教員用メモ

      この教材で完成済みにしているもの

      • 背景画像の読み込み
      • カウントダウン
      • ゲーム開始命令
      • 道路と敵の移動
      • 位置補正
      • 2レーン移動の内部処理
      • ゲームオーバー遷移の内部処理

      画像ファイル案

      • 画像① 完成ゲーム画面
      • 画像② プレイヤー表示画面
      • 画像③ lane_speed_diagram.png
      • 画像④ frame_move_diagram.png
      • 画像⑤ 左右移動画面
      • 画像⑥ スコア増加画面
      • 画像⑦ ゲームオーバー画面