2Dゲーム制作⑱(2025/07/28)

デバッグ用動作を組み込む(GameScene.cpp)

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

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

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

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


	// 当たり判定
	CollisionCheckPlayerBullet();

	//--------------------------------------------
	// テスト用処理
	//--------------------------------------------
#ifdef _DEBUG
	if (nowWKeyPress == 0 && prevWKeyPress == 1) {
		//条件1:プレイヤーが勝った
		//敵キャラを無条件にすべてやられた状態にする
		for (int yy = 0;yy < ENEMY_DISP_YNUM;yy++) {
			for (int xx = 0; xx < ENEMY_DISP_XNUM;xx++) {
				enemyFlgArray[yy][xx] = false;
			}
		}
	}
	else if (nowLKeyPress == 0 && prevLKeyPress == 1) {
		//条件2:敵弾にプレイヤーが撃たれた
		playerFlg = false;
	}
	else if (nowOKeyPress == 0 && prevOKeyPress == 1) {
		//条件3:敵に最下段まで到達された
		//敵キャラをすべてのY座標を最下段に変更
		int chkpos = WINDOW_HIG - ENEMY_MOVE_Y_SPEED; //あと一歩で下端に到達する位置
		for (int yy = ENEMY_DISP_YNUM - 1;yy >= 0;yy--) {
			for (int xx = 0;xx < ENEMY_DISP_XNUM;xx++) {
				if (enemyFlgArray[yy][xx] == true) {
					enemyPosyArray[yy][xx] = chkpos - (ENEMY_MOVE_Y_SPEED * (ENEMY_DISP_YNUM - (yy + 1)));
				}
			}
		}
	}

✅ enemyPosyArray[yy][xx] = ... の目的:

最下段に敵が到達したイベントが発生したとき、
その行の敵キャラの表示Y座標を “実際の最下段相当” に更新するための式
(※ただし最下段の行から順にチェックして、残っている敵だけに適用)


✅ 各パーツの解釈(再掲)

chkpos = WINDOW_HIG - ENEMY_MOVE_Y_SPEED;
  • chkpos は、Y方向の「最下段」より一段上の位置。
  • なぜなら、**1マス分上(=ENEMY_MOVE_Y_SPEED分)**の位置をベースにしているから。

ENEMY_MOVE_Y_SPEED * (ENEMY_DISP_YNUM - (yy + 1))
  • yy は上から下へ 0~ENEMY_DISP_YNUM - 1
  • (ENEMY_DISP_YNUM - (yy + 1)) は「下から数えて何段目か」
  • よって、この数だけ“下方向”に移動させるための距離

✅ 全体の式として

enemyPosyArray[yy][xx] =
chkpos - (ENEMY_MOVE_Y_SPEED * (ENEMY_DISP_YNUM - (yy + 1)));

➤ 実質的にはこう言っている:

「最下段のy位置」から見て、上から何段目の敵かを逆算してY座標を調整する。


✅ 結果として何が起きる?

  • 敵を “下から数えて何段目か” に応じてY座標に変換する
  • 一番下の敵が chkpos に来る(=最下段到達)
  • それより上の敵は、その分だけ上に配置される(ただし、この条件では一番下の敵だけ処理)

✅ 実際の意味(自然言語でまとめ)

敵が最下段に到達したとき、
その行にまだ残っている敵(=敵フラグが立っている)を、
ちょうど最下段まで届いたようにY座標を調整して見せている。


💡 つまるところ:

  • Y座標を物理的に最下段に置くのではなく、
  • 「どの行にいる敵か」を考慮して、“最下段にいるように見せる”座標に変換してる

という、表現とロジックのすり合わせです。
(by ChatGPT)


ステージ遷移の実装

最大ステージ数の定数を定義(SceneGame.h)

const int STAGE_NUM_MAX = 5;//最大ステージ数
~~~~~~~~~~~~~~~~~~~~~~~~~
extern int nowStageNum;

ステージ番号を格納する変数を用意(SceneGame.cpp)

int nowStageNum;//現在のステージ番号

タイトル⇒ゲーム遷移時にステージを1にする(SceneTitle.cpp)

// タイトルシーンの更新処理
void SceneTitleUpdate(void)
{
	if (nowSpaceKey == 0 && prevSpaceKey ==1) {
		//アップトリガーでスペースキーの押下を判定
		nowStageNum = 1;
		sceneKind = SCENE_KIND_GAME;
	}
}

