天井に衝突すると、天井をすり抜けてしまう。

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

天井に衝突すると、天井をすり抜けてしまう。

#1

投稿記事 by keito94 » 7年前

アクションゲームを制作する中でジャンプなどを実装してきましたが、天井に対する当たり判定で不具合が起こったので、質問したいと思います。
具体的には天井に衝突すると天井をすり抜けるということとジャンプ無しで落下すると高速で落下するということです。

Collision.h

コード:

#pragma once
class CCollision {
	float OffsetL, OffsetU, OffsetR, OffsetD;
	POINT mPt[8];
	int ColType;
public:
	CCollision(POINT pt[], float offset_l, float offset_u, float offset_r, float offset_d, int col_type);
	void Init();
	void GetMove(float* myx, float* myy, float* addx, float* addy, int* jcount, bool* gflag);
	POINT CheckMap(int x, int y);
	void GetXPosition(float* myx, float* myy, float* addx, float* addy);
	void GetYPosition(float* myx, float* myy, float* addx, float* addy, int* jcount, bool* gflag);
};
Collision.h

コード:

#include "Main.h"
#include "Mapping.h"
#include "Collision.h"

CCollision::CCollision(POINT pt[], float offset_l, float offset_u, float offset_r, float offset_d, int col_type)
	: OffsetL(offset_l), OffsetU(offset_u), OffsetR(offset_r), OffsetD(offset_d), ColType(col_type){
	Init();
	for (int i = 0; i < 8; i++)
	{
		mPt[i].x = pt[i].x;
		mPt[i].y = pt[i].y;
	}
}

void CCollision::Init(){
	ZeroMemory(mPt, sizeof(mPt));
}

POINT CCollision::CheckMap(int x, int y){
	POINT pt[] ={	
		{ x + mPt[0].x,	y + mPt[0].y },//左上
		{ x + mPt[1].x,	y + mPt[1].y },//上真ん中
		{ x + mPt[2].x,	y + mPt[2].y },//右上

		{ x + mPt[3].x,	y + mPt[3].y },//左真ん中
		{ x + mPt[4].x,	y + mPt[4].y },//右真ん中

		{ x + mPt[5].x,	y + mPt[5].y },//左下
		{ x + mPt[6].x,	y + mPt[6].y },//下真ん中
		{ x + mPt[7].x,	y + mPt[7].y },//右下
	};

	POINT res = { x, y };
	DWORD cwidth = MapObj.GetChipWidth();
	DWORD cheight = MapObj.GetChipHeight();
	//マップエディターの特性上、マップチップを0にしないと当たり判定を認識してくれないようです。
	for (int i = 0; i < 8; i++){
		//当たり判定のレイヤを読み込む。
		int index = MapObj.GetValue(1, pt[i].x / MAP_CELL, pt[i].y / MAP_CELL);
		int src_x = (index % BitCount) * cwidth;
		int src_y = (index / BitCount) * cheight;
		if (src_x == 0 && src_y == 0){
			res.x = pt[i].x / MAP_CELL;//壁の座標を代入
			res.y = pt[i].y / MAP_CELL;
			return res;//壁の座標を返す
		}
	}
	res.x = -1;//ここまで来たら当たっていないということ
	res.y = -1;
	return res;
}

void CCollision::GetXPosition(float* myx, float* myy, float* addx, float* addy) {
	float newx;
	float tempx = *myx;
	float tempaddx = *addx;

	newx = tempx + tempaddx;
	POINT block = CheckMap((int)newx, (int)*myy);
	if (block.x == -1) {
		*myx = newx;
	}
	else {
		if (tempaddx > 0) {
			*myx = (float)((block.x - 1) * 16 + 8 + OffsetR - 0.1f);
		}
		else {
			*myx = (float)((block.x + 1) * 16 + 8 + OffsetL + 0.1f);
		}
		*addx = 0;//当たっているので移動させない。
	}

}

void CCollision::GetYPosition(float* myx, float* myy, float* addx, float* addy, int* jcount, bool* gflag) {
	float newy = *myy + *addy;
	POINT block = CheckMap((int)*myx, (int)newy);
	if (block.y == -1) {
		*myy = newy;
		gflag = false;
	}
	else {
		if (addy < 0) {
			//何故か、天井をすり抜けてしまう。
			*myy = (float)((block.y + 1) * 16 + 8 - OffsetU);
		}
		else {
			*myy = (float)((block.y - 1) * 16 + 8 + OffsetD - 0.1f);
			*jcount = 0;
			*gflag = true;
		}
	}
}

void CCollision::GetMove(float* myx, float* myy, float* addx, float* addy, int* jcount, bool* gflag) {
	GetXPosition(myx, myy, addx, addy);

	if (*addy > GRAVMAX)
	{
		*addy = GRAVMAX;
	}
	else
	{
		*addy += GRAV;
	}

	GetYPosition(myx, myy, addx, addy, jcount, gflag);
}
(一部のみなので動かないということをご了承ください。)
最後に編集したユーザー keito94 on 2017年4月11日(火) 15:26 [ 編集 1 回目 ]
デバッグは投げ捨てるものではない。
今までの質問でこれは学んだこと。
質問する時は、必ずちゃんと調べた上に問題をもとにした仕様書を作ってから質問すること。
仕様書の大切さを改めて思い知った…。

