弾当たり判定について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
vjhsvb
記事: 6
登録日時: 8年前

弾当たり判定について

#1

投稿記事 by vjhsvb » 8年前

また質問させて頂きます。

質問は3つあります。

<マップについて>
キャラに当たるのが1(白)、当たらないのが0(黒)なのですが実行してみると1と0がマップデータと少しずれてしまいます。

00000001111100000

実行するとキャラが下線部分に当たるみたいな感じです。
(説明が下手ですみません)
どこがおかしくこのようなことが起きるのか教えて欲しいです。

<スクロールについて>
キャラを右に動かすとマップもスクロールされるのですが、マップのスクロールがキャラの移動に間に合わずキャラがマップ右端まで行って見えなくなってしまいます。
キャラ移動とマップスクロールを同じ速度にするにはどうすれば良いですか?
どこが間違えているのか教えてくれませんか?

<弾当たり判定について>
マップに表示したコインをキャラの弾をあてることによってスコアを取得し、弾が当たったコインは消えるというプログラムを作りたいのですが、うまい方法が見つかりません。
このプログラムに合ったサンプルを作ってくれるととてもありがたいです。

大変ご迷惑をおかけします。
よろしくお願いします。

コード:

#define _USE_MATH_DEFINES
#define _CRT_SECURE_NO_WARNINGS

#include "DxLib.h"
#include <math.h>
#include <string.h>


#define SCREEN_WIDTH     (640)                         // 画面の横幅
#define SCREEN_HEIGHT    (480)                          // 画面の縦幅
#define MAP_PIXEL_WIDTH (1280)
#define MAP_PIXEL_HEIGHT (480)
#define CHIP_SIZE        (32)                           // 一つのチップのサイズ
#define MAP_WIDTH        ( MAP_PIXEL_WIDTH / CHIP_SIZE)     // マップの横幅
#define MAP_HEIGHT       (MAP_PIXEL_HEIGHT / CHIP_SIZE)    // マップの縦幅 

#define G                (0.3F)                         // キャラに掛かる重力加速度
#define JUMP_POWER       (9.0F)                         // キャラのジャンプ力
#define SPEED            (3.0F)                         // キャラの移動スピード
#define CHAR_SIZE        (30)                           // プレイヤーのサイズ

// ショットの最大数
#define MAX_SHOT 10000

// マップデータ
char MapData[MAP_HEIGHT][MAP_WIDTH] =
{
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,

	1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
	1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
	1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,

	1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
	0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
};


int Player;
float PlX, PlY;            // プレイヤーの座標(中心座標)
float PlDownSp;            // プレイヤーの落下速度
char PlJumpFlag;           // プレイヤーがジャンプ中か、のフラグ

int Shot;//弾
int ShotValid[MAX_SHOT];	// ショットが存在するか、フラグ
int ShotX[MAX_SHOT], ShotY[MAX_SHOT];	// ショットの位置	

int coin;

int Input, EdgeInput;      // 入力状態
int FrameStartTime;        // 60FPS固定用、時間保存用変数	

int camerax = 0;//カメラの左上座標
int charx;//自キャラ位置
int view_charx;//自キャラの表示位置。この座標にdrawする。



			   ////プロトタイプ宣言////////////////////////////////////////////////////////////////////////////

			   // マップチップの値を取得する関数
int GetChipParam(float X, float Y);

// キャラクタをマップとの当たり判定を考慮しながら移動する関数
int CharMove(float *X, float *Y, float *DownSP,
	float MoveX, float MoveY, float Size, char *JumpFlag);

// アクションサンプルプログラムメイン関数
int ActMain();

// マップとの当たり判定( 戻り値 0:当たらなかった  1:左辺に当たった  2:右辺に当たった
//                                                3:上辺に当たった  4:下辺に当たった

int MapHitCheck(float X, float Y, float *MoveX, float *MoveY);
///////////////////////////////////////////////////////////////////////////////////////////////


