2Dゲーム制作Ⅰ⑤(2025/05/13)

※絶対パス・相対パス
Windows PC における 絶対パス相対パス について、具体的な例を挙げながら説明しますね。

絶対パス

絶対パスは、ファイルやフォルダの場所を ドライブのルートから完全に指定 する方法です。
どこから参照しても同じ場所を指すため、非常に明確です。

C:\Users\Yoshiyuki\Documents\report.docx

このパスは、Windows の C: ドライブ内の Users フォルダの Yoshiyuki ユーザーの Documents フォルダにある report.docx というファイルを指します。
このパスは 絶対的な位置 を表すため、どのフォルダにいても report.docx にアクセスできます。

相対パス

相対パスは 現在のフォルダを基準 にしてファイルやフォルダの場所を指定する方法です。
プログラムやスクリプトを書くときによく使われ、移動したときにパスを更新せずに済むメリットがあります。

現在のフォルダが C:\Users\Yoshiyuki\Documents である場合、
report.docx にアクセスする 相対パス は単に report.docx になります。

また、1つ上のフォルダ(親フォルダ)へ移動する場合は、
..\Desktop\memo.txt のように記述すると、Documentsひとつ上のフォルダ である Desktop にある memo.txt にアクセスできます。

まとめ

種類特徴
絶対パスどこからでも同じ場所を指すC:\Users\Yoshiyuki\Documents\report.docx
相対パス現在のフォルダを基準にするreport.docx (現在のフォルダが Documents の場合)

ファイル管理やプログラミングでは、状況に応じて 絶対パス相対パス を使い分けることが重要です。
適切に活用できると、作業がもっとスムーズになりますよ!(by Copilot)

ちなみに、プログラム中では、フォルダの区切りは「/(スラッシュ)」で、Windowsシステム上での区切りは「\(円マーク)」です。なぜ\なのかというと、文字コード的には\に当たる文字は海外では「バックスラッシュ(/とは反対に傾いているスラッシュ)」で、パソコンを日本語対応させるときに「¥」を割り当てた、という経緯があります。日本語非対応のWebシステムなんかで¥を入力すると「バックスラッシュ」になるのはこういった理由からです。
なお、最近のWindowsシステムでは大体¥も/も同じように使えるので、この区切りはあまり重要ではありません。ただ、基本は¥なので、「/」が使えないときは「\」と覚えておいてください。

【自機と敵機の当たり判定】
以前に実装していたものをマクロ定数化。

~
#define DISTANCE_R (250*250) //距離の二乗
~
		//プレイヤーの弾の処理
		if (shotTimer > 0)
		{
			shotTimer--;
		}
		int dx = 0;
		if (shotFlag == 1 && enemyFlag == 1)
		{
			dx = (shotX - enemyX) * (shotX - enemyX) + (shotY - enemyY) * (shotY - enemyY);
			if (dx < DISTANCE_R)
			{
				
				enemyFlag = 0;
				shotFlag = 0;
				enemyTimer = (rand() % 100);
				enemyX = (rand() % 360);
			}
		}
		if (shotFlag2 == 1 && enemyFlag == 1)
		{
			dx = (shotX2 - enemyX) * (shotX2 - enemyX) + (shotY2 - enemyY) * (shotY2 - enemyY);
			if (dx < DISTANCE_R)
			{
				enemyFlag = 0;
				shotFlag2 = 0;
				enemyTimer = (rand() % 100);
				enemyX = (rand() % 360);
			}
		}
		if (shotFlag3 == 1 && enemyFlag == 1)
		{
			dx = (shotX3 - enemyX) * (shotX3 - enemyX) + (shotY3 - enemyY) * (shotY3 - enemyY);
			if (dx < DISTANCE_R)
			{
				enemyFlag = 0;
				shotFlag3 = 0;
				enemyTimer = (rand() % 100);
				enemyX = (rand() % 360);
			}
		}

※コメントアウト
/* この中がコメント */
プログラムのなかでは読まれない。
// この部分がコメント
がその行だけが対象なのに対して、/* */は行を跨いでコメントアウトすることができる。
一部処理を一時的に無効にしたいときに使う。