アバター
keito94
記事: 264
登録日時: 7年前
連絡を取る:

Re: 天井に衝突すると、天井をすり抜けてしまう。

#2

投稿記事 by keito94 » 7年前

ソースコード追加。

Mapping.h

コード:

#pragma once
#include "fmfmap.h"
//マップ描画エンジンのオブジェクトクラスを宣言
extern CFmfMap MapObj;
//ビットカウントの宣言。
extern int BitCount;
//マップチップのサイズ
const int MAP_CELL = 16;
Main.h(一部のみ)

コード:

const float GRAVMAX = 10.0f;
const float GRAV = 0.5F;
Player.cpp(一部のみ)

コード:

bool CPlayer::Move() {
	JoyPad.Update();
	if (JoyPad.Get(PAD_INPUT_RIGHT)) {
		Dir = FALSE;
		VX = 3.0F;
	}
	else if (JoyPad.Get(PAD_INPUT_LEFT)){
		Dir = TRUE;
		VX = -3.0F;
	}
	else {
		VX = 0;
	}
	if (JoyPad.Get(PAD_INPUT_1) > 0 && JumpCount < 5) {
		if (JumpCount < 1 && GroundFlag) {
			JumpCount++;
			VY = -9.0F;
		}
	}
	ColObj->GetMove(&X, &Y, &VX, &VY, &JumpCount, &GroundFlag);
	return true;
}
最後に編集したユーザー keito94 on 2017年4月11日(火) 16:18 [ 編集 1 回目 ]
デバッグは投げ捨てるものではない。
今までの質問でこれは学んだこと。
質問する時は、必ずちゃんと調べた上に問題をもとにした仕様書を作ってから質問すること。
仕様書の大切さを改めて思い知った…。

アバター
keito94
記事: 264
登録日時: 7年前
連絡を取る:

Re: 天井に衝突すると、天井をすり抜けてしまう。

#3

投稿記事 by keito94 » 7年前

更にソースコード追加。
Stage.h

コード:

#pragma once
class CStage {
public:
	void Draw();
};
Stage.cpp

コード:

#include "Main.h"
#include "Stage.h"
#include "Mapping.h"
void CStage::Draw() {
	DWORD cwidth = MapObj.GetChipWidth();
	DWORD cheight = MapObj.GetChipHeight();
	for (int y = 0; y < 15; y++) {
		for (int x = 0; x < 20; x++) {
			int src_x, src_y, index;
			index = MapObj.GetValue(0, x, y);
			src_x = (index % BitCount) * cwidth;
			src_y = (index / BitCount) * cheight;
			DrawRectGraph((int)(x*MAP_CELL), (int)(y*MAP_CELL), src_x, src_y, MAP_CELL, MAP_CELL, Game->MapGraph, TRUE, FALSE);
		}
	}
}
デバッグは投げ捨てるものではない。
今までの質問でこれは学んだこと。
質問する時は、必ずちゃんと調べた上に問題をもとにした仕様書を作ってから質問すること。
仕様書の大切さを改めて思い知った…。

アバター
keito94
記事: 264
登録日時: 7年前
連絡を取る:

Re: 天井に衝突すると、天井をすり抜けてしまう。

#4

投稿記事 by keito94 » 7年前

ソースコードの全体を上げてみた。どこかおかしな点があったら教えてください。
Main.h

コード:

#pragma once
#include "DxLib.h"

class CPlayer;
class CGameMain {
	int Time;
public:
	int PlayerGh;
	int MapGraph;
	CGameMain();
	~CGameMain();
	void ErrMes(LPCSTR str, ...);
	int MapLoad(const char* mapdata,const char* mapchip);
	void Run();
};
const float GRAVMAX = 10.0f;
const float GRAV = 0.5F;
extern CGameMain *Game;
Main.cpp

コード:

#include "Main.h"
#include "Mapping.h"
#include "Stage.h"
#include "Player.h"

CFmfMap MapObj;
CGameMain *Game;
CPlayer *Player;
CStage *Stage;

int BitCount;

CGameMain::CGameMain() {
	ChangeWindowMode(TRUE); SetGraphMode(320, 240, 32);
	DxLib_Init();
	SetDrawScreen(DX_SCREEN_BACK);
	PlayerGh = LoadGraph("テスト用.png");
	MapLoad("テスト用.fmf", "マップチップ.bmp");
	Player = new CPlayer(160.0F, 215.0F);
}

void CGameMain::Run() {
	Player->Move();
	Stage->Draw();
	Player->Draw();
}