// WinMain関数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	LPSTR lpCmdLine, int nCmdShow)
{
	// 警告避け
	(void)hInstance; (void)hPrevInstance; (void)lpCmdLine; (void)nCmdShow;

	ChangeWindowMode(TRUE);
	SetWaitVSyncFlag(FALSE); //画面更新が遅くならないようにする
							 // DXライブラリの初期化
	if (DxLib_Init() == -1) return -1;

	PlayMusic("test.mp3", DX_PLAYTYPE_BACK); // test.midのバックグラウンド演奏


	// アクションゲームのメイン関数を呼ぶ
	ActMain();

	// DXライブラリの後始末
	DxLib_End();

	// 終了
	return 0;
}

// アクションサンプルプログラムメイン
int ActMain(void)
{
	int i;

	// 描画先を裏画面にセット
	SetDrawScreen(DX_SCREEN_BACK);

	// 垂直同期信号を待たない
	SetWaitVSyncFlag(FALSE);


	//ロード
	Player = LoadGraph("marimo.jpg");
	Shot = LoadGraph("ビーム2.jpg");
	coin = LoadGraph("coin.jpg");

	// プレイヤーの座標を初期化
	PlX = 320.0F;        PlY = 240.0F;

	// プレイヤーの落下速度を初期化
	PlDownSp = 0.0F;

	// ジャンプ中フラグを倒す
	PlJumpFlag = FALSE;

	// ショットの存在を初期化する
	for (i = 0; i < MAX_SHOT; i++)
		ShotValid[i] = 0;

	// 入力状態の初期化
	Input = 0;
	EdgeInput = 0;

	// 60FPS固定用、時間保存用変数を現在のカウント値にセット
	FrameStartTime = GetNowCount();

	// メインループ開始、ESCキーで外に出る
	while (ProcessMessage() == 0 && CheckHitKey(KEY_INPUT_ESCAPE) == 0)
	{
		int Key;
		int OldKey;	// 前のキー入力状態

					// キー入力取得
		Key = 0;
		OldKey = Key;
		Key = GetJoypadInputState(DX_INPUT_KEY_PAD1);

		// 画面のクリア
		ClsDrawScreen();

		// 1/60秒立つまで待つ
		while (GetNowCount() - FrameStartTime < 1000 / 60) {}

		// 現在のカウント値を保存
		FrameStartTime = GetNowCount();

		// 入力状態を更新
		{
			int i;

			// パッド1とキーボードから入力を得る
			i = GetJoypadInputState(DX_INPUT_KEY_PAD1);

			// エッジを取った入力をセット
			EdgeInput = i & ~Input;

			// 入力状態の保存
			Input = i;
		}

		////////// プレイヤーの移動処理////////////////////////////////////////////////////////////
		{
			float MoveX, MoveY;

			// 移動量の初期化
			MoveX = 0.0F;
			MoveY = 0.0F;

			// 左右の移動を見る
			if ((Input & PAD_INPUT_LEFT) != 0) MoveX -= SPEED;
			if ((Input & PAD_INPUT_RIGHT) != 0) MoveX += SPEED;

			// 地に足が着いている場合のみジャンプボタン(ボタン1 or Zキー)を見る
			if (PlJumpFlag == FALSE && (EdgeInput & PAD_INPUT_B) != 0)
			{
				PlDownSp = -JUMP_POWER;
				PlJumpFlag = TRUE;
			}

			// 落下処理
			PlDownSp += G;

			// 落下速度を移動量に加える
			MoveY = PlDownSp;

			// 移動量に基づいてキャラクタの座標を移動
			CharMove(&PlX, &PlY, &PlDownSp, MoveX, MoveY, CHAR_SIZE, &PlJumpFlag);
		}
		//////////// ショットの移動処理/////////////////////////////////////////////////////////////


		for (i = 0; i < MAX_SHOT; i++)
		{
			// ショットデータが無効だったらスキップ
			if (ShotValid[i] == 0) continue;

			// 位置を上にずらす
			ShotX[i] += 8;

			// 画面外に出ていたらショットデータを無効にする
			if (ShotX[i] < -32) ShotValid[i] = 0;
		}

		// ショットボタンを押していたらショットを出す
		// 一つ前のループでショットボタンを押していたらショットは出さない
		if ((Key & ~OldKey) & PAD_INPUT_A)
		{
			// 使われていないショットデータを探す
			for (i = 0; i < MAX_SHOT; i++)
			{
				if (ShotValid[i] == 0) break;
			}

			// もし使われていないショットデータがあったらショットを出す
			if (i != MAX_SHOT)
			{
				// ショットの位置を設定
				ShotX[i] = (int)PlX + 17;
				ShotY[i] = (int)PlY + 5;

				// ショットデータを使用中にセット
				ShotValid[i] = 1;
			}
		}
		////////// マップの描画///////////////////////////////////////////////////////////////////////
		//自キャラの座標の代入
		charx = (int)PlX;

		//カメラ位置を計算
		camerax = charx - SCREEN_WIDTH / 2;

		//左右の補正
		if (camerax < 0)
		{
			camerax = 0;
		}
		if (camerax >(MAP_PIXEL_WIDTH - SCREEN_WIDTH))
		{
			camerax = (MAP_PIXEL_WIDTH - SCREEN_WIDTH);
		}

		//画面内の位置を求める。
		view_charx = charx - camerax;

		int i, j;

		for (i = 0; i < MAP_HEIGHT; i++)
		{
			for (j = 0; j < MAP_WIDTH; j++)
			{
				// 1は当たり判定チップを表しているので1のところだけ描画
				if (MapData[i][j] == 1)
				{
					DrawBox(j * CHIP_SIZE - camerax, i * CHIP_SIZE,
						j * CHIP_SIZE + CHIP_SIZE - camerax, i * CHIP_SIZE + CHIP_SIZE,
						GetColor(255, 255, 255), TRUE);
				}

				// 2でコインを表示
				if (MapData[i][j] == 2)
				{
					DrawGraph(j * CHIP_SIZE - camerax, i * CHIP_SIZE, coin, TRUE);
				}

			}
		}


		/////////////////////////////////////////////////////////////////////////////////////////////


		// キャラクタの描画
		DrawGraph((int)(PlX - CHAR_SIZE * 0.5F), (int)(PlY - CHAR_SIZE * 0.5F) + 1, Player, TRUE);

		// ショットを描画する
		for (i = 0; i < MAX_SHOT; i++)
		{
			// ショットデータが有効な時のみ描画
			if (ShotValid[i] == 1)
			{
				DrawGraph(ShotX[i], ShotY[i], Shot, TRUE);
			}
		}

		// 画面の更新
		ScreenFlip();
	}

	// 終了
	return 0;
}