※二乗の記述方法について
数学の乗数記号”^”はC言語では論理演算の”XOR”を指すため意味が変わる。
たとえば10の二乗なら、10*10 としか表記できない。

上記を踏まえて

【今日のコード①】

#include<stdlib.h>
#include<time.h>
#include<DxLib.h>

#define SCREEN_W 480
#define SCREEN_H 880

#define CENTER_X (SCREEN_W / 2)
#define CENTER_Y (SCREEN_H / 2)

#define DISTANCE_R (250*250) //距離の二乗

#define PLAYER_W 128 //プレイヤーの横幅
#define PLAYER_H 141 //プレイヤーの高さ

bool playerFlag = true; //自機の表示フラグ
int playerX = CENTER_X;
int playerY = CENTER_Y;
int scrollY = 0;
int shotX = 0, shotY = 0, shotFlag = 0;
int shotX2 = 0, shotY2 = 0, shotFlag2 = 0;
int shotX3 = 0, shotY3 = 0, shotFlag3 = 0;// shotFlag…0:表示しない 1:表示
int shotTimer = 0;
int enemyFlag = 1, enemyX = 0, enemyY = 100, enemyTimer = 0, enemyVX = 0, enemyVY = 1; //enemy画像の幅の半分をずらして、真ん中付近に表示