ステージ番号の表示を実装(SceneGame.cpp)

//ゲームシーンの描画処理
void SceneGameDraw(void)
{
	SetDrawScreen(DX_SCREEN_BACK);	// 描画する画面を裏の画面に設定する
	ClearDrawScreen();		// 描画する画面の内容を消去する

	// プレイヤーの描画
	PlayerDraw();

	// プレイヤー弾の描画
	PlayerBulletDraw();

	// 敵の描画
	EnemyDraw();

	//文字列表示
	DrawFormatString(10, 10, 0xffffff, "PlayerStock:%d", playerStock);
	DrawFormatString(10, 28, 0xffffff, "NowStage:%d", nowStageNum);

	ScreenFlip();			// 裏の画面を表の画面に瞬間コピー
}

※DrawFormatStringのy座標を18pixelずらす理由:printfで表示される文字列は大体16pixel。
 間隔が16pixelだと文字同士が接触するので、2pixel余裕を開けて18pixel。

ステージ終了種別によって分岐を用意する(SceneGame.cpp)

//ゲームシーンのメイン処理
void SceneGameMainProc(void)
{
	SceneGameUpdate();
	SceneGameDraw();

	//ステージが終了したか
	eStageExitConditionsKind exit = StageExitConditionsCheck();
	if (exit != ESTAGE_EXIT_NON) {
		switch (exit) {
		case ESTAGE_EXIT_CLEAR:
			break;
		case ESTAGE_EXIT_ENEMY_SHOT:
			break;
		case ESTAGE_EXIT_ENEMY_OCCUPATION:
		sceneKind = SCENE_KIND_GAMEOVER;
			break;
		}
		sceneKind = SCENE_KIND_GAMEOVER;
	}

}

クリア時の処理から作成していく。
現在勝利時は
①敵フラグすべてfalse
②表示座標がゲーム進行状態のまま
となっており、そのまま進んでもプレイできない。ステージ遷移時の初期化処理を実行するために、初期化関数を分割する。

bool PlayerSysInit(void)を新たに作成。bool PlayerInit(void)⇒void PlayerInit(void)に修正
※画像読み込みをPlayerSysInit()に移動するため、戻り値が不要になる。PlayerInit()内のreturnも削除する事※


bool PlayerSysInit(void)
{
	// プレイヤー画像の読込み
	playerImage = LoadGraph("image/player.png");
	if (playerImage == -1)return false;

	return true;
}

void PlayerInit(void)
{

	//-----------------
	// 変数の初期化
	//-----------------
	// プレイヤー
	playerPosX = WINDOW_WID / 2 - PLAYER_WID / 2;	// ウィンドウの中央X座標
	playerPosY = WINDOW_HIG - PLAYER_HIG;		// プレイヤーの中心座標
	playerFlg = true;
	playerStock = PLAYER_PLANE_MAX;			// プレイヤー機数を初期化

}

bool PlayerBulletSysInit(void)を新たに作成。bool PlayerBulletInit(void)⇒void PlayerBulletInit(void)に修正。画像の読み込みを移動。(PlayerBullet.cpp)

bool PlayerBulletSysInit(void) {

	// プレイヤー弾の画像の読込み
	playerBulletImage = LoadGraph("image/playerBullet.png");
	if (playerBulletImage == -1)return false;

	return true;
}

void PlayerBulletInit(void) {

	// プレイヤー弾
	playerBulletPosX = playerBulletPosY = 0;
	playerBulletFlg = false;

}

プロトタイプ宣言も修正(PlayerBullet.h)

//---------------------------------------
// プロトタイプ宣言
//---------------------------------------
bool PlayerBulletSysInit(void);
void PlayerBulletInit(void);

bool EnemySysInit(void)を新たに作成。bool EnemyInit(void)⇒void EnemyInit(void)に修正。画像の読み込みを移動。(Enemy.cpp)

bool EnemySysInit(void) {

	//敵の画像の読込み
	int err = LoadDivGraph("image/enemy_1.png", ENEMY_PATTERNS, ENEMY_PATTERNS, 1, ENEMY_WID, ENEMY_HIG, enemyImage);
	if (err == -1)return false;
	
	return true;
}