// キャラクタをマップとの当たり判定を考慮しながら移動する
int CharMove(float *X, float *Y, float *DownSP,
	float MoveX, float MoveY, float Size, char *JumpFlag)
{
	float Dummy = 0.0F;
	float hsize;

	// キャラクタの左上、右上、左下、右下部分が当たり判定のある
	// マップに衝突しているか調べ、衝突していたら補正する

	// 半分のサイズを算出
	hsize = Size * 0.5F;

	// 先ず上下移動成分だけでチェック
	{
		// 左下のチェック、もしブロックの上辺に着いていたら落下を止める
		if (MapHitCheck(*X - hsize, *Y + hsize, &Dummy, &MoveY) == 3) *DownSP = 0.0F;

		// 右下のチェック、もしブロックの上辺に着いていたら落下を止める
		if (MapHitCheck(*X + hsize, *Y + hsize, &Dummy, &MoveY) == 3) *DownSP = 0.0F;

		// 左上のチェック、もしブロックの下辺に当たっていたら落下させる
		if (MapHitCheck(*X - hsize, *Y - hsize, &Dummy, &MoveY) == 4) *DownSP *= -1.0F;

		// 右上のチェック、もしブロックの下辺に当たっていたら落下させる
		if (MapHitCheck(*X + hsize, *Y - hsize, &Dummy, &MoveY) == 4) *DownSP *= -1.0F;

		// 上下移動成分を加算
		*Y += MoveY;
	}

	// 後に左右移動成分だけでチェック
	{
		// 左下のチェック
		MapHitCheck(*X - hsize, *Y + hsize, &MoveX, &Dummy);

		// 右下のチェック
		MapHitCheck(*X + hsize, *Y + hsize, &MoveX, &Dummy);

		// 左上のチェック
		MapHitCheck(*X - hsize, *Y - hsize, &MoveX, &Dummy);

		// 右上のチェック
		MapHitCheck(*X + hsize, *Y - hsize, &MoveX, &Dummy);

		// 左右移動成分を加算
		*X += MoveX;
	}

	// 接地判定
	{
		// キャラクタの左下と右下の下に地面があるか調べる
		if (GetChipParam(*X - Size * 0.5F, *Y + Size * 0.5F + 1.0F) == 0 &&
			GetChipParam(*X + Size * 0.5F, *Y + Size * 0.5F + 1.0F) == 0)
		{
			// 足場が無かったらジャンプ中にする
			*JumpFlag = TRUE;
		}
		else
		{
			// 足場が在ったら接地中にする
			*JumpFlag = FALSE;
		}
	}

	// 終了
	return 0;
}


