デバッグ用動作を組み込む(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(); // 裏の画面を表の画面に瞬間コピー
}
ゲームオーバー処理の追加は次回。
コメント