void EnemyInit(void) {


	//-----------------
	// 変数の初期化
	//-----------------

	// 敵キャラの変数テーブルの初期化
	for (int yy = 0; yy < ENEMY_DISP_YNUM; yy++) {
		for (int xx = 0; xx < ENEMY_DISP_XNUM; xx++) {
			enemyPosxArray[yy][xx] = (ENEMY_WID + ENEMY_DISP_X_DISTANCE) * xx;
			enemyPosyArray[yy][xx] = (ENEMY_HIG + ENEMY_DISP_Y_DISTANCE) * yy;
			enemyFlgArray[yy][xx] = true;
		}
	}

	enemyMoveDirection = ENEMY_DIR_RIGHT;
	enemyNextMoveDirection = ENEMY_DIR_NON;

	enemyMoveIntervalCounter = -1;//カウンタ初期化(00:15~)


}

プロトタイプ宣言も修正(Enemy.h)

//----------------------------------------
//プロトタイプ宣言
//----------------------------------------
bool EnemySysInit(void);
void EnemyInit(void);

ゲーム開始時の初期化処理をまとめた関数SceneGameSysInit()を作成。(SceneGame.cpp)

//ゲームシーンの初期化処理
bool SceneGameSysInit(void)
{
	if (PlayerSysInit() == false)return false;
	if (PlayerBulletSysInit() == false)return false;
	if (EnemySysInit() == false)return false;

	return true;
}

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

プロトタイプ宣言も修正(SceneGame.h)

//-------------------------------------------------
// プロトタイプ宣言
//-------------------------------------------------
bool SceneGameSysInit(void);//ゲーム起動時の初期化処理
void SceneGameInit(void);//ゲームシーンの初期化処理

タイトルでスペース押下時の処理にあるSceneGameInit()⇒SceneGameSysInit()に修正。(SceneTitle.cpp)

// タイトルシーンの更新処理
void SceneTitleUpdate(void)
{
	if (nowSpaceKey == 0 && prevSpaceKey ==1) {
		//アップトリガーでスペースキーの押下を判定
		SceneGameSysInit();
		nowStageNum = 1;
		sceneKind = SCENE_KIND_GAME;
	}
}

ここまで組み込んで、一応動く状態には戻った。このままでは、ステージクリア後に正しく初期化されない。ステージクリア時に初期化するための関数を作成する。(SceneGame.cpp)

//プレイヤー勝利時の再初期化処理
void SceneGameNextStageInit(void) {
	//敵キャラ関連の初期化
	EnemyInit();
	//プレイヤー関連の初期化
	//プレイヤーのY座標は常に最下段なので初期化不要
	//プレイヤー勝利時の呼び出しになるためPlayerFlagの更新も不要
	playerPosX = (WINDOW_WID - PLAYER_WID) / 2;	//横方向の画面中央に位置を設定
}

プロトタイプ宣言も追加(SceneGame.cpp)

//-------------------------------------------------
// プロトタイプ宣言
//-------------------------------------------------
bool SceneGameSysInit(void);//ゲーム起動時の初期化処理
void SceneGameInit(void);//ゲームシーンの初期化処理
void SceneGameNextStageInit(void);//ステージクリア時の初期化処理

ステージクリア時の処理を実装(SceneGame.cpp)

//ゲームシーンのメイン処理
void SceneGameMainProc(void)
{
	SceneGameUpdate();
	SceneGameDraw();

	//ステージが終了したか
	eStageExitConditionsKind exit = StageExitConditionsCheck();
	if (exit != ESTAGE_EXIT_NON) {
		switch (exit) {
		case ESTAGE_EXIT_CLEAR:
			//条件1:プレイヤー勝利
			//ステージ番号を1加算
			nowStageNum++;
			//ステージ番号が最大ステージ数を超えたらゲーム終了
			if (nowStageNum > STAGE_NUM_MAX)
			{
				sceneKind = SCENE_KIND_GAMEOVER;
			}
			else {
				//次のステージに進む際に敵の位置やフラグを再初期化
				SceneGameNextStageInit();
			}
			break;

		case ESTAGE_EXIT_ENEMY_SHOT:
			break;
		case ESTAGE_EXIT_ENEMY_OCCUPATION:
			sceneKind = SCENE_KIND_GAMEOVER;
			break;
		}
	}

}

自機がやられたときの処理を実装(SceneGame.cpp)