// マップとの当たり判定( 戻り値 0:当たらなかった  1:左辺に当たった  2:右辺に当たった
//                                                3:上辺に当たった  4:下辺に当たった
int MapHitCheck(float X, float Y,
	float *MoveX, float *MoveY)
{
	float afX, afY;

	// 移動量を足す
	afX = X + *MoveX;
	afY = Y + *MoveY;

	// 当たり判定のあるブロックに当たっているかチェック
	if (GetChipParam(afX, afY) == 1)
	{
		float blx, bty, brx, bby;

		// 当たっていたら壁から離す処理を行う

		// ブロックの上下左右の座標を算出
		blx = (float)((int)afX / CHIP_SIZE) * CHIP_SIZE;        // 左辺の X 座標
		brx = (float)((int)afX / CHIP_SIZE + 1) * CHIP_SIZE;    // 右辺の X 座標

		bty = (float)((int)afY / CHIP_SIZE) * CHIP_SIZE;        // 上辺の Y 座標
		bby = (float)((int)afY / CHIP_SIZE + 1) * CHIP_SIZE;    // 下辺の Y 座標

																// 上辺に当たっていた場合
		if (*MoveY > 0.0F)
		{
			// 移動量を補正する
			*MoveY = bty - Y - 1.0F;

			// 上辺に当たったと返す
			return 3;
		}

		// 下辺に当たっていた場合
		if (*MoveY < 0.0F)
		{
			// 移動量を補正する
			*MoveY = bby - Y + 1.0F;

			// 下辺に当たったと返す
			return 4;
		}

		// 左辺に当たっていた場合
		if (*MoveX > 0.0F)
		{
			// 移動量を補正する
			*MoveX = blx - X - 1.0F;

			// 左辺に当たったと返す
			return 1;
		}

		// 右辺に当たっていた場合
		if (*MoveX < 0.0F)
		{
			// 移動量を補正する
			*MoveX = brx - X + 1.0F;

			// 右辺に当たったと返す
			return 2;
		}

		// ここに来たら適当な値を返す
		return 4;
	}

	// どこにも当たらなかったと返す
	return 0;
}

