C++ DXライブラリ ゲーム開発 プログラミング講座

シューティングゲームを作る part8.シーン管理をする

 


 

まとめ

  • タイトル画面,ゲーム画面,リザルト画面のシーン管理を行った

はじめに

前回はこちら.
敵が自機狙い弾をうってくるようにしている.

どうやってシーンを管理するか

今回は,一番シンプルな方法で実現しようと思う.
その方法とは, switch 文である.

つまり,
各シーンを switch 文で分岐し,
その分岐内で各シーンの処理を行おうというわけだ.

シーン管理を実装する

まずは各シーンを表す enum を用意する.

enum Scene
{
SCENE_TITLE,
SCENE_GAME,
SCENE_RESULT
};

そして,ゲームループ内で,
enum で分岐させる.

Scene m_currentScene = SCENE_TITLE;
// ゲームループ
while( ProcessMessage() == 0 )
{
switch( m_currentScene )
{
case SCENE_TITLE:
break;
case SCENE_GAME:
break;
case SCENE_RESULT:
break;
default:
assert(false);
break;
}
}

これでシーン管理は完了だ.
あとは,各シーンで必要な処理してやれば良い.
(例えばタイトルであれば文字表示など)

シーンを移動させたい時は,
m_currentScene に移動先のシーンを指定すれば良い.

例えば,タイトル画面からゲーム画面の遷移は,

if( CheckHitKey( KEY_INPUT_RETURN ) == 1 )
{
m_currentScene = SCENE_GAME;
}

のような感じだ.

完成コード

