ページ 11

斜め坂のアルゴリズムについて

Posted: 2012年12月09日(日) 00:05
by EKISUKE
今2Dの横スクロールのゲームで斜め坂(45度のみ)の上りの当たり判定をやっているのですが、
皆さんの坂の当たり判定のアルゴリズムを教えて欲しいです。
マップエディタで制御できるものが知りたいです。

今やっている方法としては、
プレイヤーの足元の点から移動量分だけ進んだところの配列の値を調べ、その値が上り坂番号であれば、
その配列のブロックの左上の点を求め、その点から左下の坂の開始地点と右上の終了地点を求めています。
その開始地点と終了地点を使い以下のような当たり判定をしています。

コード:

//--------------------------------------------------------------
//!	@name	線分と点との当たり判定
//!	@param	[in]	pos	点
//!	@param	[in]	begin	線分開始点
//!	@param	[in]	end		線分終了点
//--------------------------------------------------------------
float CheckHitLine(Vector2& pos, Vector2 begin, Vector2 end)
{
	Vector2 tmp;
	tmp.subtract(end,begin);
	float hitline = (begin._y) + (tmp._y/tmp._x * ( pos._x - begin._x ));	//	当たり判定のあるライン

	if( pos._y > hitline ){
		float a = pos._y - hitline; 
		return a;
	}
	return -1.0f;
}
hitlineは2つの点と現在の座標をもとに当たり判定のある高さです。
それよりpos._yが大きければその差分を返すというプログラムです。
その差分だけプレイヤーのpos._yを底上げし、そのあとプレイヤーの移動を行なっています。

floatにしているのはintにすると小数点以下の値が切り捨てられ、正しい値が返ってこないと思ったからです。
aという変数はデバック用です。
今現在ブロック1つ分はちゃんと登ってくれるのですが、右上の配列を調べてくれません。
原因はなんとなくわかっているんですが、それを直すプログラムを色々試しては、ダメというのがしばらく続いて、今その解決プログラムが思いつかないので、
皆さんが坂道はどういうアルゴリズムで当たり判定を行なっているのか、自分のプログラムの流れの悪いとこはどこかを知りたかったので、質問させていただきました。

Re: 斜め坂のアルゴリズムについて

Posted: 2012年12月09日(日) 23:43
by ISLe
坂でも平らでも、移動後にいま現在立っている足元の地面の高さを調べて、めり込んでいたら補正するだけで済むのでは?
坂の上りはそれだけで足りますが、下りはヒョコヒョコと落下する感じになってしまうので、タイル番号と坂の角度のテーブルを作るなどして、左右移動に斜め移動を組み込めば良いと思います。

Re: 斜め坂のアルゴリズムについて

Posted: 2012年12月10日(月) 00:03
by EKISUKE
やはり移動後に調べるのが当たり前なんですね・・・
今普通のブロック(四角)の方も同じように進む前に次進むところ調べてそこの配列番号がブロックだったら移動をしないとしてたので、
移動前に次のフレームで進むところを調べる方法で坂もできるかと思ってたのですが、移動後ですね、ブロックも一緒に少しやってみます。
回答有難うございます

【追記】
すこし考えてたのですが、移動後に高さを調べるということはY方向のあたり判定ということですかね?X方向は斜め移動を追加するという解釈で正しいですか?

Re: 斜め坂のアルゴリズムについて

Posted: 2012年12月10日(月) 17:04
by ISLe
当たり前と言い切って良いかは分かりませんが。
あくまでも個人的な経験が元ですが、プログラムが最もシンプルになるのは『現在を判定する』を基本とした方法だと思います。
ゲーム画面は並行世界のオブジェクトを重ねて見てるものなので、先読みは不具合の予想も含めて難しいです。

X方向に平面と同じ距離を移動して、斜め移動を加えると、合計では速度が上がってしまいますから、45度の坂であればX,Yともに√2ずつ進める必要があります。
ここで先の回答に書いた角度テーブルが役に立ちます。
少なくとも坂のブロックに触れているあいだは端数を覚えておかなくてはいけません。

Re: 斜め坂のアルゴリズムについて

Posted: 2012年12月16日(日) 12:51
by EKISUKE
返信遅くなってすみません。
坂以外のことをしていました。