//ゲームシーンのメイン処理
void SceneGameMainProc(void)
{
	SceneGameUpdate();
	SceneGameDraw();

	//ステージが終了したか
	eStageExitConditionsKind exit = StageExitConditionsCheck();
	if (exit != ESTAGE_EXIT_NON) {
		switch (exit) {
		case ESTAGE_EXIT_CLEAR:
			//条件1:プレイヤー勝利
			//ステージ番号を1加算
			nowStageNum++;
			//ステージ番号が最大ステージ数を超えたらゲーム終了
			if (nowStageNum > STAGE_NUM_MAX)
			{
				sceneKind = SCENE_KIND_GAMEOVER;
			}
			else {
				//次のステージに進む際に敵の位置やフラグを再初期化
				SceneGameNextStageInit();
			}
			break;

		case ESTAGE_EXIT_ENEMY_SHOT:
			//条件2:プレイヤーが敵弾にやられた
			playerStock--;
			if (playerStock < 0) {
				//保有機数がなくなったらゲームオーバー
				sceneKind = SCENE_KIND_GAMEOVER;
			}
			else {
				//保有機数が残っていた場合に続行
				//プレイヤーのみ初期化
				playerPosX = WINDOW_WID / 2 - PLAYER_WID / 2;//横方向の画面中央
				playerFlg = true;
			}

			break;
		case ESTAGE_EXIT_ENEMY_OCCUPATION:
			sceneKind = SCENE_KIND_GAMEOVER;
			break;
		}
	}

}

敵が最下段に到達した場合の処理を追加
(テキストに準じた、4段戻すは不自然なのでBボタン押下時に1段戻す処理を追加。)
(PlayerBullet.cpp)

int bombStock;	//押し戻しの残り回数
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//敵の押し戻し処理
void Bomb(void) {

	if(bombStock > 0){
	bombStock--;
	for (int yy = 0;yy < ENEMY_DISP_YNUM;yy++) {
		for (int xx = 0; xx < ENEMY_DISP_XNUM;xx++) {
			if (enemyFlgArray[yy][xx] == true) {
				enemyPosyArray[yy][xx] -= ENEMY_MOVE_Y_SPEED;
			}
		}
	}
	}
	else {
		return;
	}
}

押し戻し処理関係を追加(PlayerBullet.h)

#pragma once

//-----------------
// 定数定義
//-----------------
// プレイヤー弾
const int PLAYER_BULLET_WID = 6;		// プレイヤー弾の画像の横サイズ
const int PLAYER_BULLET_HIG = 30;		// プレイヤー弾の画像の縦サイズ
const int PLAYER_BULLET_MOVE = 12;		// プレイヤー弾の移動速度
const int BOMB_MAX = 4;					// 押し戻しの最大回数

//----------------------------------------------
//他のファイルでも参照が必要な変数の extern宣言
//----------------------------------------------
extern int playerBulletPosX;		// プレイヤー弾のX座標
extern int playerBulletPosY;		// プレイヤー弾のY座標
extern bool playerBulletFlg;		// プレイヤー弾の発射状態管理用フラグ(true:表示、false:非表示)
extern int bombStock;				//押し戻しの使用回数

//---------------------------------------
// プロトタイプ宣言
//---------------------------------------
bool PlayerBulletSysInit(void);
void PlayerBulletInit(void);
void PlayerBulletShot(void);
void PlayerBulletMove(void);
void PlayerBulletDraw(void);
void Bomb(void);

押し戻し回数を描画処理に追加。(SceneGame.cpp)

//ゲームシーンの描画処理
void SceneGameDraw(void)
{
	SetDrawScreen(DX_SCREEN_BACK);			// 描画する画面を裏の画面に設定する
	ClearDrawScreen();		// 描画する画面の内容を消去する

	// プレイヤーの描画
	PlayerDraw();

	// プレイヤー弾の描画
	PlayerBulletDraw();

	// 敵の描画
	EnemyDraw();

	//文字列表示
	DrawFormatString(10, 10, 0xffffff, "PlayerStock:%d", playerStock);
	DrawFormatString(10, 28, 0xffffff, "BombStock:%d", bombStock);
	DrawFormatString(10, 46, 0xffffff, "NowStage:%d", nowStageNum);

	ScreenFlip();			// 裏の画面を表の画面に瞬間コピー
}

ゲームオーバー処理の追加は次回。

コメント

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