void DrawGraphCenter(int x, int y, int handle, bool flag);

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
	ChangeWindowMode(TRUE); // ウィンドウモード
	SetGraphMode(SCREEN_W, SCREEN_H, 32);

	DxLib_Init(); // DXライブラリ初期化
	SetMainWindowText("BlackPiyo VS Mike");
	int handleShot = LoadGraph("chara/egg.png");
	int handleEnemy = LoadGraph("chara/mike.png");
	int handleBg = LoadGraph("bg.png");
	int handle = LoadGraph("chara/blackpiyo-s.png");
	srand((unsigned)time(NULL) * 123);

	while (ProcessMessage() == 0) {
		SetDrawScreen(DX_SCREEN_BACK); // 裏画面に描画
		ClearDrawScreen();

		//playerを動かす処理
		if (CheckHitKey(KEY_INPUT_RIGHT))
		{
			playerX += 2;
		}
		if (CheckHitKey(KEY_INPUT_LEFT))
		{
			playerX -= 2;
		}
		if (CheckHitKey(KEY_INPUT_DOWN))
		{
			playerY += 2;
		}
		if (CheckHitKey(KEY_INPUT_UP))
		{
			playerY -= 2;
		}
		if (CheckHitKey(KEY_INPUT_SPACE) && (shotTimer == 0))
		{
			if (shotFlag == 0)
			{
				shotX = playerX;
				shotY = playerY;
				shotFlag = 1;
				shotTimer = 20;
			}
			else if (shotFlag2 == 0)
			{
				shotX2 = playerX;
				shotY2 = playerY;
				shotFlag2 = 1;
				shotTimer = 20;
			}
			else if (shotFlag3 == 0)
			{
				shotX3 = playerX;
				shotY3 = playerY;
				shotFlag3 = 1;
				shotTimer = 20;
			}
		}

		//プレーヤーの移動制限
		if (playerX > SCREEN_W - PLAYER_W / 2)playerX = SCREEN_W - PLAYER_W / 2;
		if (playerX < PLAYER_W / 2)playerX = PLAYER_W/ 2;
		if (playerY > SCREEN_H - PLAYER_H / 2)playerY = SCREEN_H - PLAYER_H / 2;
		if (playerY < PLAYER_H / 2)playerY = PLAYER_H / 2;

		//プレイヤーの弾の処理
		if (shotTimer > 0)
		{
			shotTimer--;
		}
		int dx = 0;
		if (shotFlag == 1 && enemyFlag == 1)
		{
			dx = (shotX - enemyX) * (shotX - enemyX) + (shotY - enemyY) * (shotY - enemyY);
			if (dx < DISTANCE_R)
			{
				
				enemyFlag = 0;
				shotFlag = 0;
				enemyTimer = (rand() % 100);
				enemyX = (rand() % 360);
			}
		}
		if (shotFlag2 == 1 && enemyFlag == 1)
		{
			dx = (shotX2 - enemyX) * (shotX2 - enemyX) + (shotY2 - enemyY) * (shotY2 - enemyY);
			if (dx < DISTANCE_R)
			{
				enemyFlag = 0;
				shotFlag2 = 0;
				enemyTimer = (rand() % 100);
				enemyX = (rand() % 360);
			}
		}
		if (shotFlag3 == 1 && enemyFlag == 1)
		{
			dx = (shotX3 - enemyX) * (shotX3 - enemyX) + (shotY3 - enemyY) * (shotY3 - enemyY);
			if (dx < DISTANCE_R)
			{
				enemyFlag = 0;
				shotFlag3 = 0;
				enemyTimer = (rand() % 100);
				enemyX = (rand() % 360);
			}
		}
		scrollY += 8;
		if (scrollY > SCREEN_H)
		{
			scrollY = 0;
		}

		if (shotFlag == 1)
		{
			shotY -= 4;
			if (shotY < 0)
			{
				shotFlag = 0;
			}
		}
		if (shotFlag2 == 1)
		{
			shotY2 -= 4;
			if (shotY2 < 0)
			{
				shotFlag2 = 0;
			}
		}

		if (shotFlag3 == 1)
		{
			shotY3 -= 4;
			if (shotY3 < 0)
			{
				shotFlag3 = 0;
			}
		}
		//敵の移動処理
		if (enemyFlag == 1) {
			enemyX += enemyVX; // X方向の移動
			enemyY += enemyVY; // Y方向の移動

				dx = (playerX - enemyX) * (playerX - enemyX) + (playerY - enemyY) * (playerY - enemyY);

				if (dx < DISTANCE_R)
				{
					playerFlag = FALSE; //プレイヤー消滅
					enemyFlag = FALSE; //敵消滅
				}

			// 範囲外判定:敵が画面外に出たらフラグをオフにし再出現を準備
			if (enemyX < -400 || enemyX -400 > SCREEN_W) {
				enemyFlag = 0; // X方向の反転
			}
			if (enemyY  < -400 || enemyY -400 > SCREEN_H) {
				enemyFlag = 0; // Y方向の反転
			}
		}
		else {
			// 敵の再出現処理
			if (enemyTimer-- < 0) {
				enemyFlag = 1;
				enemyX = rand() % SCREEN_W;
				enemyY = -300;
				enemyVX = rand() % 5 - 2; // -2~2のランダム速度
				enemyVY = rand() % 5 - 2; // -2~2のランダム速度
				enemyTimer = 50;
			}
		}
		DrawGraph(0, scrollY, handleBg, TRUE);
		DrawGraph(0, scrollY - SCREEN_H, handleBg, TRUE);
		DrawGraphCenter(playerX, playerY, handle, playerFlag);
		DrawGraphCenter(shotX, shotY, handleShot, shotFlag == 1);
		DrawGraphCenter(shotX2, shotY2, handleShot, shotFlag2 == 1);
		DrawGraphCenter(shotX3, shotY3, handleShot, shotFlag3 == 1);
		DrawGraphCenter(enemyX, enemyY, handleEnemy, TRUE);

		ScreenFlip();
	}
	DxLib_End();
}
//画像の中心位置基準で表示
//input x, y 表示位置座標
//handle 画像ID
//flag 表示/非表示
void DrawGraphCenter(int playerX, int playerY, int handle, bool flag)
{
	int width, height;

	if (flag)
	{
		GetGraphSize(handle, &width, &height);
		DrawGraph(playerX - (width / 2), playerY -( height / 2), handle, TRUE);
	}
}


プレイヤーに残機を追加するために下記コードを追加。

~
int playerTimer = 0;
int playerLife;
~
//プレイヤーが一定時間消えた後、復活する
if ((playerFlag == false) && (playerLife >= 0) )
{
	if (playerTimer++ > PLAYER_DEL_PERIOD) {
		playerFlag = TRUE;
		playerTimer = 0;
		playerLife--;
		}
}

上記を踏まえて
【今日のコード②】

#include<stdlib.h>
#include<time.h>
#include<DxLib.h>

#define SCREEN_W 480
#define SCREEN_H 880

#define CENTER_X (SCREEN_W / 2)
#define CENTER_Y (SCREEN_H / 2)