今、坂ブロックは進行可能ブロックとしてみて、プレイヤーの足元の点からプレイヤーの移動量の点が坂ブロックに属していれば、坂判定を行うという形です。
どうやらこの方法だと、坂(上り)ブロックの上の方の次の坂ブロックとの隙間でプレイヤーが止まっちゃいます。
おそらく、プレイヤーの足元の点からプレイヤーの移動量分だけ伸ばした点が、
                    ⊿      ⊿
ここの坂ブロックを調べずに→⊿□     ⊿□←ここの進行不可ブロックを調べてしまうためだと思います。

この問題で、色々悩んでいるのですが、移動後に現在の高さを調べて、めり込んでいたら補正で、可能なのでしょうか?
後、普通の進行不可ブロックはプレイヤーの進行方向にプレイヤーの半径+プレイヤーの移動量の先の配列の番号を調べて、進行不可なら移動しないという形です。
うまく説明できないので一応マップチップとのあたり判定の部分のソースコードを載せます。 見ずらい場合はすみません。

map.h(map.Get_MatrixPosの部分のみ)

コード:

//	ブロックの左上の点を取る
Vector2	Map::Get_MatrixPos(Vector2 pos)
{
	//	もらった座標をブロックの大きさで割る
	int x = (int)(pos._x / BLOCK_SIZE);		
	int y = (int)(pos._y / BLOCK_SIZE);
	//	割った後の座標をブロックの大きさ分かけてあげることで、ブロックの左上の点を返す
	return Vector2(x  * BLOCK_SIZE,y  * BLOCK_SIZE);
}					 
HitManager.cpp

コード:

//	次フレームの場所を返す
Vector2 HitManager::Get_Dist_Pos(Vector2 pos, Vector2 mov)
{
	//	いったん一時保存用のベクトルを作る
	Vector2 tmp;
	//	移動量分足した点を求める
	tmp._x  = pos._x + mov._x;
	tmp._y	= pos._y + mov._y;
	return tmp;
}

//	次フレームの配列の値をとる
int	HitManager::Get_Distance_Val(Vector2 pos, Vector2 mov, Map &map)
{
	//	いったん一時保存用のベクトルを作る
	Vector2 tmp;
	//	移動先の点を求める
	tmp = Get_Dist_Pos(pos,Vector2(mov._x, mov._y));

	int x1, y1;	//マップ番号用
	//	マップ番号を求める
	x1 = (int)( tmp._x / BLOCK_SIZE );
	y1 = (int)( tmp._y / BLOCK_SIZE );
	
	if( x1 < MAP_WIDTH && x1 > 0 
		&& y1 < MAP_HEIGHT && y1 > 0){	//	配列参照外を起こしてなかったら
		//	マップの値を返す
		return map.MapData[y1][x1];
	}
	//	進行可能番号を返す
	return 255;
}