// マップチップの値を取得する関数
int GetChipParam(float X, float Y)
{
	int x, y;

	// 整数値へ変換
	x = (int)X / CHIP_SIZE;
	y = (int)Y / CHIP_SIZE;

	// マップからはみ出ていたら 0 を返す
	if (x >= MAP_WIDTH || y >= MAP_HEIGHT || x < 0 || y < 0) return 0;

	// 指定の座標に該当するマップの情報を返す
	return MapData[y][x];
}

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: 弾当たり判定について

#2

投稿記事 by softya(ソフト屋) » 8年前

まったく同一の質問と思われるもう一つ方を削除させていただきました。
回答がつかないうちに新しい質問はお避けください。
それと、こちらの質問もまともに解決しておりませんので、回答がつかないのはvjhsvb さんが回答者とやりとりをちゃんと行っていないのが原因かと思います。
http://dixq.net/forum/viewtopic.php?f=3&t=17591#p136243
もう一度、ちゃんと回答者とちゃんとやりとりをされてはどうでしょうか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

vjhsvb
記事: 6
登録日時: 8年前

Re: 弾当たり判定について

#3

投稿記事 by vjhsvb » 8年前

softya(ソフト屋) さんが書きました:まったく同一の質問と思われるもう一つ方を削除させていただきました。
回答がつかないうちに新しい質問はお避けください。
それと、こちらの質問もまともに解決しておりませんので、回答がつかないのはvjhsvb さんが回答者とやりとりをちゃんと行っていないのが原因かと思います。
http://dixq.net/forum/viewtopic.php?f=3&t=17591#p136243
もう一度、ちゃんと回答者とちゃんとやりとりをされてはどうでしょうか?
回答者から返信が来なく、完成しないといけない期日までに時間がない場合はどうすれば良いですか

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: 弾当たり判定について

#4

投稿記事 by softya(ソフト屋) » 8年前

vjhsvb さんが書きました:
softya(ソフト屋) さんが書きました:まったく同一の質問と思われるもう一つ方を削除させていただきました。
回答がつかないうちに新しい質問はお避けください。
それと、こちらの質問もまともに解決しておりませんので、回答がつかないのはvjhsvb さんが回答者とやりとりをちゃんと行っていないのが原因かと思います。
http://dixq.net/forum/viewtopic.php?f=3&t=17591#p136243
もう一度、ちゃんと回答者とちゃんとやりとりをされてはどうでしょうか?
回答者から返信が来なく、完成しないといけない期日までに時間がない場合はどうすれば良いですか
vjhsvb さんがちゃんと全て返答し、丸投げにならないように努力されれば返答が来ない事態は避けらると思います。
逆に何度も同内容で投稿すれば、回答者のメンツは同じですので回答が来ない確率が上がるだけです。

【補足】当掲示板のフォーラムルールの説明のため補足します。 http://dixq.net/board/board.html
質問者と回答者が気持ち良く掲示板を利用するためのルールとなっています。
掲示板では課題の丸投げを原則として禁止させていただいております。 ここまでやったけど、ここの部分がうまく動作しないだとOKです。
なので1・2の質問は問題ありません。ただ、実行用データがないので確認には手間がかかります。そのため回答がつき辛いでしょう。
3に関しては、丸投げになりますのでプログラムではなくうまい方法だけ聞けば良いのではないでしょうか?
それと下記のやり取りで、みけCATさんの回答をスルーされていますよね。
http://dixq.net/forum/viewtopic.php?f=3&t=17591#p136036
これはみけCATさんに失礼ですよね。そういう質問者に他の回答者も回答する気が起きるでしょうか?考えてみてください。
ルール的にも違反となります。

「完成しないといけない期日までに時間がない場合」
これは初めて聞きました。今までの質問に書かれてましたか?
vjhsvb さんの都合はこちらには何も言わないと伝わりませんよ。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

閉鎖

“C言語何でも質問掲示板” へ戻る