#include "DxLib.h"
#include <math.h>
#include <assert.h>
#define ShotNum 100
#define EnemyNum 10
#define EnemyShotNum 100
enum Scene
{
SCENE_TITLE,
SCENE_GAME,
SCENE_RESULT
};
float playerPosX = 320.0f;
float playerPosY = 340.0f;
float shotPosX[ ShotNum ];
float shotPosY[ ShotNum ];
float enemyPosX[ EnemyNum ] = {};
float enemyPosY[EnemyNum] = {};
int enemyLife[ EnemyNum ] = {};
int enemyDeathFlg[ EnemyNum ] = {};
float enemyShotPosX[EnemyShotNum] = {};
float enemyShotPosY[EnemyShotNum] = {};
float enemyShotVecX[EnemyShotNum] = {};
float enemyShotVecY[EnemyShotNum] = {};
int counter = 0;
int shotId = 0;
int enemyShotId = 0;
int deathFlg = 0;
int killNum = 0;
// 色を定義
int white  = 0xffffffff;
int red        = 0xffff0000;
int green  = 0xff00ff00;
int blue   = 0xff0088ff;
void DrawEnemyShot( float x, float y )
{
DrawBox(x-4, y-4, x+4, y+4, red, 0);
}
void DrawEnemy( float x, float y )
{
DrawBox(x-16, y-16, x+16, y+16, red, 0);
DrawPixel( x, y, red );
}
void DrawPlayer( float x, float y )
{
// 本体の表示
DrawBox(x-16, y-16, x+16, y+16, 0xff0088ff, 0);
}
void DrawShot( float x, float y )
{
DrawBox(x-2, y-16, x+2, y+16, 0xff0088ff, 0);
}
void PlayerVSEnemy()
{
// エネミーループ
for( int j = 0; j < EnemyNum; j++ )
{
float x, y, r;
x = playerPosX - enemyPosX[ j ];
y = playerPosY - enemyPosY[ j ];
r = 32;
if( x*x + y * y < r*r )
{
deathFlg = 1;
}
}
}
void PlayerVSEnemyShot()
{
// エネミーループ
for( int j = 0; j < EnemyShotNum; j++ )
{
float x, y, r;
x = playerPosX - enemyShotPosX[ j ];
y = playerPosY - enemyShotPosY[ j ];
r = 20;
if( x*x + y * y < r*r )
{
deathFlg = 1;
}
}
}
void ShotVSEnemy()
{
// ショットループ
for( int i = 0; i < ShotNum; i++ )
{
// エネミーループ
for( int j = 0; j < EnemyNum; j++ )
{
float x, y, r;
x = shotPosX[ i ] - enemyPosX[ j ];
y = shotPosY[ i ] - enemyPosY[ j ];
r = 32;
if( x*x + y * y < r*r )
{
enemyLife[ j ]--; // 敵体力を減らす
shotPosX[ i ] = -100; // 画面外に出す
}
}
}
}
void UpdateEnemyShot()
{
for( int i = 0; i < EnemyShotNum; i++ )
{
// まだ生きている
DrawEnemyShot(enemyShotPosX[i], enemyShotPosY[i]);
enemyShotPosX[ i ] += enemyShotVecX[ i ];
enemyShotPosY[ i ] += enemyShotVecY[ i ];
}
}
void InitEnemyShot()
{
for( int i = 0; i < EnemyShotNum; i++ )
{
enemyShotPosX[ i ] = 0;
enemyShotPosY[ i ] = 0;
enemyShotVecX[ i ] = 0;
enemyShotVecY[ i ] = 0;
}
}
void StartEnemyShot(  float x, float y, int id  )
{
enemyShotPosX[ id ] = x;
enemyShotPosY[ id ] = y;
float xv = playerPosX - enemyShotPosX[ id ];
float yv = playerPosY - enemyShotPosY[ id ];
float v = sqrt( xv*xv + yv*yv );
enemyShotVecX[ id ] = ( xv / v )  * 2;
enemyShotVecY[ id ] = (  yv / v ) * 2;
}
void UpdateEnemy()
{
for( int i = 0; i < EnemyNum; i++ )
{
if( enemyLife[ i ] < 0 )
{
if( enemyDeathFlg[ i ] != 1 )
{
killNum++;
enemyDeathFlg[ i ] = 1;
}
enemyPosX[ i ] = -100; // 雑に画面外に
continue;
}
// まだ生きている
if( counter % 120 == 0 )
{
StartEnemyShot( enemyPosX[ i ], enemyPosY[ i ], enemyShotId);
enemyShotId++;
if( enemyShotId >= EnemyShotNum )
{
enemyShotId = 0;
}
}
DrawEnemy(enemyPosX[i], enemyPosY[i]);
enemyPosY[i] += 1;
}
}
void StartEnemy( float x, float y, int id )
{
enemyPosX[id] = x;
enemyPosY[id] = y;
enemyDeathFlg[ id ] = 0;
enemyLife[ id ] = 4;
}
void UpdateShot()
{
float speed = 40;
for( int i = 0; i < ShotNum; i++ )
{
shotPosY[ i ] -= speed;
DrawShot( shotPosX[ i ], shotPosY[ i ] );
}
}
void StartShot( float x, float y, int id )
{
shotPosX[id] = x;
shotPosY[id] = y;
}
void UpdatePlayer()
{
if( CheckHitKey( KEY_INPUT_RIGHT ) == 1 )
{
playerPosX += 5;
}
if( CheckHitKey( KEY_INPUT_LEFT ) == 1 )
{
playerPosX -= 5;
}
if( CheckHitKey( KEY_INPUT_UP ) == 1 )
{
playerPosY -= 5;
}
if( CheckHitKey( KEY_INPUT_DOWN ) == 1 )
{
playerPosY += 5;
}
if( CheckHitKey( KEY_INPUT_Z ) == 1 )
{
StartShot( playerPosX, playerPosY, shotId );
shotId++;
// 100 発を超えたら 1 発目に戻す
if( shotId >= 100 )
{
shotId = 0;
}
}
DrawPlayer( playerPosX, playerPosY );
}
// プログラムは WinMain から始まります
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
//int main()
{
ChangeWindowMode( true );
if( DxLib_Init() == -1 )     // DXライブラリ初期化処理
{
return -1 ;          // エラーが起きたら直ちに終了
}
Scene m_currentScene = SCENE_TITLE;
// ゲームループ
while( ProcessMessage() == 0 )
{
ClearDrawScreen(); // 画面を消す
switch( m_currentScene )
{
case SCENE_TITLE:
DrawString(  100,  100, "シューティングゲーム" , white );
DrawString(  200,  140, "Enter を押してね" , white );
if( CheckHitKey( KEY_INPUT_RETURN ) == 1 )
{
InitEnemyShot();
killNum = 0;
deathFlg = 0;
counter = 0;
m_currentScene = SCENE_GAME;
for( int i = 0; i < EnemyNum; i++ )
{
StartEnemy( GetRand(500) + 32, -40*i, i );
}
}
break;
case SCENE_GAME:
UpdatePlayer();
UpdateShot();
UpdateEnemy();
UpdateEnemyShot();
ShotVSEnemy();
PlayerVSEnemy();
PlayerVSEnemyShot();
counter++;
if( killNum >= 10 || deathFlg == 1 || counter >= 900 )
{
m_currentScene = SCENE_RESULT;
}
break;
case SCENE_RESULT:
if( killNum >= 10 )
{
DrawString(  0,  0, "クリア!" , white );
DrawString(  0,  40, "スペースキーでタイトルに戻る" , white );
}
else
{
DrawString(  0,  0, "ゲームオーバー" , red );
DrawString(  0,  40, "スペースキーでタイトルに戻る" , white );
}
if( CheckHitKey( KEY_INPUT_SPACE ) == 1 )
{
m_currentScene = SCENE_TITLE;
}
break;
default:
assert(false);
break;
}
ScreenFlip(); //裏画面を表画面に反映
}
WaitKey();
DxLib_End() ;               // DXライブラリ使用の終了処理
return 0 ;               // ソフトの終了 
}

おわりに

今回はシリーズの最終回.
一度書いたコードを崩してもう一回書いたり,
初歩的なことを噛み砕いて説明するのは思ったよりきつかった.

後半は明らかに説明が適当になっているためそこは反省点.

  • この記事を書いた人

GOTH

鹿児島県出身,吉祥寺在住の27歳.職業はゲーム会社でプログラマー.趣味はバイク,車,キャンプ,ガジェット,読書,そしてゲーム開発. サイトのテーマはプログラミングとガジェットでライフハック.たまに趣味に関する雑感や記録を残していく備忘録.ツイッターもやってます.
自己紹介
お問い合わせ

-C++, DXライブラリ, ゲーム開発, プログラミング講座