//	マップとのあたり判定(Y方向)
float HitManager::HIT_MAP_Y(Status &status, Map &map, int *data)
{
	Vector2 pos = status._pos;	// 測定のためのポジション設定(画像の真ん中)
	float top		= pos._y - status._radius + 1.0f;	// 上下左右の座標設定(画像の上の点)
	float bottom	= pos._y + status._radius - 1.0f;	// 上下左右の座標設定(画像の下の点)
	float left		= pos._x - status._radius + 16.0f;	// 上下左右の座標設定(画像の左の点)
	float right		= pos._x + status._radius - 16.0f;	// 上下左右の座標設定(画像の右の点)

	//---- 上下左右判定
	
	//--- 地面,天井
	Vector2 posl, posr, center;					//	左右真ん中
	posl._x = left;					//	左の点を入れる
	posr._x = right;				//	右の点を入れる
	center._x = status._pos._x;		//	真ん中の点を入れる

	if( status._dir._y > 0.0f ){	// 移動量の向きが正の値だったら
		//---- 地面
		//	下のマップ番号を調べるために下の点を入れる
		posl._y = bottom;
		posr._y = bottom;
		center._y = bottom;
	}else{							// 移動量の向きが負の値だったら
		//---- 天井
		//	上のマップ番号を調べるために上の点を入れる
		posl._y = top;
		posr._y = top;
		center._y = top;
	}

	int l,r,c;	//	左右真ん中
	//	マップ番号を取得
	l = Get_Distance_Val(posl,Vector2(0.0f,status._mov._y),map);
	r = Get_Distance_Val(posr,Vector2(0.0f,status._mov._y),map);
	c = Get_Distance_Val(center,Vector2(0.0f,status._mov._y),map);	

	//---- 今の状態を保存(別のことに使う)
	data[0] = l;	//	プレイヤーの左下の配列の値を入れる
	data[1] = r;	//	プレイヤーの右下の配列の値を入れる
	data[2] = c;	//	プレイヤーの真下の配列の値を入れる
	

	//	足元の点
	t_pos = Vector2(status._pos._x, status._pos._y + status._radius - 1.0f);
	//	足元の点のマップ番号を取得
	int p = Get_Distance_Val(t_pos,Vector2(0.0f, 0.0f ), map);
	if( p == 4 ){	//	斜め坂登りだったら
		//	次フレームの進む点を求める
		t_pos = Get_Dist_Pos(t_pos,Vector2(status._mov._x,0.0f));
		//	ブロックの左下の点を求める
		begin_p = Vector2(map.Get_MatrixPos(t_pos)._x - 2.0f, map.Get_MatrixPos(t_pos)._y + 32.0f + 2.0f);
		//	ブロックの右上の点を求める
		end_p	= Vector2(map.Get_MatrixPos(t_pos)._x + 32.0f - 2.0f, map.Get_MatrixPos(t_pos)._y - 2.0f);
		//	点と線とのあたり判定をする
		return CheckHitLine(t_pos, begin_p, end_p);	// めり込み量を返す
	}
	else if((( l == 19 || r == 19  || c == 19  )||( l == 0 || r == 0  || c == 0 ))
	&& ( l != 4 && r != 4  && c != 4 ))	// 調べたマップの値が19(ブロック番号)または0(地面番号)かつ全て4(坂)以外の時
	{	
		return 0.0f;	// めり込み量を返す
	}
	return -1.0f;		//	当たってない
	
}

//	マップとのあたり判定(X方向)
float HitManager::HIT_MAP_X(Status &status, Map &map)
{
	Vector2 pos = status._pos;	// 測定のためのポジション設定(画像の真ん中)
	float top		= pos._y - status._radius + 1.0f;	// 上下左右の座標設定(画像の上の点)
	float bottom	= pos._y + status._radius - 1.0f;	// 上下左右の座標設定(画像の下の点)
	float left		= pos._x - status._radius + 16.0f;	// 上下左右の座標設定(画像の左の点)
	float right		= pos._x + status._radius - 16.0f;	// 上下左右の座標設定(画像の右の点)

		Vector2 post, posb, center;		//	上下真ん中
		post._y = top;					//	上の点を入れる
		posb._y = bottom;				//	下の点を入れる
		center._y = status._pos._y;		//	真ん中の点を入れる

		if( status._dir._x > 0.0f ){			// 移動量の向きが正の値だったら
			//---- 右方向
			//	右のマップ番号を調べるために右の点を入れる
			post._x = right;
			posb._x = right;
			center._x = right;
		}else if( status._dir._x < 0.0f ){		// 移動量の向きが負の値だったら
			//---- 左方向
			//	左のマップ番号を調べるために左の点を入れる
			post._x = left;
			posb._x = left;
			center._x = left;
		}
		int t,b,c;	// 上下真ん中
		//	マップの番号を取得
		t = Get_Distance_Val(post,Vector2(status._mov._x,0.0f),map);
		b = Get_Distance_Val(posb,Vector2(status._mov._x,0.0f),map);
		c = Get_Distance_Val(center,Vector2(status._mov._x,0.0f),map);
		
		
		// あたり判定
		//	足元の点
		t_pos = Vector2(status._pos._x, status._pos._y + status._radius - 5.0f);
		//---- デバッグ用
		s_pos = Get_Dist_Pos(t_pos,Vector2(0.0f,0.0f));
		d_pos = map.Get_MatrixPos(t_pos);
		//	足元の点のマップ番号を取得
		int p = Get_Distance_Val(t_pos,Vector2(0.0f, 0.0f), map);
		if( p == 4 ){	//	斜め坂登りだったら
			//	次フレームの進む点を求める
			t_pos = Get_Dist_Pos(t_pos,Vector2(status._mov._x,0.0f));
			//	ブロックの左下の点を求める
			begin_p = Vector2(map.Get_MatrixPos(t_pos)._x - 2.0f, map.Get_MatrixPos(t_pos)._y + 32.0f + 2.0f);
			//	ブロックの右上の点を求める
			end_p	= Vector2(map.Get_MatrixPos(t_pos)._x + 32.0f - 2.0f, map.Get_MatrixPos(t_pos)._y - 2.0f);
			//	足元の点が坂の開始地点と終了地点の間なら
			if( t_pos._x >= begin_p._x && t_pos._x < end_p._x )
			{
				//	点と線とのあたり判定をする
				return CheckHitLine(t_pos, begin_p, end_p);	// めり込み量を返す
			}
			return -1.0f;
		}
		else if((t == 19 || b == 19 || c == 19) && (t != 4 && b != 4 && c != 4))	// 調べたマップの値が19(ブロック番号)かつ全て4(坂)以外の時
		{	
			//	めり込み量を返す
			return 0.0f;
		}
		
	return -1.0f;		//	当たってない
}

