2dアクションゲームの坂道

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
foo

2dアクションゲームの坂道

#1

投稿記事 by foo » 8年前

Dxlibを使って2dのアクションゲームを作っているのですが、

坂道の部分の作り方がわかりません

マップを作るとき、マップデータの配列を読込んでその情報をもとに四角いマップチップ?からマップを作るので階段状になってしまうと思うのですが、

マップとのあたり判定もプレイヤーのいる位置を配列の番号に変換して、移動量を足したときの配列の番号がマップの番号になっていないか確認するので

どうやっても無理なんじゃないかとおもっているのですが、どうやって作っているのですか?

あんどーなつ
記事: 171
登録日時: 8年前
連絡を取る:

Re: 2dアクションゲームの坂道

#2

投稿記事 by あんどーなつ » 8年前

スーパーマリオ2までは坂道がないけど、3からは出てきてますよね。
坂道は斜めの直線なので、古典的な当たり判定の式では判定できないです。

予想ですけど、ピクセル画像の単位が32x32としたときに、

コード:

char sakamiti_atari[32][32] = {
0,0,0...
1,0,0...
1,1,0...
1,1,1...
...
};
みたいなのを作って、キャラクターの周りの坂道について判定していると思います。
判定しなければならない坂道は最高6つになりますよね
(キャラクター2x1で中途半端なところにいるのを勘案して3x2)。

foo

Re: 2dアクションゲームの坂道

#3

投稿記事 by foo » 8年前

お聞きしたいのですが
予想ですけど、ピクセル画像の単位が32x32としたときに、
char sakamiti_atari[32][32] = {
0,0,0...
1,0,0...
1,1,0...
1,1,1...
...
};
これはつまり1pxごとにあたり判定を取るということでしょうか?

判定しなければならない坂道は最高6つになりますよね
(キャラクター2x1で中途半端なところにいるのを勘案して3x2)。
すみません。どこの6つなのでしょうか?

0,0,0...
1,player,0...
1,1,0...
1,1,1...
...

このプレイヤーの位置での下端、左端

0,0,0...
1,0,player...
1,1,0...
1,1,1...
...

このプレイヤーの位置での角部分

これの左右で6つということですか?

あんどーなつ
記事: 171
登録日時: 8年前
連絡を取る:

Re: 2dアクションゲームの坂道

#4

投稿記事 by あんどーなつ » 8年前

右上がりの坂がバグっていますが、やり方は示していると思います。
直線の坂なので、当たり判定を配列ではなく、1次不等式に変えました。
(製作時間5時間くらいかかっているから勘弁してほしいー)

コード:

#include "DxLib.h"

#define _USE_MATH_DEFINES

#include <math.h>

const int BLOCKSIZE = 20;

const UCHAR map_transpose[6][7] = {
	//   012345
		"WWWWWW",
		"W    W",
		"W    W",
		"W    W",
		"W@ P/W",
		"WWWWWW" };

UCHAR map[6][6];

int px, py; // キャラクターの位置
int vx, vy; // キャラクターの速度
int npx, npy; // キャラクターの次の位置
bool up = false, left = false, right = false; // キー状態

void DrawMap() {
	for (int i = 0; i < 6; i++) {
		for (int j = 0; j < 6; j++) {
			if (map[i][j] == 'W')
				DrawBox(i * BLOCKSIZE, j * BLOCKSIZE, (i + 1)*BLOCKSIZE, (j + 1)*BLOCKSIZE,
					GetColor(0xff, 0x00, 0x00), TRUE);
			else if (map[i][j] == '@')
				DrawTriangle(i * BLOCKSIZE, j * BLOCKSIZE, (i + 1)*BLOCKSIZE, (j + 1)*BLOCKSIZE,
					i*BLOCKSIZE, (j + 1)*BLOCKSIZE, GetColor(0xff, 0x00, 0x00), TRUE);
			else if (map[i][j] == '/')
				DrawTriangle(i * BLOCKSIZE, (j + 1) * BLOCKSIZE, (i + 1)*BLOCKSIZE, j*BLOCKSIZE,
					(i + 1)*BLOCKSIZE, (j + 1)*BLOCKSIZE, GetColor(0xff, 0x00, 0x00), TRUE);
		}
	}
}

void DrawCharacter() {
	DrawBox(px, py, px + BLOCKSIZE, py + 2 * BLOCKSIZE,
		GetColor(0x00, 0xff, 0x00), TRUE);
}

