2Dゲーム制作⑲(2025/07/30)

ゲームオーバーシーンの作成

変数を宣言(SceneGameOver.cpp)

//-----------------
// 変数の宣言
//-----------------
int gameOverImage;	//ゲームオーオバー画像のハンドル番号

画像サイズを定数定義(SceneGameOver.h)

//---------------------------------
// 定数定義
//---------------------------------
const int GAME_OVER_GRAPH_WID = 800;	//ゲームオーバー画像の幅
const int GAME_OVER_GRAPH_HID = 500;	//ゲームオーバー画像の高さ

関数に処理を追加(SceneGameOver.cpp)

//ゲームオーバーシーンの初期化処理
bool SceneGameOverInit(void)
{
	gameOverImage = LoadGraph("image/gameOver.png");
	if (gameOverImage == -1)false;//画像読み込みに失敗したら終了

	return true;
}
//ゲームオーバーシーンのメイン処理
void SceneGameOverMainProc(void)
{
	SceneGameOverUpdate();
	SceneGameOverDraw();
}
//ゲームオーバーシーンの更新処理
void SceneGameOverUpdate(void)
{
	if (nowSpaceKey == 0 && prevSpaceKey == 1) {
		sceneKind = SCENE_KIND_TITLE;
	}
}
//ゲームオーバーシーンの描画処理
void SceneGameOverDraw(void) 
{
	SetDrawScreen(DX_SCREEN_BACK);	//描画する画面を裏画面に設定
	ClearDrawScreen();		//描画する画面の内容を消去

	int xx = (WINDOW_WID - GAME_OVER_GRAPH_WID) / 2;
	int yy = (WINDOW_HIG - GAME_OVER_GRAPH_HID) / 2;
	DrawGraph(xx, yy, gameOverImage, true);

	//文字列表示
	DrawString(xx, yy, "Hit Space Key!!!", 0xffffff);


	ScreenFlip();					//裏画面を表画面と入れ替え
}

作成した処理を組み込み(main.cpp)


	//-----------------
	// ゲームループ
	//-----------------
	while (ProcessMessage() == 0 && CheckHitKey(KEY_INPUT_ESCAPE) == 0) {
		//スペースキーの状態を更新
		prevSpaceKey = nowSpaceKey;//現在のスペースキーの状態を前のキー状態にコピー
		nowSpaceKey = CheckHitKey(KEY_INPUT_SPACE);//現在のスペースキーの状態を更新
		//テスト用処理
		prevWKeyPress = nowWKeyPress;
		nowWKeyPress = CheckHitKey(KEY_INPUT_W);
		prevLKeyPress = nowLKeyPress;
		nowLKeyPress = CheckHitKey(KEY_INPUT_L);
		prevOKeyPress = nowOKeyPress;
		nowOKeyPress = CheckHitKey(KEY_INPUT_O);
		prevBKeyPress = nowBKeyPress;
		nowBKeyPress = CheckHitKey(KEY_INPUT_B);


		//シーンごとに呼び出す処理を変える
		switch (sceneKind) {
		case SCENE_KIND_TITLE:
			SceneTitleMainProc();
			break;
		case SCENE_KIND_GAME:
			SceneGameMainProc();
			break;
		case SCENE_KIND_GAMEOVER:
			SceneGameOverMainProc();
			break;
		default:
			break;
		}

	}

ここまででゲームスタート⇒ゲームオーバー⇒再スタート の流れができるようになった。


敵の攻撃処理の作成

敵弾発射の仕様はひとまず下記
1)各列の一番下の段にいる敵のみ弾を発射可能
2)プレイヤー弾同様、画面外に出たら非表示にし、再発射可能にする
3)同じタイミングで弾を打たないように、乱数を使用してランダムなタイミングで発射。
  まずは10%の確率で実装。発射確率はすべての敵で共通。

EnemyBullet.cpp、EnemyBullet.hを作成する。

まずはincludeと変数定義(EnemyBullet.cpp)

#include<DxLib.h>
#include"Enemy.h"
#include"EnemyBullet.h"

//--------------------------------
//変数宣言
//--------------------------------
int enemyBulletImage;	//敵弾画像のハンドル番号
int enemyBulletPosXArray[ENEMY_DISP_YNUM][ENEMY_DISP_XNUM];
int enemyBulletPosYArray[ENEMY_DISP_YNUM][ENEMY_DISP_XNUM];
bool enemyBulletFlgArray[ENEMY_DISP_YNUM][ENEMY_DISP_XNUM];

