HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2013
1120
初心者アクションゲームのブロックとの衝突判定について8解決


初心者

リンク

2013/11/20(Wed) 23:28:27|NO.58323

いわゆるマリオのようなアクションゲームを作ろうとしている者です。
変数のマップチップでブロックを作って、そのブロックとの衝突判定を作ろうとしたのですが

・右にベクトルがあって主人公の右下か右上がブロックにめり込んでいたら押し出す
・下にベクトルがあって主人公の右下か左下がブロックにめり込んでいたら押し出し着地

この2つの処理を作ってみたところ
右の壁に衝突する判定を先に行ってしまうと、右に移動中に着地した時に一瞬止まってしまい、
着地する判定を先に行ってしまうと、右に移動中に壁にぶつかると壁に着地してしまうんです。

ひょっとしたら初歩的なことなのかもしれませんが、ここ数日これのせいで先に進まなくて・・・
分かる方がいらっしゃいましたら、どうかよろしくお願いします。



この記事に返信する


きのよー

リンク

2013/11/20(Wed) 23:42:15|NO.58325

下にブロックがある場合は、キャラクターの座標を床の上に指定しなおせばいいのでは?
x座標はそのままで。
で、右の壁にぶつかった時は、ベクトルを反転させるか壁の手前にキャラクターの座標を指定し直すか。
y座標はそのままで。



初心者

リンク

2013/11/20(Wed) 23:48:48|NO.58327

>>きのよーさん

返信ありがとうございます。

一応そのように作ってみたつもりなのですが、私は多分
「右の壁にぶつかった時」と「着地した時」の判定の仕方が分かってないんだと思います。

「キャラクターの右下か右上がめり込んでいる+右にベクトルがかかっている」
という条件で右の壁への衝突判定を行っているのですが、それだと
「右に移動しながら着地」した場合でも「右下がめり込みつつ右にベクトルがかかっている」
状態であるので、着地と同時に右に衝突してしまうんです。

この2つを区別する良い条件があればいいんですが・・・



掘木

リンク

2013/11/21(Thu) 02:09:22|NO.58330

斜め方向への移動や判定の速度にこだわりがなければ、
横と縦の衝突判定は別々に行ってはどうでしょう。

1:x方向の移動を行う
2:x方向の移動で壁に埋まった場合xを補正(衝突)
3:y方向の移動を行う
4:y方向の移動で壁に埋まった場合yを補正(衝突)



123

リンク

2013/11/21(Thu) 07:14:36|NO.58332

よくわかんないけど、マリオのジャンプでブロックを押すときの動作で考えると、
ジャンプして上に上がってるときはブロックを押すけど、
ジャンプして下に下がってるときはブロックを押さない。
こういう事?