bool IsContact() {
	int i = px / BLOCKSIZE, j = py / BLOCKSIZE;
	if (px % BLOCKSIZE == 0 && py % BLOCKSIZE == 0) {
		// キャラクターはブロック交差点上
		if (map[i][j + 2] == 'W' || map[i][j + 2] == '@' || map[i][j + 2] == '/') return true;
		if (map[i - 1][j + 2] == '@' || map[i - 1][j + 2] == '/') return true;
		if (map[i + 1][j + 2] == '@' || map[i + 1][j + 2] == '/') return true;
		return false;
	}
	if (py % BLOCKSIZE == 0) {
		// キャラクターはブロック横棒境界線上
		if (map[i][j + 2] == 'W' || map[i + 1][j + 2] == 'W') return true;
		return false;
	}
	if (px % BLOCKSIZE == 0) {
		// キャラクターはブロック縦線境界線上
		return false;
	}
	// キャラクターはブロック内
	if (map[i][j + 2] == '@') {
		if (px % BLOCKSIZE == py % BLOCKSIZE) return true;
	}
	if (map[i][j + 2] == '/') {
		if ((px % BLOCKSIZE) + (py % BLOCKSIZE) == BLOCKSIZE - 1) return true;
	}
	return false;
}

// プログラムは WinMain から始まります
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	LPSTR lpCmdLine, int nCmdShow)
{
	if (DxLib_Init() == -1) return -1;

	// マップを転置(x->y, y->x)
	for (int i = 0; i < 6; i++)
		for (int j = 0; j < 6; j++)
			map[i][j] = map_transpose[j][i];

	// キャラクターの初期化
	for (int i = 0; i < 6; i++) {
		for (int j = 0; j < 6; j++) {
			if (map[i][j] == 'P') {
				px = i * BLOCKSIZE;
				py = (j - 1) * BLOCKSIZE;
			}
		}
	}

	SetDrawScreen(DX_SCREEN_BACK);

	while (!CheckHitKey(KEY_INPUT_ESCAPE)) {
		up = (CheckHitKey(KEY_INPUT_UP) != 0);
		left = (CheckHitKey(KEY_INPUT_LEFT) != 0);
		right = (CheckHitKey(KEY_INPUT_RIGHT) != 0);
		ClearDrawScreen();
		DrawMap();
		DrawCharacter();

		if (IsContact()) {
			if (up) vy = -15; else vy = 0;
			if (left) vx = -1;
			else if (right) vx = 1;
			else vx = 0;
		}
		else {
			vy += 2;
		}

		npx = px + vx;
		npy = py + vy;

		// 横の接触修正
		int ni = npx / BLOCKSIZE;
		int nj = npy / BLOCKSIZE;
		if (npx % BLOCKSIZE == 0) {
			// ブロック縦線境界上
			; // void
		}
		else if (npy % BLOCKSIZE == 0) {
			// ブロック横線境界上
			if (vx > 0 && (map[ni + 1][nj] == 'W' || map[ni + 1][nj + 1] == 'W'))
				npx -= npx % BLOCKSIZE;
			if (vx < 0 && (map[ni][nj] == 'W' || map[ni][nj + 1] == 'W'))
				npx += BLOCKSIZE - npx % BLOCKSIZE;
		}
		else {
			if (vx > 0 && (map[ni + 1][nj] == 'W' || map[ni + 1][nj + 1] == 'W' || map[ni + 1][nj + 2] == 'W'))
				npx -= npx % BLOCKSIZE;
			if (vx < 0 && (map[ni][nj] == 'W' || map[ni][nj + 1] == 'W' || map[ni][nj + 2] == 'W'))
				npx += BLOCKSIZE - npx % BLOCKSIZE;
		}

		// 縦の接触修正
		if (npy % BLOCKSIZE == 0 && npx % BLOCKSIZE == 0) {
			// ブロック境界交差点上
			; // void
		} else if (npy % BLOCKSIZE == 0) {
			// ブロック横線境界上
			if (map[ni][nj + 1] == '@') {
				npy -= BLOCKSIZE;
				npy += npx % BLOCKSIZE;
				vy = 0;
			}
			if (map[ni + 1][nj + 1] == '/') {
				npy = npy - (npx % BLOCKSIZE);
				vy = 0;
			}
		}
		else if (npx % BLOCKSIZE == 0) {
			// ブロック縦線境界上
			if (vy < 0 && map[ni][nj] == 'W') {
				npy += BLOCKSIZE - (npy % BLOCKSIZE);
				vy = -vy;
			}
			if (vy > 0 && (map[ni][nj + 2] == 'W' || map[ni][nj+2]=='@'||map[ni][nj+2]=='/')) {
				npy -= npy % BLOCKSIZE;
				vy = 0;
			}
		}
		else {
			if (vy < 0 && (map[ni][nj] == 'W' || map[ni + 1][nj] == 'W')) {
				npy += BLOCKSIZE - (npy % BLOCKSIZE);
				vy = -vy;
			}
			if (vy > 0 && (map[ni][nj + 2] == 'W' || map[ni + 1][nj + 2] == 'W')) {
				npy -= npy % BLOCKSIZE;
				vy = 0;
			}
			if (map[ni][nj + 2] == '@' && (npy % BLOCKSIZE > npx % BLOCKSIZE)) {
				npy = npy - (npy % BLOCKSIZE) + (npx % BLOCKSIZE);
				vy = 0;
			}
			if (map[ni + 1][nj + 2] == '/' && ((npx % BLOCKSIZE) + (npy % BLOCKSIZE) > BLOCKSIZE - 1)) {
				npy = npy - (npy % BLOCKSIZE) + (BLOCKSIZE - 1) - (npx % BLOCKSIZE);
				vy = 0;
			}
		}

		px = npx;
		py = npy;
		ScreenFlip();
		ProcessMessage();
	}

	DxLib_End();               // DXライブラリ使用の終了処理

	return 0;              // ソフトの終了 
}