めり込み量によってその後の処理はこうなってます。
HitManager.cpp

コード:

int data[3];	//	データ一時保管用
	//--- 上下方向
	if( HIT_MAP_Y(ninja, map, data) == 0.0f ){
		ninja.hit_ground = true;
		ninja.set_underval(data);	//	データを入れる
	}else if( HIT_MAP_Y(ninja, map, data) > 0.0f ){
		ninja.hit_ground = true;
		float a = HIT_MAP_Y(ninja, map, data);	//	値確認用
		ninja._pos._y -= a;
		ninja.set_underval(data);	//	データを入れる
	}else{
		ninja.hit_ground = false;
		ninja.set_underval(data);	//	データを入れる
	}
	//---- 左右方向
	if( HIT_MAP_X(ninja, map) == 0.0f){
		ninja.hit_wall = true;
	}else if( HIT_MAP_X(ninja, map) > 0.0f ){
		float a = HIT_MAP_X(ninja, map);		//	値確認用
		ninja._pos._y -= a;
	}else{
		ninja.hit_wall = false;
	}
	d_nin =  ninja._pos;
ninja.hit_wallやninja.hit_groundののちの処理は以下のようになっています
Ninja.cpp

コード:

if( hit_wall == true ){		// 壁と当たったら
	_mov._x = 0;	//	移動量を0に
	SCROLL_SPEED_X = _mov._x;	//	スクロール量を入れる(後々変更予定)
}else{
	SCROLL_SPEED_X = _mov._x;	//	スクロール量を入れる(後々変更予定)
	_pos._x += _mov._x;	//	移動
}
if( hit_ground == true ){		//	地面と接触していたら
	_mov._y = 0;				// 重力を0にする
	status_mode = STATUS_RUN;
	hit_ground = false;		// 当たっているフラグをfalseにする
}else{
	//----重力設定
	_pos._y += _mov._y;		//	重力
}
このあたり判定を「移動後に現在の高さを調べて、めり込んでいたら補正」に変更するにはどの辺が肝なのでしょうか?
学校の先生に相談したときは調べる方向を斜めにするということだったのですが、その方法をしてもダメだったので、改めて質問させていただきました。
ソースコードで足りない部分がありましたらお願いします。

Re: 斜め坂のアルゴリズムについて

Posted: 2012年12月16日(日) 18:01
by ISLe
複雑なコードなので詳しくは見ていませんが、前の回答にも書いたとおり移動先を見るようにすると予想外の不具合にぶつかる可能性が高くなるのでお勧めしません。
足りないどころかもっとシンプルにできると思います。

進行不可ブロックの角が飛び出ているのなら引っ掛かるのが正解ですので、実際にピクセル単位で坂の増分をチェックしてみてください。
そうでなければ移動前と現在の座標の関係を見てX方向かY方向に適切に補正すれば引っ掛かることはなくなるはずです。

あと、坂を登っているとき、床との接点がキャラクターの中心とか内側にある場合は、横方向には地面にめり込みます。
なので坂の下のブロックは、通常の進行不可ブロックと見た目は同じでも、めり込んでも良い属性のブロックを配置する必要があります。