int CGameMain::MapLoad(const char* mapdata, const char* mapchip) {
	if (!MapObj.Open(mapdata)) {
		ErrMes("Error Code 30: MapData is Not Loading.");
		return -1;
	}
	BYTE* layer = (BYTE*)MapObj.GetLayerAddr(0);
	if (layer == NULL) {
		ErrMes("Error Code 31: Null Layer.");
		MapObj.Close();
		return -1;
	}
	if ((MapGraph = LoadGraph(mapchip)) == -1) {
		ErrMes("Error Code 41: MapChip is Not Loading.");
		return -1;
	}
	MapGraph = LoadGraph(mapchip);
	BitCount = MapObj.GetLayerBitCount() == 8 ? 16 : 256;
}

void CGameMain::ErrMes(LPCSTR str, ...) {
	char buf[1024];
	wsprintf(buf, str, (char*)(&str + 1));
	MessageBox(NULL, buf, "Error!!", MB_OK);
}

CGameMain::~CGameMain() {
	DxLib_End();
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPervInstance, LPSTR lpCmdLine, int nCmdShow) {
	Game = new CGameMain;
		while(ScreenFlip()==0&&ProcessMessage()==0&&ClearDrawScreen()==0) {
			Game->Run();
		}
	delete Game;
}
Mover.h

コード:

#pragma once
class CMover {
public:
	float X, Y, L, T, R, B;
	float VX, VY;
	CMover(float x, float y, float l, float t, float r, float b);
};
Mover.cpp

コード:

#include "Mover.h"
CMover::CMover(float x, float y, float l, float t, float r, float b)
	:X(x), Y(y), L(l), T(t), R(r), B(b), VX(0.0F), VY(0.0F) {

}
Player.h

コード:

#pragma once
#include "Mover.h"
class CCollision;
class CPlayer : public CMover {
protected:
	CCollision* ColObj;
	int JumpCount;
	float Grav;
	int Dir;
	bool GroundFlag;
public:
	CPlayer(float x, float y);
	bool Move();
	void Draw();
};
Player.cpp

コード:

#include "Main.h"
#include "Player.h"
#include "JoyPad.h"
#include "Collision.h"

CJoyPad JoyPad;

CPlayer::CPlayer(float x, float y)
	:CMover(x, y, -8, -8, 8, 8),Dir(FALSE),JumpCount(0),GroundFlag(false),Grav(0.5F) {
	POINT pt[] = {
		{ (LONG)L,	(LONG)T },//左上
		{ 0,			(LONG)T },//上真ん中
		{ (LONG)R,	(LONG)T },//右上

		{ (LONG)L,	0 },//左真ん中
		{ (LONG)R,	0 },//右真ん中

		{ (LONG)L,	(LONG)B },//左下
		{ 0,			(LONG)B },//下真ん中
		{ (LONG)R,	(LONG)B },//右下
	};
	ColObj = new CCollision(pt, 0, 0, 0, 0, 0);
}

bool CPlayer::Move() {
	JoyPad.Update();
	if (JoyPad.Get(PAD_INPUT_RIGHT)) {
		Dir = FALSE;
		VX = 3.0F;
	}
	else if (JoyPad.Get(PAD_INPUT_LEFT)){
		Dir = TRUE;
		VX = -3.0F;
	}
	else {
		VX = 0;
	}
	if (JoyPad.Get(PAD_INPUT_1) > 0 && JumpCount < 5) {
		if (JumpCount < 1 && GroundFlag) {
			JumpCount++;
			VY = -9.0F;
		}
	}
	ColObj->GetMove(&X, &Y, &VX, &VY, &JumpCount, &GroundFlag);
	return true;
}

void CPlayer::Draw() {
	DrawRotaGraph((int)X, (int)Y, 1.0, 0.0, Game->PlayerGh, FALSE, Dir);
}
デバッグは投げ捨てるものではない。
今までの質問でこれは学んだこと。
質問する時は、必ずちゃんと調べた上に問題をもとにした仕様書を作ってから質問すること。
仕様書の大切さを改めて思い知った…。

アバター
keito94
記事: 264
登録日時: 7年前
連絡を取る:

Re: 天井に衝突すると、天井をすり抜けてしまう。

#5

投稿記事 by keito94 » 7年前

どうやら僕自身の簡単なミスで、条件分岐の部分にポインタが使われていないことに原因があったようです。
しっかり修正しました。自己解決いたしましたのでこの質問を示させていただきます。
デバッグは投げ捨てるものではない。
今までの質問でこれは学んだこと。
質問する時は、必ずちゃんと調べた上に問題をもとにした仕様書を作ってから質問すること。
仕様書の大切さを改めて思い知った…。

アバター
keito94
記事: 264
登録日時: 7年前
連絡を取る:

Re: 天井に衝突すると、天井をすり抜けてしまう。

#6

投稿記事 by keito94 » 7年前

しまった解決押すの忘れてた!!
デバッグは投げ捨てるものではない。
今までの質問でこれは学んだこと。
質問する時は、必ずちゃんと調べた上に問題をもとにした仕様書を作ってから質問すること。
仕様書の大切さを改めて思い知った…。

返信

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