続いて定数定義(EnemyBullet.h)

#pragma once

//-----------------------
// 定数定義
// -----------------------
const int ENEMY_BULLET_MOVE = 3;	//敵弾の移動量
const int ENEMY_BULLET_WID = 16;	//敵弾の画像幅
const int ENEMY_BULLET_HIG = 25;	//敵弾の画像高さ

敵弾の初期化処理関数を作成(EnemyBullet.cpp)

//-------------------------------
//敵弾の初期化処理
//-------------------------------
bool EnemyBulletSysInit(void)
{
	enemyBulletImage = LoadGraph("image/enemyBullet.png");
	if (enemyBulletImage == -1)return false;
	return true;
}

プロトタイプ宣言(EnemyBullet.h)

//-------------------------------
//敵弾の初期化処理
//-------------------------------
bool EnemyBulletSysInit(void);

includeを追加して処理を組み込み(SceneGame.cpp)

#include "EnemyBullet.h"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//ゲームシーンの初期化処理
bool SceneGameSysInit(void)
{
	if (PlayerSysInit() == false)return false;			//画像の読み込みを行っている為、エラー(falseが返されたとき)に処理を終了させる
	if (PlayerBulletSysInit() == false)return false;	//画像の読み込みを行っている為、エラー(falseが返されたとき)に処理を終了させる
	if (EnemySysInit() == false)return false;			//画像の読み込みを行っている為、エラー(falseが返されたとき)に処理を終了させる
	if (EnemyBulletSysInit() == false)return false;		//画像の読み込みを行っている為、エラー(falseが返されたとき)に処理を終了させる
	return true;
}

画像解放用の処理を追加
(EnemyBullet.cpp)

bool EnemyBulletRelease(void)
{
	if (DeleteGraph(enemyBulletImage) == -1)return false;
	return true;
}

(Player.cpp)