ウチのブログで、2Dゲームの床や壁の当たり判定をいろいろ試したサンプルプログラムを公開してます。
坂の処理はないですが、基本的な壁や床との当たり判定の参考になるかもしれません。
Javaで書いているのですが、Cしか知らなくても読めると思います。

Re: 斜め坂のアルゴリズムについて

Posted: 2012年12月21日(金) 11:45
by EKISUKE
ISLe さんが書きました:複雑なコードなので詳しくは見ていませんが、前の回答にも書いたとおり移動先を見るようにすると予想外の不具合にぶつかる可能性が高くなるのでお勧めしません。
足りないどころかもっとシンプルにできると思います。
なるほど、もっとシンプルにですね。やってみます。
ISLe さんが書きました: 進行不可ブロックの角が飛び出ているのなら引っ掛かるのが正解ですので、実際にピクセル単位で坂の増分をチェックしてみてください。
そうでなければ移動前と現在の座標の関係を見てX方向かY方向に適切に補正すれば引っ掛かることはなくなるはずです。


あと、坂を登っているとき、床との接点がキャラクターの中心とか内側にある場合は、横方向には地面にめり込みます。
なので坂の下のブロックは、通常の進行不可ブロックと見た目は同じでも、めり込んでも良い属性のブロックを配置する必要があります。


ウチのブログで、2Dゲームの床や壁の当たり判定をいろいろ試したサンプルプログラムを公開してます。
坂の処理はないですが、基本的な壁や床との当たり判定の参考になるかもしれません。
Javaで書いているのですが、Cしか知らなくても読めると思います。
参考にさせていただきます。
今まで、あたり判定を、進む方向の配列をしらべて、そこが進行不可なら移動しないというあたり判定でしたが、
プレイヤーの周囲のブロックをしらべて、そのブロックと四角同士のあたり判定に変えてやってみます。

Re: 斜め坂のアルゴリズムについて

Posted: 2012年12月27日(木) 14:33
by EKISUKE
四角同士の当たり判定をやってみてはいるのですが、なぜか、ブロックに横からあたると、そのブロックで止まるときと、そのブロックの上に移動してしまうときがあります。

今のやり方は、移動後に、Y方向のあたり判定と補正をしてからX方向のあたり判定と補正という形にしています。
あたり判定と補正の流れは以下の通りです。

まず、プレイヤーをマップチップ番号に直して、そのマップチップ番号から-2から+2の範囲のマップチップすべてをしらべ、
壁番号だったらあたり判定を行い、めり込んでいたらプレイヤーのポジションにめり込み量を足すという方法でやっています。

Y方向のあたり判定です。

コード:

