第二課題:インベーダーゲーム
【1】開始時点のソースコードの解説
まずは、前回と同じ様に main.cpp 一本である程度動く状態を作り、その後で、関数分割
ファイル分割の手順を踏む。ファイル分割して、敵を動かすあたりまでできたところで、クラス化に入っていく予定(7月中旬目途)。
※DxLibの設定
https://dxlib.xsrv.jp/use/dxuse_vscom2022.html
※ソースファイルのインポート
ソースファイルを右クリック ⇒ 追加 ⇒ 既存の項目

ファイルを選択して[追加]。

※ビルドについて

・ソリューションのビルド:変更点だけをビルド
・ソリューションのリビルド:プロジェクト内のすべてをビルド
インポートしたmain.cppファイル。ここにコードを書き加えていく。
//-----------------------------------------------------------------------------
// インベーダーゲーム
// 氏 名:
//-----------------------------------------------------------------------------
#include <DxLib.h>
#include <time.h>
//-----------------
// 定数定義
//-----------------
const int WINDOW_WID = 800; // ウィンドウの横サイズ
const int WINDOW_HIG = 600; // ウィンドウの縦サイズ
// プレイヤー
const int PLAYER_WID = 32; // プレイヤー画像の横サイズ
const int PLAYER_HIG = 19; // プレイヤー画像の縦サイズ
const int PLAYER_MOVE_SPEED = 6; // プレイヤーの左右の移動速度
// プレイヤー弾
const int PLAYER_BULLET_WID = 6; // プレイヤー弾の画像の横サイズ
const int PLAYER_BULLET_HIG = 30; // プレイヤー弾の画像の縦サイズ
const int PLAYER_BULLET_MOVE = 12; // プレイヤー弾の移動速度
// 敵キャラ
const int ENEMY_WID = 32; // 敵の画像の横サイズ
const int ENEMY_HIG = 23; // 敵の画像の縦サイズ
// 敵キャラの画面表示関連
const int ENEMY_DISP_XNUM = 8; // 敵キャラの横の表示個数
const int ENEMY_DISP_YNUM = 7; // 敵キャラの縦の表示個数
const int ENEMY_DISP_ALL_NUM = ENEMY_DISP_XNUM * ENEMY_DISP_YNUM;
//-----------------
// 変数の宣言
//-----------------
// プレイヤー
int playerImage; // 自機の画像ハンドル番号
int playerPosX; // 自機のX座標
int playerPosY; // 自機のY座標
bool playerFlg; // 自機の表示状態管理用フラグ(true:表示、false:非表示)
// プレイヤー弾
int playerBulletImage; // プレイヤー弾の画像ハンドル番号
int playerBulletPosX; // プレイヤー弾のX座標
int playerBulletPosY; // プレイヤー弾のY座標
bool playerBulletFlg; // プレイヤー弾の発射状態管理用フラグ(true:表示、false:非表示)
// 敵キャラ
int enemyImage; // 敵の画像ハンドル番号
int enemyPosxArray[ENEMY_DISP_YNUM][ENEMY_DISP_XNUM]; // 敵のX座標表示位置テーブル
int enemyPosyArray[ENEMY_DISP_YNUM][ENEMY_DISP_XNUM]; // 敵のY座標表示位置テーブル
bool enemyFlgArray[ENEMY_DISP_YNUM][ENEMY_DISP_XNUM]; // 敵の存在フラグテーブル
//-----------------
// WinMain関数
//-----------------
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow)
{
//-----------------
// システム設定
//-----------------
SetWindowText("000000 苗字 名前"); // ウィンドウのタイトルの設定
SetGraphMode(WINDOW_WID, WINDOW_HIG, 32); // ウィンドウのサイズと色モードの設定
ChangeWindowMode(true); // ウィンドウの表示モード(true=ウィンドウモード/false=フルスクリーン)
int err = DxLib_Init(); // DXライブラリの初期化
if (err == -1)return -1;
SRand((unsigned int)time(NULL));
//---------------------------
// グラフィックの登録
//---------------------------
// プレイヤー画像の読込み
playerImage = LoadGraph("image/player.png");
if (playerImage == -1)return -1;
// プレイヤー弾の画像の読込み
playerBulletImage = LoadGraph("image/playerBullet.png");
if (playerBulletImage == -1)return -1;
// 敵の画像の読込み
enemyImage = LoadGraph("image/enemy_1.png");
if (enemyImage == -1)return -1;
//-----------------
// 変数の初期化
//-----------------
// プレイヤー
playerPosX = WINDOW_WID / 2 - PLAYER_WID / 2; // ウィンドウの中央X座標
playerPosY = WINDOW_HIG - PLAYER_HIG;
playerFlg = true;
// プレイヤー弾
playerBulletPosX = playerBulletPosY = 0;
playerBulletFlg = false;
// 敵キャラ
for (int yy = 0; yy < ENEMY_DISP_YNUM; yy++) {
for (int xx = 0; xx < ENEMY_DISP_XNUM; xx++) {
enemyPosxArray[yy][xx] = ENEMY_WID * xx;
enemyPosyArray[yy][xx] = ENEMY_HIG * yy;
enemyFlgArray[yy][xx] = true;
}
}
//-----------------
// ゲームループ
//-----------------
while (ProcessMessage() == 0 && CheckHitKey(KEY_INPUT_ESCAPE) == 0) {
//---------------------------
// 更新処理
//---------------------------
// プレイヤーの移動
// プレイヤー弾の処理
// 当たり判定
//---------------------------
// 描画処理
//---------------------------
SetDrawScreen(DX_SCREEN_BACK); // 描画する画面を裏の画面に設定する
ClearDrawScreen(); // 描画する画面の内容を消去する
// プレイヤーの描画
// プレイヤー弾の描画
// 敵の描画
ScreenFlip(); // 裏の画面を表の画面に瞬間コピー
}
//----------------------
// システムの終了処理
//----------------------
DxLib_End(); // DXライブラリの終了処理
return 0; // ゲームの正常終了
}
※二次元配列
int Array[x][y];
もちろんです、佳之さん!
int Array[x][y]; は、2次元配列(二重配列)を宣言する構文です。これで、整数型(int)のデータを格納する表のようなデータ構造を作れます。
📦 構文の意味
int Array[x][y];
- int:この配列が整数 (int) を格納することを示します。
- Array:配列の名前です。自由に名前をつけられます。
- [x][y]:それぞれのインデックス(添え字)
- x は行数(縦方向)
- y は列数(横方向)
たとえば int Array[3][4]; なら、3行4列の配列 → 合計 12個の int 値が格納できます。
🎯 使い方例
#include <stdio.h>
int main() {
int Array[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
printf("%d\n", Array[1][2]); // 結果: 6(2行目3列目の値)
return 0;
}
💡補足ポイント
- Cでは配列のインデックスは0から始まるため、Array[0][0] が最初の要素になります。
- Array[x][y] という宣言は、x 行、y 列の“固定サイズの表”を作る感覚です。
- 動的にサイズを変えたい場合は、ポインタと malloc() を使った動的2次元配列にする必要があります(少し高度ですが、興味があれば解説します!)
📊 例:int Array[3][4];
のイメージ
行\列 | 0列 | 1列 | 2列 | 3列 |
---|---|---|---|---|
0行 | Array[0][0] | Array[0][1] | Array[0][2] | Array[0][3] |
1行 | Array[1][0] | Array[1][1] | Array[1][2] | Array[1][3] |
2行 | Array[2][0] | Array[2][1] | Array[2][2] | Array[2][3] |
この配列は「3行4列」の構造を持ち、全部で 3×4=12個
の int
型のデータを格納できます。
🔍 ポイントまとめ
- 行が先、列が後 →
Array[行][列]
- 配列のインデックスは 0から始まる
- 表形式で管理できるので、ゲームの盤面やデータテーブルなどに便利
(by Copilot)
※代入について
a = b = 0 ;
というような記法も可能。
順序でいえば、b=0⇒a=bの順で処理される。
プレイヤーの描画処理を追加。
// プレイヤーの描画
if (playerFlg) {
DrawGraph(playerPosX, playerPosY, playerImage, true);
}
プレイヤーの移動処理を追加。(移動制限付き)
// プレイヤーの移動
if (playerFlg) {
//右キーが押されたか
if (CheckHitKey(KEY_INPUT_RIGHT) == 1) {
playerPosX += PLAYER_MOVE_SPEED; //右に移動
}
//左キーが押されたか
if (CheckHitKey(KEY_INPUT_LEFT) == 1) {
playerPosX -= PLAYER_MOVE_SPEED; //左に移動
}
//左端のチェック
if (playerPosX < 0)playerPosX = 0;
//右端のチェック
if (playerPosX > (WINDOW_WID - PLAYER_WID - 1))playerPosX = (WINDOW_WID - PLAYER_WID - 1);
}
プレイヤー弾の描画処理を追加
// プレイヤー弾の描画
if (playerBulletFlg) {
DrawGraph(playerBulletPosX, playerBulletPosY, playerBulletImage, true);
}
プレイヤー弾の発射処理を追加
// プレイヤー弾の処理
if (playerBulletFlg == false) {
//弾が未発射状態なので射撃可能
if (CheckHitKey(KEY_INPUT_SPACE) == 1) {
playerBulletPosX = (playerPosX + PLAYER_WID / 2) - (PLAYER_BULLET_WID / 2);
playerBulletPosY = playerPosY - PLAYER_BULLET_HIG;
playerBulletFlg = true;
}
}
弾の移動処理を追加
//弾の移動処理
playerBulletPosY -= PLAYER_BULLET_MOVE;
if (playerBulletPosY < 0)playerBulletFlg = false;
}
敵の描画処理を追加
// 敵の描画
for (int yy = 0; yy < ENEMY_DISP_YNUM; yy++) {
for (int xx = 0; xx < ENEMY_DISP_XNUM;xx++) {
if (enemyFlgArray[yy][xx]) {
DrawGraphScreen(enemyPosxArray[yy][xx], enemyPosyArray[yy][xx], enemyImage, true);
}
}
}
関数分離
Init(初期化)、Update(更新)、Draw(描画)に分けて記述する。
プロトタイプ宣言はWinMainの直前、関数本体の記述はWinMainの下に配置。
//プロトタイプ宣言
bool Init(void);
void Update(void);
void Draw(void);
//---------------------------
//初期化処理
//---------------------------
bool Init(void)
{
return 0;
}
//---------------------------
//更新処理
//---------------------------
void Update(void)
{
}
//---------------------------
//描画処理
//---------------------------
void Draw(void)
{
}
関数分割後に、処理を関数に移した結果。
【今日のコード①】
//-----------------------------------------------------------------------------
// インベーダーゲーム
// 氏 名:
//-----------------------------------------------------------------------------
#include <DxLib.h>
#include <time.h>
//-----------------
// 定数定義
//-----------------
const int WINDOW_WID = 800; // ウィンドウの横サイズ
const int WINDOW_HIG = 600; // ウィンドウの縦サイズ
// プレイヤー
const int PLAYER_WID = 32; // プレイヤー画像の横サイズ
const int PLAYER_HIG = 19; // プレイヤー画像の縦サイズ
const int PLAYER_MOVE_SPEED = 6; // プレイヤーの左右の移動速度
// プレイヤー弾
const int PLAYER_BULLET_WID = 6; // プレイヤー弾の画像の横サイズ
const int PLAYER_BULLET_HIG = 30; // プレイヤー弾の画像の縦サイズ
const int PLAYER_BULLET_MOVE = 12; // プレイヤー弾の移動速度
// 敵キャラ
const int ENEMY_WID = 32; // 敵の画像の横サイズ
const int ENEMY_HIG = 23; // 敵の画像の縦サイズ
// 敵キャラの画面表示関連
const int ENEMY_DISP_XNUM = 8; // 敵キャラの横の表示個数
const int ENEMY_DISP_YNUM = 7; // 敵キャラの縦の表示個数
const int ENEMY_DISP_ALL_NUM = ENEMY_DISP_XNUM * ENEMY_DISP_YNUM;
//-----------------
// 変数の宣言
//-----------------
// プレイヤー
int playerImage; // 自機の画像ハンドル番号
int playerPosX; // 自機のX座標
int playerPosY; // 自機のY座標
bool playerFlg; // 自機の表示状態管理用フラグ(true:表示、false:非表示)
// プレイヤー弾
int playerBulletImage; // プレイヤー弾の画像ハンドル番号
int playerBulletPosX; // プレイヤー弾のX座標
int playerBulletPosY; // プレイヤー弾のY座標
bool playerBulletFlg; // プレイヤー弾の発射状態管理用フラグ(true:表示、false:非表示)
// 敵キャラ
int enemyImage; // 敵の画像ハンドル番号
int enemyPosxArray[ENEMY_DISP_YNUM][ENEMY_DISP_XNUM]; // 敵のX座標表示位置テーブル
int enemyPosyArray[ENEMY_DISP_YNUM][ENEMY_DISP_XNUM]; // 敵のY座標表示位置テーブル
bool enemyFlgArray[ENEMY_DISP_YNUM][ENEMY_DISP_XNUM]; // 敵の存在フラグテーブル
//プロトタイプ宣言
bool Init(void);
void Update(void);
void Draw(void);
//-----------------
// WinMain関数
//-----------------
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow)
{
if (Init() == false)return -1;
//-----------------
// ゲームループ
//-----------------
while (ProcessMessage() == 0 && CheckHitKey(KEY_INPUT_ESCAPE) == 0) {
//---------------------------
// 更新処理
//---------------------------
Update();
//---------------------------
// 描画処理
//---------------------------
Draw();
}
//----------------------
// システムの終了処理
//----------------------
DxLib_End(); // DXライブラリの終了処理
return 0; // ゲームの正常終了
}
//---------------------------
//初期化処理
//---------------------------
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));
//---------------------------
// グラフィックの登録
//---------------------------
// プレイヤー画像の読込み
playerImage = LoadGraph("image/player.png");
if (playerImage == -1)return false;
// プレイヤー弾の画像の読込み
playerBulletImage = LoadGraph("image/playerBullet.png");
if (playerBulletImage == -1)return false;
// 敵の画像の読込み
enemyImage = LoadGraph("image/enemy_1.png");
if (enemyImage == -1)return false;
//-----------------
// 変数の初期化
//-----------------
// プレイヤー
playerPosX = WINDOW_WID / 2 - PLAYER_WID / 2; // ウィンドウの中央X座標
playerPosY = WINDOW_HIG - PLAYER_HIG;
playerFlg = true;
// プレイヤー弾
playerBulletPosX = playerBulletPosY = 0;
playerBulletFlg = false;
// 敵キャラ
for (int yy = 0; yy < ENEMY_DISP_YNUM; yy++) {
for (int xx = 0; xx < ENEMY_DISP_XNUM; xx++) {
enemyPosxArray[yy][xx] = ENEMY_WID * xx;
enemyPosyArray[yy][xx] = ENEMY_HIG * yy;
enemyFlgArray[yy][xx] = true;
}
}
return true;
}
//---------------------------
//更新処理
//---------------------------
void Update(void)
{
// プレイヤーの移動
if (playerFlg) {
//右キーが押されたか
if (CheckHitKey(KEY_INPUT_RIGHT) == 1) {
playerPosX += PLAYER_MOVE_SPEED; //右に移動
}
//左キーが押されたか
if (CheckHitKey(KEY_INPUT_LEFT) == 1) {
playerPosX -= PLAYER_MOVE_SPEED; //左に移動
}
//左端のチェック
if (playerPosX < 0)playerPosX = 0;
//右端のチェック
if (playerPosX > (WINDOW_WID - PLAYER_WID - 1))playerPosX = (WINDOW_WID - PLAYER_WID - 1);
}
// プレイヤー弾の処理
if (playerBulletFlg == false) {
//弾が未発射状態なので射撃可能
if (CheckHitKey(KEY_INPUT_SPACE) == 1) {
playerBulletPosX = (playerPosX + PLAYER_WID / 2) - (PLAYER_BULLET_WID / 2);
playerBulletPosY = playerPosY - PLAYER_BULLET_HIG;
playerBulletFlg = true;
}
}
else {
//弾の移動処理
playerBulletPosY -= PLAYER_BULLET_MOVE;
if (playerBulletPosY < 0)playerBulletFlg = false;
}
// 当たり判定
}
//---------------------------
//描画処理
//---------------------------
void Draw(void)
{
SetDrawScreen(DX_SCREEN_BACK); // 描画する画面を裏の画面に設定する
ClearDrawScreen(); // 描画する画面の内容を消去する
// プレイヤーの描画
if (playerFlg) {
DrawGraph(playerPosX, playerPosY, playerImage, true);
}
// プレイヤー弾の描画
if (playerBulletFlg) {
DrawGraph(playerBulletPosX, playerBulletPosY, playerBulletImage, true);
}
// 敵の描画
for (int yy = 0; yy < ENEMY_DISP_YNUM; yy++) {
for (int xx = 0; xx < ENEMY_DISP_XNUM;xx++) {
if (enemyFlgArray[yy][xx]) {
DrawGraph(enemyPosxArray[yy][xx], enemyPosyArray[yy][xx], enemyImage, true);
}
}
}
ScreenFlip(); // 裏の画面を表の画面に瞬間コピー
}
コメント