#define DISTANCE_R (250*250) //距離の二乗
#define PLAYER_DEL_PERIOD (30)
#define PLAYER_MAX (1)
#define GAME_START_PERIOD (60)//GAME START表示時間

#define PLAYER_W 128 //プレイヤーの横幅
#define PLAYER_H 141 //プレイヤーの高さ

bool playerFlag = true; //自機の表示フラグ
int playerX = CENTER_X;
int playerY = CENTER_Y;
int scrollY = 0;
int shotX = 0, shotY = 0, shotFlag = 0;
int shotX2 = 0, shotY2 = 0, shotFlag2 = 0;
int shotX3 = 0, shotY3 = 0, shotFlag3 = 0;// shotFlag…0:表示しない 1:表示
int shotTimer = 0;
int enemyFlag = 1, enemyX = 0, enemyY = 100, enemyTimer = 0, enemyVX = 0, enemyVY = 1; //enemy画像の幅の半分をずらして、真ん中付近に表示
int playerTimer = 0;
int playerLife;
int gameTimer = 0;
int n = 0;

void DrawGraphCenter(int x, int y, int handle, bool flag);

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
	ChangeWindowMode(TRUE); // ウィンドウモード
	SetGraphMode(SCREEN_W, SCREEN_H, 32);

	DxLib_Init(); // DXライブラリ初期化
	SetMainWindowText("BlackPiyo VS Mike");
	int handleShot = LoadGraph("chara/egg.png");
	int handleEnemy = LoadGraph("chara/mike.png");
	int handleBg = LoadGraph("bg.png");
	int handle = LoadGraph("chara/blackpiyo-s.png");
	srand((unsigned)time(NULL) * 123);

	playerLife = PLAYER_MAX
		;

	while (ProcessMessage() == 0) {
		SetDrawScreen(DX_SCREEN_BACK); // 裏画面に描画
		ClearDrawScreen();

		//playerを動かす処理
		if (CheckHitKey(KEY_INPUT_RIGHT))
		{
			playerX += 2;
		}
		if (CheckHitKey(KEY_INPUT_LEFT))
		{
			playerX -= 2;
		}
		if (CheckHitKey(KEY_INPUT_DOWN))
		{
			playerY += 2;
		}
		if (CheckHitKey(KEY_INPUT_UP))
		{
			playerY -= 2;
		}
		if (CheckHitKey(KEY_INPUT_SPACE) && (shotTimer == 0))
		{
			if (shotFlag == 0)
			{
				shotX = playerX;
				shotY = playerY;
				shotFlag = 1;
				shotTimer = 20;
			}
			else if (shotFlag2 == 0)
			{
				shotX2 = playerX;
				shotY2 = playerY;
				shotFlag2 = 1;
				shotTimer = 20;
			}
			else if (shotFlag3 == 0)
			{
				shotX3 = playerX;
				shotY3 = playerY;
				shotFlag3 = 1;
				shotTimer = 20;
			}
		}

		//プレーヤーの移動制限
		if (playerX > SCREEN_W - PLAYER_W / 2)playerX = SCREEN_W - PLAYER_W / 2;
		if (playerX < PLAYER_W / 2)playerX = PLAYER_W/ 2;
		if (playerY > SCREEN_H - PLAYER_H / 2)playerY = SCREEN_H - PLAYER_H / 2;
		if (playerY < PLAYER_H / 2)playerY = PLAYER_H / 2;

		//プレイヤーの弾の処理
		if (shotTimer > 0)
		{
			shotTimer--;
		}
		int dx = 0;
		if (shotFlag == 1 && enemyFlag == 1)
		{
			dx = (shotX - enemyX) * (shotX - enemyX) + (shotY - enemyY) * (shotY - enemyY);
			if (dx < DISTANCE_R)
			{
				
				enemyFlag = 0;
				shotFlag = 0;
				enemyTimer = (rand() % 100);
				enemyX = (rand() % 360);
			}
		}
		if (shotFlag2 == 1 && enemyFlag == 1)
		{
			dx = (shotX2 - enemyX) * (shotX2 - enemyX) + (shotY2 - enemyY) * (shotY2 - enemyY);
			if (dx < DISTANCE_R)
			{
				enemyFlag = 0;
				shotFlag2 = 0;
				enemyTimer = (rand() % 100);
				enemyX = (rand() % 360);
			}
		}
		if (shotFlag3 == 1 && enemyFlag == 1)
		{
			dx = (shotX3 - enemyX) * (shotX3 - enemyX) + (shotY3 - enemyY) * (shotY3 - enemyY);
			if (dx < DISTANCE_R)
			{
				enemyFlag = 0;
				shotFlag3 = 0;
				enemyTimer = (rand() % 100);
				enemyX = (rand() % 360);
			}
		}
		scrollY += 8;
		if (scrollY > SCREEN_H)
		{
			scrollY = 0;
		}

		if (shotFlag == 1)
		{
			shotY -= 4;
			if (shotY < 0)
			{
				shotFlag = 0;
			}
		}
		if (shotFlag2 == 1)
		{
			shotY2 -= 4;
			if (shotY2 < 0)
			{
				shotFlag2 = 0;
			}
		}

		if (shotFlag3 == 1)
		{
			shotY3 -= 4;
			if (shotY3 < 0)
			{
				shotFlag3 = 0;
			}
		}
		//敵の移動処理
		if (enemyFlag == 1) {
			enemyX += enemyVX; // X方向の移動
			enemyY += enemyVY; // Y方向の移動

				dx = (playerX - enemyX) * (playerX - enemyX) + (playerY - enemyY) * (playerY - enemyY);

				if (dx < DISTANCE_R)
				{
					playerFlag = FALSE; //プレイヤー消滅
					enemyFlag = FALSE; //敵消滅
				}
				//プレイヤーが一定時間消えた後、復活する
				if ((playerFlag == false) && (playerLife >= 0) )
				{
					if (playerTimer++ > PLAYER_DEL_PERIOD) {
						playerFlag = TRUE;
						playerTimer = 0;
						playerLife--;
					}
				}
			// 範囲外判定:敵が画面外に出たらフラグをオフにし再出現を準備
			if (enemyX < -400 || enemyX -400 > SCREEN_W) {
				enemyFlag = 0;
				enemyTimer = 40;
			}
			if (enemyY  < -400 || enemyY -400 > SCREEN_H) {
				enemyFlag = 0;
				enemyTimer = 40;
			}
		}
		else {
			// 敵の再出現処理
			if (enemyTimer-- < 0) {
				enemyFlag = 1;
				enemyX = rand() % SCREEN_W;
				enemyY = -300;
				enemyVX = rand() % 5 - 1; // -1~1のランダム速度
				enemyVY = rand() % 5 - 2; // -2~2のランダム速度
				enemyTimer = 50;
			}
		}
		DrawGraph(0, scrollY, handleBg, TRUE);
		DrawGraph(0, scrollY - SCREEN_H, handleBg, TRUE);
		DrawGraphCenter(playerX, playerY, handle, playerFlag);
		DrawGraphCenter(shotX, shotY, handleShot, shotFlag == 1);
		DrawGraphCenter(shotX2, shotY2, handleShot, shotFlag2 == 1);
		DrawGraphCenter(shotX3, shotY3, handleShot, shotFlag3 == 1);
		DrawGraphCenter(enemyX, enemyY, handleEnemy, TRUE);

		//残機表示
		DrawFormatString(0, 0, GetColor(255, 255, 255), "残機:%d", playerLife);
		
		//ゲームオーバー表示
		if (playerLife == -1)
		{
			DrawString(70, 240, "GAME OVER", GetColor(255, 255, 255));
		}

		//ゲームスタート表示
		if (gameTimer++ < GAME_START_PERIOD)
		{
			DrawString(120, CENTER_Y, "GAME START", GetColor(255, 255, 255));
		}

		ScreenFlip();
	}
	DxLib_End();
}
//画像の中心位置基準で表示
//input x, y 表示位置座標
//handle 画像ID
//flag 表示/非表示
void DrawGraphCenter(int playerX, int playerY, int handle, bool flag)
{
	int width, height;

	if (flag)
	{
		GetGraphSize(handle, &width, &height);
		DrawGraph(playerX - (width / 2), playerY -( height / 2), handle, TRUE);
	}
}

・・・機数がー1表示になってしまう問題があるけど、とりあえずタイムアップなので今日はここまで。

コメント

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