//	マップとのあたり判定(Y方向)
float HitManager::HIT_MAP_Y(Status &status, Map &map, int *data)
{
	Vector2 pos = status._pos;	// 測定のためのポジション設定(画像の真ん中)
	Vector2 mov = Vector2(0.0f,status._mov._y);		//	Y方向のみ
	float top		= pos._y - status._radius + 1.0f;	// 上下左右の座標設定(画像の上の点)
	float bottom	= pos._y + status._radius - 1.0f;	// 上下左右の座標設定(画像の下の点)
	float left		= pos._x - status._radius + 16.0f;	// 上下左右の座標設定(画像の左の点)
	float right		= pos._x + status._radius - 16.0f;	// 上下左右の座標設定(画像の右の点)

	//---- デバッグ用
	d_top	 = top;
	d_bottom = bottom;
	d_left	 = left;
	d_right	 = right;

	//---- 上下判定
	
	//--- 地面,天井
	Vector2 block;	//	一時保存
	//block.add(pos,mov);
	block = pos;
	//	マップ番号に直す
	block = map.toMap(block);
	//	めり込み方向判定
	if(mov._y > 0.0f){			//	プラスなら
		//---- 地面
		//	周囲のブロックを調べる
		for( int y=block._y + 2; y>block._y - 2; y--){
			for( int x=block._x + 2; x>block._x - 2; x--){
				//---- 配列外かどうか
				if(x<0 || x>MAP_WIDTH)			continue;
				if(y<0 || y>MAP_HEIGHT)			continue;
				//---- 進行かのブロックかどうか
				if(map.MapData[y][x] == 255)	continue;
				Vector2 tmp;	//	一時保存
				tmp = Vector2(x,y);	//	ブロック番号を入れる
				tmp = map.toPos(tmp);	//	マップから座標に直す
				//---- ブロックの上下左右
				float b_top		= tmp._y;
				float b_bottom	= tmp._y + BLOCK_SIZE;
				float b_left	= tmp._x;
				float b_right	= tmp._x + BLOCK_SIZE;

				//	あたり判定
				if(left < b_right && right > b_left){
					if(top < b_bottom && bottom > b_top){
						//---- デバッグ用
						d_pos = tmp;
						//	当たってる
						//	めり込み量保存変数
						float sink;
						sink = b_top - bottom ;	//	地面
						//	めり込み量を返す
						return sink;			
					}
				}
			}
		}
	}else if ( mov._y < 0.0f){	//	マイナスなら
		//---- 天井
		//	周囲のブロックを調べる
		for( int y=block._y - 2; y<block._y + 2; y++){
			for( int x=block._x - 2; x<block._x + 2; x++){
				//---- 配列外かどうか
				if(x<0 || x>MAP_WIDTH)			continue;
				if(y<0 || y>MAP_HEIGHT)			continue;
				//---- 進行かのブロックかどうか
				if(map.MapData[y][x] == 255)	continue;
				Vector2 tmp;	//	一時保存
				tmp = Vector2(x,y);	//	ブロック番号を入れる
				tmp = map.toPos(tmp);	//	マップから座標に直す
				//---- ブロックの上下左右
				float b_top		= tmp._y;
				float b_bottom	= tmp._y + BLOCK_SIZE;
				float b_left	= tmp._x;
				float b_right	= tmp._x + BLOCK_SIZE;
				
				//	あたり判定
				if(left < b_right && right > b_left){
					if(top < b_bottom && bottom > b_top){
						//---- デバッグ用
						d_pos = tmp;
						//	当たってる
						//	めり込み量保存変数
						float sink;
						//	めり込み方向判定
						sink = b_bottom - top ;	//	天井
						//	めり込み量を返す
						return sink;
					}
				}
			}
		}
	}

	//	当たってない
	return 0.0f;
}
返す値はめり込み量でその値によって以下のような処理をしています。

コード:

Vector2	sink;
// 初期化
sink.clear();
//---- めり込み量計算
sink._y = HIT_MAP_Y(ninja, map, data);
//---- 上下方向
if( sink._y != 0.0f ){
	ninja.hit_ground = true;
	ninja._pos._y += sink._y;
}else{
	ninja.hit_ground = false;
}
この後にX方向もY方向と同様にしています。
周辺のブロックのしらべる方向を変えてみたり、XとYのあたり判定の順序を変えてみたり、としたのですが、
なかなか解決しないので、質問させていただきました。

追記
すいません。自己解決しました。
ISLeさんのブログに似たようなことがあり、よくよく考えたら、Y方向でも周囲全部をしらべているのがダメな原因だと思い、
調べる範囲を、今のプレイヤーのブロックから-2個のブロックだけを調べるようにしたらいけました・・・

コード:

