|
|
|
2014/7/9(Wed) 20:05:33|NO.62793
こんにちは。
今作っているゲームで矩形と点との接触判定が必要になり作ったのですが、
作りたかった挙動である、「斜めの部分に衝突している時に滑らせる」というものが作れず困っています。
アクションゲームの坂道や3Dゲームの壁のような挙動を目指しています。
よろしくお願いします。
以下作ってみた接触判定
#module
//接触判定
#defcfunc objcol double wid, double hei, double ang, double cx, double cy, double px, double py
hit = 0
dist = sqrt(powf(cx-px,2)+powf(cy-py,2))
angle = atan(cy-py,cx-px) - ang
fixpx = cx+cos(angle)*dist
fixpy = cy+sin(angle)*dist
if (fixpx > cx - wid/2) & (fixpy > cy - hei/2) & (fixpx < cx + wid/2) & (fixpy < cy + hei/2){
hit = 1
}
return hit
#global
randomize
buffer 1
color 0,0,255 : boxf
gsel 0
x = 100.0
y = 100.0
a = deg2rad(rnd(360)
*main
color 0,0,0 : boxf
//移動
movex = 0.0
movey = 0.0
stick key,15
if key & 1 : movex -= 4.0
if key & 2 : movey -= 4.0
if key & 4 : movex += 4.0
if key & 8 : movey += 4.0
x += movex
y += movey
//当たり判定
color 255,255,255
if objcol(150.0,100.0,a,320.0,240.0,x,y){
x -= movex
y -= movey
pos 0,0 : mes "hit"
}
//四角形を表示
gmode 0,150,100
pos 320,240
grotate 1,0,0,a
//自分を表示
color 255,0,0
pos x-8.0,y-8.0
mes "●"
redraw 1
redraw 0
await 16
frame++
goto *main
| |
|
2014/7/9(Wed) 21:32:34|NO.62795
まず点と矩形のサイズ、傾きだけでは出来ない
どの辺に当たったかを判定する必要がある
なので判定命令に移動前の点の座標(移動速度でも良いが)を渡す必要がある
移動速度と移動前の座標を変数で渡せば判定後の座標を
そのまま移動前の座標変数に代入すれば良いから
扱いやすいかな・・・
|
|
2014/7/10(Thu) 00:40:14|NO.62798
あー,難しいですよね衝突判定
回転する矩形(OBB)を衝突に組み込むのは若干敷居が高いとこなんですが,質問者さんはヒットチェックまでは既に理解してらっしゃるようで…
一応,基本的にどんなゲームになるにせよ通常物理的な挙動はゲームの都合に合わせてカスタマイズする必要があるので,今回の私の回答は参考程度に捉えてください
☆質問者さんの現在の実装:私の認識
点を移動させた時,移動させた後の座標が矩形内に存在した場合,前いた座標へ戻す(移動量を0にしたことと等価)
★今回私が実装した内容
点を移動させた後点が矩形内に存在した場合,「矩形外で最も近い所」へ押し戻す
「矩形外で最も近い所」というのは,めり込んでる点に最も近い辺の方へ,その辺に垂直に押し戻すことを意味します
(正直図なしですっと理解できるように説明するの難しいので,ちょっと訳分からなかったらコード見たほうがいいかもしれません)
#module
//接触判定&押し戻しベクトル計算
// 引数(左から順番に):幅,高さ,角度,矩形中心(X,Y),点中心(X,Y),押し戻しベクトル(X,y)
#defcfunc objcol double wid, double hei, double ang, double cx, double cy, double px, double py, var pbx, var pby
pbx =0.0 : pby =0.0
// 矩形から見た点の位置ベクトル
dx = px -cx : dy = py -cy
// 矩形がAABBになるような空間に写像する:-ang度回転する
/*
質問者さんは角度を用いた変換をしていますが,
ここではどうせなのでベクトルを用いた計算でやります
あとatan一回よりcos,sin一回ずつのが実は早い気がした,というのもあります
*/
c =cos( ang ) : s =sin( ang )
fixpx = c*dx +s*dy// この辺は適当な行列計算
fixpy = -s*dx +c*dy
// あたっているか?
afixpx = absf( fixpx ) : afixpy = absf( fixpy )
if ( (afixpx>=wid/2.0) || (afixpy>=hei/2.0) ) {
return 0// 当たっていないならさようなら
}
// X軸に押し戻した場合と,Y軸に押し戻した場合の押し戻し量を計算
pushback_x = wid/2.0 -afixpx : pushback_y = hei/2.0 -afixpy
// 押し戻す方向を決定する:押し戻し量が小さい方を採用する
if ( pushback_x < pushback_y ) {
pushback_y =0.0// X軸を採用したいので,Y軸は押し戻し量0にする
} else {
pushback_x =0.0
}
// 押し戻しベクトルの符号を合わせる
if ( fixpx < 0 ) : pushback_x = -pushback_x
if ( fixpy < 0 ) : pushback_y = -pushback_y
// 元の空間に写像し直す:ang度回転する
pbx = c*pushback_x -s*pushback_y
pby = s*pushback_x +c*pushback_y
return 1
#global
randomize
buffer 1
color 0,0,255 : boxf
gsel 0
x = 100.0
y = 100.0
a = deg2rad(rnd(360)
*main
color 0,0,0 : boxf
//移動
movex = 0.0
movey = 0.0
stick key,15
if key & 1 : movex -= 4.0
if key & 2 : movey -= 4.0
if key & 4 : movex += 4.0
if key & 8 : movey += 4.0
x += movex
y += movey
//当たり判定
color 255,255,255
pbx =0.0 : pby =0.0
if objcol(150.0,100.0,a,320.0,240.0,x,y,pbx,pby){
x += pbx
y += pby
pos 0,0 : mes "hit"
}
//四角形を表示
gmode 0,150,100
pos 320,240
grotate 1,0,0,a
//自分を表示
color 255,0,0
pos x-8.0,y-8.0
mes "●"
redraw 1
redraw 0
await 16
frame++
goto *main
□この方法の良いところ
・接触していた場合,点の位置を常にオブジェクトとの接触面ピッタリに戻せる
・特になんの苦労もせず斜面に沿って移動させることができる(逆にこの動作を望まない場合シンドい)
■この方法のイケてないところ
・複数個のオブジェクトと同時に接触していた場合,オブジェクトの衝突判定処理順に動作が依存する
・オブジェクトの移動速度がめちゃくちゃ早い場合,衝突判定がとれてても突き抜けることがある(反対側へ押し戻した方が押し戻し量が少なくなるため)
・1フレームでオブジェクト外へ押し戻されるが,速度に影響はないため不自然に感じる場合がある
まあイケてないところに対する処理はテンプレみたいな手法があったり,押し戻しベクトル見て処理できるところとかあるので,そこまで突き詰めたい場合は調べてみるなりするといいと思います
*その他個人的に思うところ
わたし的には衝突判定に速度ベクトル渡しておいた方が,後々変な処理加える時もそこそこ柔軟に対応できるのでオススメです(まあこれはその時になったら修正でもいいんですが)
あと今回傾いた矩形(OBB)ということで,傾いてはいるが4つの角全てが直角な四角形を対象としていましたが,考え方を応用すれば菱型とかも割と簡単に対応できたりしますよ,検討しているなら作ってみると面白いかもしれません
あるいは,点ではなく円とOBBとかもそんなに難しくないので,色んな形の衝突判定やりたかったらその辺から手を出してみるのがオススメです(楕円とかは若干シンドくなります)
そのような感じ,長文失礼
| |
|
2014/7/10(Thu) 16:49:39|NO.62803
>暇人さん
やっぱり辺が云々ってなっちゃいますよね…
調べたら辺について出てきたのですがよく分からずスルーしちゃってました。
>3kさん
まさしく目指していた挙動です!
実を言うとベクトルがよくわかっていないので角度を戻す感じで計算してました。
このソースを見つつベクトルについてもう少し学習してみようと思います。
お二方回答どうもありがとうございました!
|
|
2014/7/11(Fri) 00:29:05|NO.62818
もう解決してるけど接触辺の方向に押し出す命令を作ってみた
点の移動と、矩形が回転してる場合に対応
矩形自体が移動やサイズが変わって点に当たった場合は
NO.62798の処理と同じような処理で妥協(矩形の移動速度やサイズ変更の情報も命令に与えないといけなくなるので・・・)
#module
//接触判定
//変数 = objcol( wid, hei, ang, ang2, cx, cy, px, py, vx, vy )
// wid = 矩形の幅
// hei = 矩形の高さ
// ang = 矩形の角度
// ang2 = 今回傾けた角度
// cx = 矩形の中心X座標
// cy = 矩形の中心Y座標
// px = 点のX座標(入出力)必ず実数型変数を使用の事
// py = 点のY座標(入出力)必ず実数型変数を使用の事
// vx = 今回の点の移動速度X
// vy = 今回の点の移動速度Y
//接触が無ければstatに0が返る
//statが1の場合横辺に接触して上下どちらかに押し出された 、2の場合縦辺に接触して左右どちらかに押し出された
//statが3の場合横辺に浅くめり込んだので上下どちらかに押し出された、4の場合縦辺に浅くめり込んだので左右どちらかに押し出された
//上下左右と言うのは矩形が回転してない状態から見て
#defcfunc objcol double wid, double hei, double ang,double ang2, double cx, double cy, var px, var py, double vx, double vy
//矩形の傾きに合わせて点を回転
difx=cx-px
dify=cy-py
fixpx = cos(ang)*difx+sin(ang)*dify
fixpy = -sin(ang)*difx+cos(ang)*dify
if absf(fixpx)<wid/2.0 {
if absf(fixpy)<hei/2.0 {
//前回の傾きと今回の移動量から矩形に対しての前回の位置を算出
c=cos(ang-ang2)
s=sin(ang-ang2)
fixpx2=(c*(difx+vx) +s*(dify+vy))
fixpy2=(-s*(difx+vx) +c*(dify+vy))
if absf(fixpx2)<wid/2.0 and absf(fixpy2)<hei/2.0 {//移動前の座標が今回の矩形の中に入ってる
//めり込みが少ない方に押し出す
dvx=absf(fixpx)-wid/2.0 //回転させた点の位置と判定範囲の差分算出(マイナスなら範囲内、これが押出量になる)
dvy=absf(fixpy)-hei/2.0
if dvx>dvy {//縦辺側に押し出す
if fixpx>0.0 {dvx-0.00001}else{dvx*-1.0-0.00001}//押し出す方向決定
px-dvx*sin(-ang-M_PI/2.0)
py-dvx*cos(-ang-M_PI/2.0)
return 4
}else{
if fixpy>0.0 {dvy-0.00001}else{dvy*-1.0-0.00001}
px-dvy*cos(ang-M_PI/2.0)
py-dvy*sin(ang-M_PI/2.0)
return 3
}
}else{//移動方向と座標から接触する辺方向に押出
//前回と今回の回転後の座標から移動速度を算出
vxx = fixpx-fixpx2
vyy = fixpy-fixpy2
//移動速度を移動方向として接触する可能性がある辺を判定し押出方向と量を決定
if vxx<0.0 {dx=vyy*(wid/2.0-(fixpx+vxx)):dvx=fixpx-wid/2.0-0.00001}else{dx=vyy*(-wid/2.0-(fixpx+vxx)):dvx=fixpx+wid/2.0+0.00001}//-0.00001してるのは回転させると誤差が出て場所によっては判定外に押し出せないので補正
if vyy<0.0 {dy=vxx*(hei/2.0-(fixpy+vyy)):dvy=fixpy-hei/2.0-0.00001}else{dy=vxx*(-hei/2.0-(fixpy+vyy)):dvy=fixpy+hei/2.0+0.00001}
//横辺か縦辺のどちらから接触するか判定(dxの方が大きければ横辺に接触したとして処理)
if absf(dx)<absf(dy) {//縦辺に接触
px-dvx*sin(-ang-M_PI/2.0)
py-dvx*cos(-ang-M_PI/2.0)
return 2
}else{//横辺に接触
px-dvy*cos(ang-M_PI/2.0)
py-dvy*sin(ang-M_PI/2.0)
return 1
}
}
}
}
return 0
#global
randomize
buffer 1
color 0,0,255 : boxf
gsel 0
x = 100.0
y = 100.0
a = deg2rad(rnd(360))
aa=0.0
addang=0.01
*main
color 0,0,0 : boxf
//移動
movex = 0.0
movey = 0.0
stick key,15
if key & 1 : movex -= 4.0
if key & 2 : movey -= 4.0
if key & 4 : movex += 4.0
if key & 8 : movey += 4.0
x += movex
y += movey
//当たり判定
color 255,255,255
if objcol(150.0,100.0,a,0.0,320.0,240.0,x,y,movex,movey){
pos 0,0 : mes "hit "+stat
}
aa+addang
if objcol(100.0,50.0,aa,addang,100.0,300.0,x,y,movex,movey){
pos 0,32 : mes "hit "+stat
}
//四角形を表示
gmode 0,150,100
pos 320,240
grotate 1,0,0,a
//四角形を表示
gmode 0,100,50
pos 100,300
grotate 1,0,0,aa
//自分を表示
color 255,0,0
pos x-8.0,y-8.0
mes "●"
redraw 1
redraw 0
await 16
frame++
goto *main
NO.62798との違いとしては、点が矩形に当たればすり抜ける方向には押し出さない(矩形自体が移動してなければ・・・)
NO.62798の方法は上から下に垂直に当たったとしても
めり込みがx4,y5だった場合横に押し出してしまうのが一番の弱点だと思う
垂直ジャンプしても着地したら外に押し出されるみたいな・・・
| |
|
2014/7/11(Fri) 01:24:07|NO.62819
>暇人さん
これだけでも面白いゲームが作れそう・・・
|
|