アバター
Dixq (管理人)
管理人
記事: 1662
登録日時: 14年前
住所: 北海道札幌市
連絡を取る:

Re: 2dアクションゲームの坂道

#5

投稿記事 by Dixq (管理人) » 8年前

複雑な地面との当たり判定であれば地面の画像の上にキャラクタの画像を上書きして画像と画像の当たり判定を計算。
もし当たっていれば進行方向と反対方向に押し戻せばいいのではないかと思います。
3Dのポリゴン地面上を歩き回るサンプルがDXライブラリのHPに乗っていますが、考え方は参考にしたらいいのではないかと思います。
http://dxlib.o.oo7.jp/program/dxprogram_3D.html

あんどーなつ
記事: 171
登録日時: 8年前
連絡を取る:

Re: 2dアクションゲームの坂道

#6

投稿記事 by あんどーなつ » 8年前

Dixq さん

貴重なご意見どうもありがとうございます。
私も少しコードを書いてみて気づいたことがあるのですが、フレーム毎の移動量を大きくすると壁のすり抜けみたいな現象が発生しました。当たり判定を何回かやり直せばうまくいくのではないかと思いますが、Dixqさんのほうではどのように行っていますか?

foo

Re: 2dアクションゲームの坂道

#7

投稿記事 by foo » 8年前

あんどーなつ さん

わざわざサンプルプログラムまで作っていただいてありがとうございます

これを参考にしばらく自分で頑張ってみようかと思います


Dixq さん

教えていただきありがとうございます。そちらも参考にしながら進めていきたいと思います

アバター
Dixq (管理人)
管理人
記事: 1662
登録日時: 14年前
住所: 北海道札幌市
連絡を取る:

Re: 2dアクションゲームの坂道

#8

投稿記事 by Dixq (管理人) » 8年前

あんどーなつさん

当たり判定の計算って正確にやろうとしたら結構真面目に数学の知識が必要になります。
当たり判定を簡略化しても、です。
で、簡略化するために、キャラクターは一般的なゲームにおいては簡単な三角形(または円)の集合の場合が多いです。
地面も三角形の集合ですから、三角形と三角形の当たり判定が計算出来ればよいことになります。
hit.png
hit.png (12.97 KiB) 閲覧数: 5631 回
現在のご質問は三角形と三角形(四角形)の当たり判定を正確に計算しても上図のようにすり抜けが判定できないと言うことだと思いますが、
これは進んでいるベクトルが分かっているので三角形の頂点がそのベクトルにどれだけ進んだのかという線分を3本計算し、
それと衝突対象との計算をすればすり抜けると言うことはありません。

で、計算方法ですが、私はこのような計算方法の参考には、この本を使っています。


この本に載っている三角形と三角形の当たり判定の計算方法の紹介を一部紹介します。
画像
画像
画像
画像
画像
画像
画像
画像
このような流れで計算可能です。
線分と三角形の当たり判定の計算方法も載っていますのでもしこのような計算方法を学びたければ書籍をどうぞ。
この本でなくとも似たような本は沢山あります。
プログラミングの特定の分野では数学が切っても切り離せない部分がありますので。

で、こんな計算をいちいちするのはめんどくさいので自分でせっせとあらかじめライブラリを作ってしまうか、
人が作ったライブラリを利用するか、DXライブラリならDXライブラリ標準関数を使うかするといいでしょう。

例えばこんな面倒な計算をしなくても、三角形と線分の当たり判定を計算してくれる関数はDXライブラリ内にあります。
http://dxlib.o.oo7.jp/function/dxfunc_3d.html#R16N4

当たり判定の計算は正直めんどくさいですよね。

あんどーなつ
記事: 171
登録日時: 8年前
連絡を取る:

Re: 2dアクションゲームの坂道

#9

投稿記事 by あんどーなつ » 8年前

Dixq さん

あたり判定について虎の巻のようなものをご教授頂けるとは思いませんでした。
本当に本当にありがとうございます。

また、己の浅学を自覚させられました。精進いたします。

閉鎖

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