(’’

リンク

2013/11/21(Thu) 16:47:32|NO.58338

自分はこういう感じでやっています(長いので下判定だけ)
/*マップ判定を取得*/
vxs=x+vx+vvx:vys=y+vy+vvy mD = (getPixel(x,my_floor(vys)+boxh) & fBlock)!=0 | (getPixel(x+boxw,my_floor(vys)+boxh) & fBlock)!=0 /*マップでソート処理*/ fi=(my_floor(vys+boxh)/tilesize)*tilesize -boxh -1 if(mU=0 & mD=1 && y<fi) : y=fi /*判定による加速停止処理*/ if(mU|mD){vy=0:vvy=0} x+=vx:x+=vvx:y+=vy:y+=vvy //速度加算 vvx=0 : vvy=0

x,yは座標、vx,vy,vvx,vvyは速度、tilesizeはマップタイルのサイズ、boxw,hはx,yからの判定の広さ
getPixelは座標からマップのタイルを取得する命令です
my_floorは小数点切り上げ命令です。

こういう感じに作れば正確な処理ができるはず……

それと、fblockはブロックかブロックでないかのフラグです。



さか

リンク

2013/11/21(Thu) 22:18:14|NO.58350

「キャラクターの右下か右上がめり込んでいる+右にベクトルがかかっている」
と言うような条件を以下のようにANDで2箇所(3箇所)を見て判断したらどうでしょうか?

if 「右にベクトルがかかっている」{
 if キャラクターの右下がめりこんでる」AND「右上がめり込んでいる」{
  //右壁にめり込んでいる
  左へ押し出す
 }
 if キャラクターの右下がめりこんでる」AND「左下がめり込んでいる」{
  //下壁にめり込んでいる
  上へ押し出す
}
 // 必要であれば
 if キャラクターの右下がめりこんでる」AND「右上がめり込んでいる」AND「左下がめり込んでいる」{
  //右下壁にめり込んでいる
  左上へ押し出す
}
}



暇人

リンク

2013/11/22(Fri) 01:23:06|NO.58369

左下、右下両方当たってるなら上に戻す
右上、右下両方当たってるなら左に戻す

これだと角一つだけ当たってる時に上手く行かないので
移動前の座標も使う

右下が当たってたらブロックの上か左辺が当たったとして処理
移動速度が縦か横どちらか0なら速度のある方を移動

斜めに移動してたら移動前の座標から直線移動では絶対当たらない
方向を除外して移動

ブロックの斜め方向から当たった場合の処理は手抜きなので
作ってるものによっては気になるかも知れない・・・

#define CHIPSIZE 64 #define MAPMAXX 10 #define MAPMAXY 8 mysizex=CHIPSIZE //自分のサイズ mysizey=CHIPSIZE mycsx=mysizex-1 //自分の左上を基点にして右下端の判定をする時キャラサイズをそのまま使うと隣のブロックを最小してしまうので-1(キャラがウィンドウ座標0にいてキャラ右端を64としたら64で割ると1になってしまう) mycsy=mysizey-1 myspx=4 //自分の速度 myspy=4 //4が障害物、100以上が自分 map_txt={" 1,4,4, 1,1,1,1,1,1,1 1,4,1, 1,1,1,1,4,1,1 2,4,2, 2,2,2,2,4,2,2 2,2,2, 2,4,4,4,2,4,4 2,2,2, 2,2,2,4,2,2,4 3,3,3,103,3,3,3,3,3,4 4,3,3, 4,4,4,3,3,3,4 4,4,3, 3,3,3,3,4,4,4"} dim map,MAPMAXX,MAPMAXY+1 buffer 1 //テキストデータをそのまま判定にも使うと面倒(個人的にだが)なので配列に入れなおす。ついでにbufferに背景を作成 split map_txt,"\n",map_y_txt repeat MAPMAXY split map_y_txt(cnt),",",map_x_txt mapy=cnt repeat MAPMAXX map(cnt,mapy)=int(map_x_txt(cnt)) if map(cnt,mapy)=4 {//障害物 gradf cnt*CHIPSIZE,mapy*CHIPSIZE,CHIPSIZE,CHIPSIZE,((cnt+mapy)\2),$444444,$888888 }else{//障害物以外 if map(cnt,mapy)>=100 {//自分 mypx=cnt*CHIPSIZE:mypy=mapy*CHIPSIZE map(cnt,mapy)=map(cnt,mapy)\100 //座標をセットしたらクリア(100未満を背景マップチップとして指定できる) } gradf cnt*CHIPSIZE,mapy*CHIPSIZE,CHIPSIZE-1,CHIPSIZE-1,1,$880077-$220000*map(cnt,mapy),$880055-$220000*map(cnt,mapy) } loop loop gsel 0 repeat redraw 0 pos 0,0 celput 1,0 stick stk,$F //移動量を求めるための準備(判定後の処理に必要になってくる) mypx_old=mypx mypy_old=mypy if stk&1 {mypx-myspx} if stk&4 {mypx+myspx} if stk&2 {mypy-myspy} if stk&8 {mypy+myspy} //マップデータ外に出さない処理 mypx=limit(mypx,0,CHIPSIZE*MAPMAXX-mysizex) mypy=limit(mypy,0,CHIPSIZE*MAPMAXY-mysizey) //自分の左上のグリッド座標(マップ左上が0,0で右下最大がMAPMAXX-1,MAPMAXY-1 、0からなので使用チップ数-1になる) myg_lx=mypx/CHIPSIZE myg_uy=mypy/CHIPSIZE //自分の右下のグリッド座標(myg_lx,myg_dyで左下、myg_rx,myg_uyが右上になる) myg_rx=(mypx+mycsx)/CHIPSIZE myg_dy=(mypy+mycsy)/CHIPSIZE color 255,255,255 pos 0,0 if map(myg_lx,myg_uy)=4 {mes strf("X=%d,Y=%d",myg_lx,myg_uy):gradf myg_lx*CHIPSIZE,myg_uy*CHIPSIZE,CHIPSIZE,CHIPSIZE,1,$aa4444,$aa8888}//左上 if map(myg_rx,myg_uy)=4 {mes strf("X=%d,Y=%d",myg_rx,myg_uy):gradf myg_rx*CHIPSIZE,myg_uy*CHIPSIZE,CHIPSIZE,CHIPSIZE,1,$aa4444,$aa8888}//右上 if map(myg_lx,myg_dy)=4 {mes strf("X=%d,Y=%d",myg_lx,myg_dy):gradf myg_lx*CHIPSIZE,myg_dy*CHIPSIZE,CHIPSIZE,CHIPSIZE,1,$aa4444,$aa8888}//左下 if map(myg_rx,myg_dy)=4 {mes strf("X=%d,Y=%d",myg_rx,myg_dy):gradf myg_rx*CHIPSIZE,myg_dy*CHIPSIZE,CHIPSIZE,CHIPSIZE,1,$aa4444,$aa8888}//右下 //二つ以上の角が当たってるか判定 rlud=0 if map(myg_lx,myg_uy)=4 {rlud | 3}//左上が当たってたら左1上2で3を設定(stickと同じ) if map(myg_rx,myg_uy)=4 {rlud | 6} if map(myg_rx,myg_dy)=4 {rlud | 12} if map(myg_lx,myg_dy)=4 {rlud | 9} if rlud {//どこかが当たってる if (rlud&5)=5 {//自分の左右が当たってる if (mypy-mypy_old)>0 {//下に移動中 mypy=myg_dy*CHIPSIZE-mysizey //ブロックの上に戻す }else{//上に移動中 mypy=myg_uy*CHIPSIZE+CHIPSIZE //ブロックの下に戻す } mypy_old=mypy //縦方向の移動は無かった事にする myg_uy=mypy/CHIPSIZE //移動したので再度グリッド座標算出 myg_dy=(mypy+mycsy)/CHIPSIZE }else{ if (rlud&10)=10 {//自分の上下が当たってる if (mypx-mypx_old)>0 {//右に移動中 mypx=myg_rx*CHIPSIZE-mysizex //ブロックの左に戻す }else{//左に移動中 mypx=myg_lx*CHIPSIZE+CHIPSIZE //ブロックの右に戻す } mypx_old=mypx myg_lx=mypx/CHIPSIZE myg_rx=(mypx+mycsx)/CHIPSIZE } } //上の処理してもまだ当たってる場合の処理 //移動速度(上で判定後の移動処理してからの速度となる) spdx=mypx-mypx_old spdy=mypy-mypy_old spd_rlud=(spdx<0) | (spdx>0)*4 | (spdy<0)*2 | (spdy>0)*8 //左に移動なら1、上に移動なら2を設定(stickと同じ) if spd_rlud {//移動中 if spdx=0 or spdy=0 {//縦か横に移動中 if spd_rlud=1{//左 if map(myg_lx,myg_uy)=4 or map(myg_lx,myg_dy)=4{mypx=myg_lx*CHIPSIZE+CHIPSIZE} } if spd_rlud=2 {//上 if map(myg_lx,myg_uy)=4 or map(myg_rx,myg_uy)=4{mypy=myg_uy*CHIPSIZE+CHIPSIZE} } if spd_rlud=4 {//右 if map(myg_rx,myg_uy)=4 or map(myg_rx,myg_dy)=4{mypx=myg_rx*CHIPSIZE-mysizex} } if spd_rlud=8 {//下 if map(myg_lx,myg_dy)=4 or map(myg_rx,myg_dy)=4{mypy=myg_dy*CHIPSIZE-mysizey} } }else{//斜めに移動中 if spd_rlud=3 {//左上に移動中 if map(myg_lx,myg_uy)=4 {//左上が当たった if myg_uy = (mypy_old/CHIPSIZE) {//前回のグリッド位置が今回当たったブロックと同じY位置だから側面しか当たらないとして処理 mypx=myg_lx*CHIPSIZE+CHIPSIZE //ブロックの右に移動 }else{//上で処理されなかったもの全てブロックの下に当たったとして処理(手抜き) mypy=myg_uy*CHIPSIZE+CHIPSIZE //ブロックの下に移動 } }else{//右上と左下の判定 if map(myg_rx,myg_uy)=4 {//右上が当たった mypy=myg_uy*CHIPSIZE+CHIPSIZE }else{ if map(myg_lx,myg_dy)=4 {mypx=myg_lx*CHIPSIZE+CHIPSIZE}//左下が当たった } } } if spd_rlud=6 {//右上に移動中 if map(myg_rx,myg_uy)=4 { if myg_uy = (mypy_old/CHIPSIZE) { mypx=myg_rx*CHIPSIZE-mysizex }else{ mypy=myg_uy*CHIPSIZE+CHIPSIZE } }else{ if map(myg_lx,myg_uy)=4 {mypy=myg_uy*CHIPSIZE+CHIPSIZE}else{ if map(myg_rx,myg_dy)=4 {mypx=myg_rx*CHIPSIZE-mysizex} } } } if spd_rlud=12 {//右下に移動中 if map(myg_rx,myg_dy)=4 { if myg_dy = ((mypy_old+mycsy)/CHIPSIZE) { mypx=myg_rx*CHIPSIZE-mysizex }else{ mypy=myg_dy*CHIPSIZE-mysizey } }else{ if map(myg_lx,myg_dy)=4 {mypy=myg_dy*CHIPSIZE-mysizey}else{ if map(myg_rx,myg_uy)=4 {mypx=myg_rx*CHIPSIZE-mysizex} } } } if spd_rlud=9 {//左下に移動中 if map(myg_lx,myg_dy)=4 { if myg_dy = ((mypy_old+mycsy)/CHIPSIZE) { mypx=myg_lx*CHIPSIZE+CHIPSIZE }else{ mypy=myg_dy*CHIPSIZE-mysizey } }else{ if map(myg_rx,myg_dy)=4 {mypy=myg_dy*CHIPSIZE-mysizey}else{ if map(myg_lx,myg_uy)=4 {mypx=myg_lx*CHIPSIZE+CHIPSIZE} } } } } } } gradf mypx,mypy,mysizex,mysizey,1,$55ddee,$55ddee //自分の直ぐ下にブロックがあるか判定 if map(mypx/CHIPSIZE,(mypy+mysizey)/CHIPSIZE)=4 or map((mypx+mycsx)/CHIPSIZE,(mypy+mysizey)/CHIPSIZE)=4 {mes "着地"}//mycsyじゃ無くmysizeyを使って足元下の座標にする redraw 1 await 16 loop

map(,)の判定回数は最大で7回
足元の判定含めて最大9回



初心者

リンク

2013/11/22(Fri) 20:18:19|NO.58377

ちょっと忙しくて返信が遅れました、申し訳ありません。

主に掘木さんのアドバイスで分かった気がします。
横方向と縦方向のベクトルを座標に反映する際、同時に行うのではなく、

横方向に移動→横のブロックとの当たり判定
縦方向に移動→縦のブロックとの当たり判定

のように組んでみたら、今のところ正常に動いてくれたようです。
(まだ右や上の判定を作っていないのでなんとも言えませんが)


皆さんわざわざ答えていただいて本当に有難うございました。
順番を変えるだけでも随分違ってくるので、プログラミングはやはり難しいですが頑張ってみます。



ONION software Copyright 1997-2023(c) All rights reserved.