for( int y=block._y + 2; y>block._y - 2; y--){
			for( int x=block._x + 2; x>block._x - 2; x--){
Y方向のあたり判定の周囲のブロックを調べているここを

コード:

for( int y=block._y + 2; y>block._y; y--){
			for( int x=block._x + 2; x>block._x - 2; x--){
こういう風にしたらいけました。

Re: 斜め坂のアルゴリズムについて

Posted: 2012年12月27日(木) 16:41
by ISLe
EKISUKE さんが書きました:追記
すいません。自己解決しました。
ISLeさんのブログに似たようなことがあり、よくよく考えたら、Y方向でも周囲全部をしらべているのがダメな原因だと思い、
調べる範囲を、今のプレイヤーのブロックから-2個のブロックだけを調べるようにしたらいけました・・・
それだと将来的に問題になる状況が出てくる可能性があるような気がしますけど。
当たってるのに当たっていないことにするというのは、問題があるのに見なかったことにしているということですから。

当たっているブロックは全部処理する前提で、移動前の座標との差分をとってX補正を優先するかY補正を優先するか分けるようにしたほうが良いと思います。
簡単なのは水平に(近い方向から)ぶつかったときはY補正をスキップするという方法です。
#わたしのブログのサンプルプログラムもそうしています。

それでもうまくいかないなら、プログラムがシンプルになるようにマップデータの構造を工夫することをお勧めします。

Re: 斜め坂のアルゴリズムについて

Posted: 2012年12月27日(木) 18:06
by EKISUKE
移動前との座標の差分というのは移動量でも可能でしょうか?
移動量でやってみたところ、Y方向の補正をスキップすると、X方向に進めなくなるようです。
おそらく、Y方向のをスキップすることによってめり込んだ状態でX方向のあたり判定があるためだと思うのですが、移動量ではなく、差分でないとだめでしょうか?
理由を教えていただけるとありがたいです。

Re: 斜め坂のアルゴリズムについて

Posted: 2012年12月28日(金) 00:55
by ISLe
移動量と座標の差分は同じものだと思いますけど。

想像ですけど、hit_groundフラグですか、地面に立ってるフラグだと思いますけど、想定外にクリアされてしまったりしてませんか?
補正のタイミングを見直してみると良いかもしれません。

Re: 斜め坂のアルゴリズムについて

Posted: 2012年12月28日(金) 13:40
by EKISUKE
hit_groundは想定外にクリアはされてはいないと思います。
hit_groundをfalseにしているのはジャンプしたときと、あたり判定で当たっていないと判定が出た時にしています。

ISLeさんのブログのサイドビューの実験のところを参考にしてやっているのですが、
あたり判定のfor文の中の「引っ張り上げられないための除外」は足元より上のブロックとのあたり判定は行わないということですか?
だとすると、for文での検索範囲を少なくしたのと変わらないのではないのでしょうか?

それと、左右のあたり判定は床の補正を行った後でないと、めり込んだ状態で左右のあたり判定を行おうとするので、ひっかっかってしまいます。
壁に当たった時に上に登ってしまうのは、キャラより上のブロックを調べて、当たっているという判定でY方向の補正をしてしまうためだと思います。
そのためのY補正スキップなのですが、スキップしてしまうと、X方向が・・・・ってなってしまいます。
X方向は足元の高さをY方向にくらべて1.0小さくしたりなどしましたが、相変わらずです・・・
補正のタイミングがだめなんですかね・・・
キャラが高さ64幅32に対してブロックが32x32なのが問題なんですかね

Re: 斜め坂のアルゴリズムについて

Posted: 2012年12月28日(金) 18:02
by ISLe
EKISUKE さんが書きました:hit_groundは想定外にクリアはされてはいないと思います。
hit_groundをfalseにしているのはジャンプしたときと、あたり判定で当たっていないと判定が出た時にしています。
地面に立っているときは、次のフレームでY補正がスキップされるはずです。
Y補正がスキップされたとき当たってないと判定されてしまうのではないですか?
EKISUKE さんが書きました:ISLeさんのブログのサイドビューの実験のところを参考にしてやっているのですが、
あたり判定のfor文の中の「引っ張り上げられないための除外」は足元より上のブロックとのあたり判定は行わないということですか?
だとすると、for文での検索範囲を少なくしたのと変わらないのではないのでしょうか?
あれは、以前から書いている、移動量(座標差分)を元にY補正をスキップするという処理そのものですが。
当たり判定自体はすべてのブロックに対して行なってますよ。
EKISUKE さんが書きました:それと、左右のあたり判定は床の補正を行った後でないと、めり込んだ状態で左右のあたり判定を行おうとするので、ひっかっかってしまいます。
壁に当たった時に上に登ってしまうのは、キャラより上のブロックを調べて、当たっているという判定でY方向の補正をしてしまうためだと思います。
そのためのY補正スキップなのですが、スキップしてしまうと、X方向が・・・・ってなってしまいます。
X方向は足元の高さをY方向にくらべて1.0小さくしたりなどしましたが、相変わらずです・・・
補正のタイミングがだめなんですかね・・・
キャラが高さ64幅32に対してブロックが32x32なのが問題なんですかね
Y補正をスキップするのはY補正が結果的に不要になる場合です。
X補正を行うことでY方向のめり込みが無くならないといけません。

X補正をしてもY方向にめり込んだまま、というのがそもそもおかしいです。
その場合はY補正が行われなければいけないのです。
どこか間違っています。

ちなみにわたしのサンプルプログラムでもキャラのサイズはブロックより縦長です。


(重なっているではなく)接触している状態を維持する方法は、ゲームプログラムで特に難しい問題です。
内部的には、異なるレイヤーで動いているキャラクタを重ねて見ているだけですから、何を以て接触していると定義するかという話になります。
おそらく、補正と絡めた上で、地面に立っている状態をどうやって維持するかが現在直面している問題のキモではないでしょうかね。

Re: 斜め坂のアルゴリズムについて

Posted: 2012年12月28日(金) 21:18
by EKISUKE
ISLe さんが書きました: あれは、以前から書いている、移動量(座標差分)を元にY補正をスキップするという処理そのものですが。
当たり判定自体はすべてのブロックに対して行なってますよ。

Y補正をスキップするのはY補正が結果的に不要になる場合です。
X補正を行うことでY方向のめり込みが無くならないといけません。

X補正をしてもY方向にめり込んだまま、というのがそもそもおかしいです。
その場合はY補正が行われなければいけないのです。
どこか間違っています。

ちなみにわたしのサンプルプログラムでもキャラのサイズはブロックより縦長です。

(重なっているではなく)接触している状態を維持する方法は、ゲームプログラムで特に難しい問題です。
内部的には、異なるレイヤーで動いているキャラクタを重ねて見ているだけですから、何を以て接触していると定義するかという話になります。
おそらく、補正と絡めた上で、地面に立っている状態をどうやって維持するかが現在直面している問題のキモではないでしょうかね。
スキップのことを勘違いしていました。
ISLe さんが書きました:簡単なのは水平に(近い方向から)ぶつかったときはY補正をスキップするという方法です
この意味をX方向の移動量がY方向より多ければY方向の処理をスキップするのだと思っていました。
それと、僕のプログラムでは地面に接触している状態でも、重力は常にかけている状態なので、Yをスキップするとめり込んだ状態になってしまうようです。

重力をhit_groundがfalseの時のみ増減するようにし、y方向の移動量が0の時はY補正をスキップするという形で上にのぼるのは解決できました。
しかし、重力というのは常にかかっているものでなければいけないのではないですか?
常に重力はかかっているものということとしてやってきていたので疑問に思いました。

Re: 斜め坂のアルゴリズムについて

Posted: 2012年12月29日(土) 01:15
by ISLe
EKISUKE さんが書きました:この意味をX方向の移動量がY方向より多ければY方向の処理をスキップするのだと思っていました。
あの辺は先の記事に進むとどんどん変化してますからね。
大筋では、ブロックを横に伸ばして重ならない位置からめり込んだときはY補正する、という仕様です。
EKISUKE さんが書きました:重力をhit_groundがfalseの時のみ増減するようにし、y方向の移動量が0の時はY補正をスキップするという形で上にのぼるのは解決できました。
しかし、重力というのは常にかかっているものでなければいけないのではないですか?
常に重力はかかっているものということとしてやってきていたので疑問に思いました。
地面に立っているときは、地面から重力と逆方向に同じ大きさの力が働いてバランスを取ります。
なので、地面に立っているときは、重力を加えると同時に、重力と逆方向に同じ大きさの力を加えなければいけません。
結果的にはプラマイゼロなので、プログラムとしては、地面に立っているときは重力を加えない(&加速度もリセット)、ということにするのが簡単です。

物理エンジン使ったプログラムでオブジェクトが密集すると爆発する現象がありますが、常に重力を加えるとああいうことが起こります。
それを避けるにはそういうことが起きないようにマップをデザインする必要があります。
#あるいはそういうものだと割り切る。
要は単純なマップにしか使えないということです。

接触判定を考えておくとマップにいろんな仕掛けを入れる際に役に立つと思います。

Re: 斜め坂のアルゴリズムについて

Posted: 2013年1月21日(月) 18:57
by EKISUKE
返信おくれてすみません。
坂のプログラムを作る時間がなく、当分このスレッドに返信できませんでした。
これからも、少しの間返信できないことが続くと思うので、勝手ながら一旦解決とさせていただきます。

あたり判定のアルゴリズムの考え方教えていただきありがとうございました。
ISLeさんのブログを見ながら、ほかのことについても勉強させていただきます。

また坂のプログラムに触れることがありましたら、お願いします。

本当に勝手で申し訳ございません。