bool PlayerRelease(void)
{
	if (DeleteGraph(playerImage) == -1)return false;
	return true;

プロトタイプ宣言を追加(Player.h)

bool PlayerRelease(void);

(PlayerBullet.cpp)

//プレイヤー弾画像の解放
bool PlayerBulletRelease(void)
{
	if (DeleteGraph(playerBulletImage) == -1)return false;
	return true;
}

プロトタイプ宣言を追加(PlayerBullet.h)

bool PlayerBulletRelease(void);

(Enemy.cpp)

bool EnemyRelease(void)
{
	for (int ii = 0; ii < ENEMY_PATTERNS; ii++) {
		if (DeleteGraph(enemyImage[ii] == -1))return false;
		}
        return true;
}

プロトタイプ宣言を追加(Enemy.h)

bool EnemyRelease(void);

(SceneTitle.cpp)

// タイトルシーンの解放処理
bool SceneTitleRelease(void)
{
	if (DeleteGraph(titleImage) == -1)return false;
	return true;
}

(SceneTitle.cpp)

//ゲームオーバーシーンのリリース処理
bool SceneGameOverRelease(void)
{
	if (DeleteGraph(gameOverImage) == -1)return false;
	return  true;
}

SceneGameRelease()に組み込み。(SceneGame.cpp)

//ゲームシーンの解放処理
bool SceneGameRelease(void)
{
	if (PlayerRelease() == -1)return false;
	if (PlayerBulletRelease() == -1)return false;
	if (EnemyRelease() == -1)return false;
	if (EnemyBulletRelease() == -1)return false;
	return;
}

ゲームの終了処理を修正(main.cpp)

	//----------------------
	// システムの終了処理
	//----------------------
	int result = 0;
	if (SceneTitleRelease() == false)result = -1;
	if (SceneGameRelease() == false)result = -1;
	if (SceneGameOverRelease() == false)result = -1;
	if(DxLib_End() == -1)result = -1;				// DXライブラリの終了処理

	return result;					// ゲームの正常終了

弾の初期化用関数を作成(EnemyBullet.cpp)

bool EnemyBulletInit(void)
{
	for (int yy = 0; yy < ENEMY_DISP_YNUM; yy++){
		for (int xx = 0; xx < ENEMY_DISP_XNUM; xx++) {
			enemyBulletPosXArray[yy][xx] = enemyBulletPosYArray[yy][xx] = 0;//弾の位置初期化
			enemyBulletFlgArray[yy][xx] = false;							//弾の存在フラグを初期化

		}
	}
}

プロトタイプ宣言(EnemyBullet.h)

//-------------------------------
//プロトタイプ宣言
//-------------------------------
bool EnemyBulletSysInit(void);
bool EnemyBulletInit(void);
bool EnemyBulletRelease(void);

初期化処理を組み込み(SceneGame.cpp)

void SceneGameInit(void)
{
	PlayerInit();
	PlayerBulletInit();
	EnemyInit();
	EnemyBulletInit();

}

敵弾射出処理を実装に当たって、条件と方法を確認
・ランダムに敵の残っている列をチェック
※その敵が撃った弾が画面内に存在していれば発射処理をスキップ
・その列で生存している敵が見つからなかった場合は発射処理をせずに別の列へチェックを進める
※X軸のループのなかにY軸のループを作ってチェックしていく※
・ランダムなクールタイムを挟むことで発射をランダムに

乱数の最大値と種を仕込む

(EnemyBullet.h)

//-----------------------
// 定数定義
// -----------------------
const int ENEMY_BULLET_MOVE = 3;	//敵弾の移動量
const int ENEMY_BULLET_WID = 16;	//敵弾の画像幅
const int ENEMY_BULLET_HIG = 25;	//敵弾の画像高さ
const int ENEMY_BULLET_RAND_MAX = 99;
const int ENEMY_BULLET_RAND_VALUE = 10;

(main.cpp)

//---------------------------
//初期化処理
//---------------------------
bool Init(void)
{
	//-----------------
	// システム設定
	//-----------------
	SetWindowText("2505889 ****");			// ウィンドウのタイトルの設定
	SetGraphMode(WINDOW_WID, WINDOW_HIG, 32);		// ウィンドウのサイズと色モードの設定
	ChangeWindowMode(true);							// ウィンドウの表示モード(true=ウィンドウモード/false=フルスクリーン)

	int err = DxLib_Init();							// DXライブラリの初期化
	if (err == -1)return false;

	SRand((unsigned int)time(NULL));


弾の発射処理を実装(EnemyBullet.cpp)

void EnemyBulletShot(void)
{
    static int cooldown = 0;
    if (cooldown > 0) {         // クールダウン中は撃たない
        cooldown--;
        return;
    }

    // 次の発射までのフレーム数をランダム化(例:15〜45F)
    cooldown = 15 + GetRand(30); // ← SRand は Init で呼ばれてるのでOK

    // ---- 撃つ列をランダムに選ぶ ----
    // 何回かトライして、敵の残ってる列を引けたら撃つ
    for (int tries = 0; tries < ENEMY_DISP_XNUM; ++tries) {
        int xx = GetRand(ENEMY_DISP_XNUM - 1);  // 列をランダム選択

        // その列で一番下の生存敵を探す(下→上へ走査)
        int shooterY = -1;
        for (int yy = ENEMY_DISP_YNUM - 1; yy >= 0; --yy) {
            if (enemyFlgArray[yy][xx]) {
                shooterY = yy;
                break;
            }
        }
        if (shooterY < 0) continue; // この列は全滅。別列を試す

        // ★列に弾が既に1発あるなら見送り(「1列1発」ルール)
        bool columnBusy = false;
        for (int yy = 0; yy < ENEMY_DISP_YNUM; ++yy) {
            if (enemyBulletFlgArray[yy][xx]) {
                columnBusy = true;
                break;
            }
        }
        if (columnBusy) continue;

        // 発射:敵の現在座標から出す(中央下端)
        enemyBulletFlgArray[shooterY][xx] = true;
        enemyBulletPosXArray[shooterY][xx] = enemyPosxArray[shooterY][xx] + (ENEMY_WID - ENEMY_BULLET_WID) / 2;
        enemyBulletPosYArray[shooterY][xx] = enemyPosyArray[shooterY][xx] + ENEMY_HIG;

        break; // 1フレームに1発だけ
    }
}

プロトタイプ宣言に追加(EnemyBullet.h)

//-------------------------------
//プロトタイプ宣言
//-------------------------------
bool EnemyBulletSysInit(void);
bool EnemyBulletInit(void);
bool EnemyBulletRelease(void);
void EnemyBulletShot(void);

ゲームシーンの更新処理に組み込み(SceneGame.cpp)

//ゲームシーンの更新処理
void SceneGameUpdate(void)
{
	PlayerMove();

	// プレイヤー弾の移動処理
	PlayerBulletMove();

	// プレイヤー弾の発射処理
	PlayerBulletShot();

	//敵の移動処理
	EnemyMove();

	//敵弾の発射処理
	EnemyBulletShot();


	// 当たり判定
	CollisionCheckPlayerBullet();

敵弾の移動処理の実装

・弾が存在しているかどうかのチェックをforループで行って、移動処理を行っていく。(EnemyBullet.cpp)

void EnemyBulletMove(void)
{
	for (int xx; xx < ENEMY_DISP_YNUM; xx++) {
		for (int yy = ENEMY_DISP_YNUM - 1; yy >= 0; yy--) {
			if (enemyBulletFlgArray[yy][xx] == true) {
				//存在している敵弾のみ移動させる
				enemyBulletPosYArray[yy][xx] += ENEMY_BULLET_MOVE;
				if (enemyBulletPosYArray[yy][xx] > WINDOW_HIG) {
					//弾が画面外に移動
					enemyBulletFlgArray[yy][xx] = false;
				}
			}
		}
	}
}

敵弾の表示処理を実装する

・弾を撃った敵がやられて、弾が移動中の場合⇒次フレームでの発射チェックを行っているが、同列のほかの敵キャラは弾が撃てる…という状況が起こりうる。それが重なると、2発、3発と、打てる状況になるので、ゲームルールによっては処理を組み込む必要がある。今回は仕様として処理。

(EnemyBullet.cpp)

void EnemyBulletDraw(void)
{
	for (int xx = 0; xx < ENEMY_DISP_XNUM; xx++) {
		for (int yy = ENEMY_DISP_YNUM - 1; yy >= 0; yy--) {
			if (enemyBulletFlgArray[yy][xx] == true) {
				//存在している敵弾のみ描画
				DrawGraph(enemyBulletPosXArray[yy][xx], enemyBulletPosXArray[yy][xx], enemyBulletImage, true);
			}
		}
}

プロトタイプ宣言も行う(EnemyBullet.h)

//-------------------------------
//プロトタイプ宣言
//-------------------------------
bool EnemyBulletSysInit(void);
bool EnemyBulletInit(void);
bool EnemyBulletRelease(void);
void EnemyBulletShot(void);
void EnemyBulletDraw(void);

敵弾x自機の当たり判定を実装。
敵弾座標テーブルと存在フラグテーブルをextern宣言(EnemyBullet.h)

当たり判定を実装(SceneGame.cpp)

//衝突判定
bool CollisionCheckEnemyBullet(void)
{
	if (playerFlg == false) return false;
	{
		for (int yy = 0; yy < ENEMY_DISP_YNUM; yy++) {
			for (int xx = 0; xx < ENEMY_DISP_XNUM; xx++) {
					if (enemyBulletFlgArray[yy][xx] == true) {
						//敵弾が存在している
						int posx = enemyBulletPosXArray[yy][xx];
						int posy = enemyBulletPosYArray[yy][xx];
						if ((playerPosY + PLAYER_HIG > posy)
							&& (playerPosY < posy + ENEMY_BULLET_HIG)
							&& (playerPosX < posx + ENEMY_BULLET_WID)
							&& (playerPosX + PLAYER_WID > posx)){
							//プレイヤーに命中
							enemyBulletFlgArray[yy][xx] = false;
							playerFlg = false;
							return true;
						}
					}
			}

		}
	}
	return false;
}

作成した関数を組み込み(SceneGame.cpp)

//ゲームシーンの更新処理
void SceneGameUpdate(void)
{
	PlayerMove();


	// プレイヤー弾の移動処理
	PlayerBulletMove();

	// プレイヤー弾の発射処理
	PlayerBulletShot();


	//敵の移動処理
	EnemyMove();

	//敵弾の移動処理
	EnemyBulletMove();

	//敵弾の発射処理
	EnemyBulletShot();


	// 当たり判定
	CollisionCheckPlayerBullet();
	CollisionCheckEnemyBullet();

いったん動くものになった。次回以降クラス化。


※ダブルバッファリング
裏画面に描画⇔表画面に切り替え を繰り返してアニメーションさせる方式

コメント

タイトルとURLをコピーしました