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


HSPTV!掲示板


未解決 解決 停止 削除要請

2007
0313
アキスキャラクターが壁にめり込んでしまう場合の対策は?14解決


アキス

リンク

2007/3/13(Tue) 19:24:03|NO.6272


buffer 1:picload"map.bmp" buffer 2:picload"obj.bmp" buffer 4:picload"my.bmp" screen 0,16*20,16*15,20:title"QUEST-OF-MAGICSTONE" dim map,20,15 x=1:y=1 px=x*16:py=y*16 map(0,0) =1,1,1,1 map(0,1) =1,0,0,1 map(0,2) =1,1,0,1 map(0,3) =1,0,0,1 map(0,4) =1,0,1,1 map(0,5) =1,0,0,1 map(0,6) =1,1,0,1 map(0,7) =1,0,0,1 map(0,8) =1,0,1,1 map(0,9) =1,0,0,1 map(0,10) =1,1,0,1 map(0,11) =1,0,0,1 map(0,12) =1,0,1,1 map(0,13) =1,0,0,1 map(0,14) =1,1,1,1 repeat:await 10 gosub *draw dx=px:dy=py stick k,53 if k&1 :dx-- if k&4 :dx++ if k&16 :;未完成 if k&32 :;未完成 if dx<=0 :dx=0 if map(dx/16,dy/16)=0 { px=dx:py=dy } gosub *foral loop stop *draw redraw 0 BG=3 if BG=0 :color 0,255,255:boxf if BG=1 :color 255,65,25:boxf if BG=2 :color 5,25,65:boxf if BG=3 :color 0,0,0:boxf if BG=4 :color 0,45,150:boxf if BG=5 :color 100,100,100:boxf gmode 2 repeat 15 cnty=cnt repeat 20 pos cnt*16,cnty*16 code=map(cnt,cnty) if code<4 { gcopy 1,code*16,0,16,16:code=map(cnt,cnty) }else{ code-=4:gcopy 2,code*16,0,16,16 } loop loop pos px,py gcopy 4,32,0,16,16 redraw 1 return *foral dy=py dy++ if map(dx/16,dy/16)=0 :py=dy return
以下のソースを実行するとキャラクターが壁にめり込んでしまいます。
何か対策は無いのでしょうか?

###デバッグ用情報
←・→で操作
map.bmpの内容
左から"無","壁","トゲ","青い石"
のような構成になってます。
obj.bmpの内容
左から"爆弾箱","回復箱","ジャンプ台","←移動パネル","→移動パネル"
my.bmp"の内容
左から"にっこりの顔","不満の顔","右向き主人公","左向き主人公"

となっています。
キャラのサイズは全て16×16です。

P.S
ソースは全部必要部分です。



この記事に返信する


GENKI

リンク

2007/3/13(Tue) 21:54:43|NO.6273

 判定個所を自機の左上1ドットのみしか使用していないのが原因です。
自機の4つ角全てに判定を行う点を付け加えれば、大丈夫だと思います。
http://hspwiki.nm.land.to/?%BE%AE%A5%EF%A5%B6%2F%A5%A2%A5%AF%A5%B7%A5%E7%A5%F3%2F%B4%F0%CB%DC%C6%B0%BA%EE#x39cc22f


以下お願いです。

1.できればそのまま実行できるスクリプトにしてくれると助かります。
【対策例】

buffer 1:picload"map.bmp" buffer 2:picload"obj.bmp" buffer 4:picload"my.bmp"


screen 1:mes "□■" screen 4:color 0,0,255:mes "□□■"
とするなど。


> ソースは全部必要部分です。

2.不要な個所は出来るだけ削ってくれると助かります。

if k&16 :;未完成 if k&32 :;未完成


BG=3 if BG=0 :color 0,255,255:boxf ... if BG=5 :color 100,100,100:boxf
などなど…他にもありますが、これらは明らかに今回の質問内容に対して不要な個所です。

3.適度な改行やコメント、またもう少しタブを加えたほうがスクリプトが見やすくなります。


さて、2,3はデバッグにおける必須項目です。
もう少し頑張ってもらえると原因を探しやすくなるので助かるのですが…。



アキス

リンク

2007/3/13(Tue) 22:01:22|NO.6274

>>GENKIさん
はい、わかりました。
「無駄なところ」を少々見落としていました。

>>本題へ・・・
左上、右上、左下、右下、の全部を判定すればいいのですね。
となると、ピクセル単位の座標を表す変数px,pyを右下の座標を表す、px2,py2という変数を用意し、
IFで&を使って判断するわけですね。
dupを使えばIFが少し短くなりそうです。

今日はもう遅いので、出来ません。
明日試してみます。



アキス

リンク

2007/3/14(Wed) 14:53:44|NO.6280

一回試しましたが、やっぱりめり込んでしまうようです。
(落ちた時に)
ちなみに修正済みのIF命令
if map(dx/16,dy/16)=0 & map((dx+16)/16,(dy+16)/16)=0




osakana

リンク

2007/3/14(Wed) 17:03:44|NO.6283

一応この場ではいらない所を削って、
なるべく元のソースのまま無理矢理動くようにして見ましたが
構想を練り直して、1から書きなおした方が早いかもしれません。

screen 1:mes "□■" screen 4:color 0,0,255:mes "□□■" screen 0,16*20,16*15,20:title"QUEST-OF-MAGICSTONE" dim map,20,15 x=1 :y=1 :dx=x*16 :dy=y*16 map(0,0) =1,1,1,1 map(0,1) =1,0,0,1 map(0,2) =1,1,0,1 map(0,3) =1,0,0,1 map(0,4) =1,0,1,1 map(0,5) =1,0,0,1 map(0,6) =1,1,0,1 map(0,7) =1,0,0,1 map(0,8) =1,0,1,1 map(0,9) =1,0,0,1 map(0,10) =1,1,0,1 map(0,11) =1,0,0,1 map(0,12) =1,0,1,1 map(0,13) =1,0,0,1 map(0,14) =1,1,1,1 repeat await 10 gosub *draw stick k,5 if k&1 :if map((dx-1)/16,dy/16)=0 :dx-- if k&4 :if map(dx/16+1,dy/16)=0 :dx++ gosub *foral title "dx:"+dx+",dy"+dy+"/"+(dx/16)+","+(dy/16) loop *draw redraw 0 color 0,0,0 :boxf repeat 15 :cnty=cnt repeat 20 code=map(cnt,cnty) pos cnt*16,cnty*16 :gcopy 1,code*16,0,16,16 loop loop pos dx,dy :gcopy 4,32,0,16,16 redraw 1 return *foral if map((dx+1)/16,dy/16+1)=0 & map((dx+15)/16,dy/16+1)=0 :dy++ return



アキス

リンク

2007/3/14(Wed) 17:25:56|NO.6286

osakanaさん
GENKIさん
真にありがとうございます。
壁にめり込まず動くようになりました。
お二人ともご教授ありがとうございます。



KIMU

リンク

2007/3/15(Thu) 22:06:08|NO.6321

もう解決してるみただけど・・・
移動速度が1以外でキャラ絵に余白があっても壁や床にピッタリ密着出来て
キャラが2ブロック以上の大きさでも問題が出ない方法を挙げてみる
(中途半端に元のスクリプトを使ってるから分かり憎いかも・・・)

buffer 1:mes " ■":buffer 2:buffer 4:cls 4:color 255:mes "  ■■":mes "  ■■" #define Bsize 16 ;1ブロックサイズ //壁の判定用 #const WsizeX Bsize ;キャラの幅(Bsizeで割り切れる数値を指定) #const WSPsize 3 ;左右のスペース(WsizeXがキャラの見た目の大きさと違う時に余白分を設定) #const LSPsize WSPsize ;基点(dx)からの判定距離(左)(基点がキャラの中心なら左右で分ける必要ないかな?) #const RSPsize WsizeX-WSPsize-1 ;基点(dx)からの判定距離(右) #const TLOOP RSPsize/Bsize+1 ;横幅が1ブロックより大きいときの判定を繰り返す数 //床と天上の判定用 #const HsizeY Bsize*2 ;キャラの高さ #const HSPsize 3 ;上下のスペース #const USPsize HSPsize ;基点(dy)からの判定距離(上) #const DSPsize HsizeY-1-HSPsize ;基点(dy)からの判定距離(下) #const HLOOP DSPsize/Bsize+1 screen 0,16*20,16*15,20:dim pxx,100:dim pyy,100:dim map,20,15:randomize:x=1:y=1:px=x*Bsize:py=y*Bsize repeat 15*20:map(cnt\20,cnt/20)=(((cnt+1)\20)<2)|((((cnt+20)/20)\15)<2)|(rnd(6)=3)*((cnt/20)>2):loop Jstart=18 Jend=20 Sspeed=1.5 Gacr=0.2 dx=px dy=py fx=double(dx) fy=double(dy) repeat:await 10 px=fx:py=fy gosub *draw stick k,$fff if k&1 :fx-Sspeed if k&4 :fx+Sspeed if ((k&16)>0) { ;[スペース]ジャンプ始動 if (dwnf=0) and (jycnt<Jend) and (upf=0) {jfy=Jstart :jycnt++} }else{ if (jycnt>0) and (upf=0) {jycnt=Jend} } if jycnt>0 { ;ジャンプ処理 if jfy>0 {jfy--:fy=limitf(fy-Gacr*jfy,0,16*14)}else{jycnt=0:dwnf=1} } if dwnf>0 {dycnt+1:fy=limitf(fy+Gacr*dycnt,0,16*14)}else{dycnt=0} ;前回床が無ければ落下処理 dx=int(fx) dy=int(fy) //ブロックの角部分の遊び幅設定 ydif=dy-py ;上下速度が遊び幅より大きいと引っ掛かるので縦の移動量を取って遊び幅から減算 HDSPsizeY=DSPsize-ydif-4 ;ちょっと床を踏み外しても戻れるように-4 HUSPsizeY=USPsize-ydif+4 ;落下しながら横穴に入りやすいように+4 if dx!px { lx=(dx/Bsize)*Bsize lcdx=(dx+LSPsize)/Bsize ;左側の判定用 rcdx=(dx+RSPsize)/Bsize ;右側の判定用 ucdy=(dy+HUSPsizeY)/Bsize ;上横側の判定用 repeat HLOOP dcdy=(dy+HDSPsizeY-cnt*Bsize)/Bsize ;下横側の判定用 if fx<px{ if map(lcdx,dcdy)|map(lcdx,ucdy) {dx=lx+Bsize-WSPsize :break} }else{ if map(rcdx,dcdy)|map(rcdx,ucdy) {dx=lx+WSPsize :break} } loop } gosub *foral if dx!fx {fx=double(dx)} if dy!fy {fy=double(dy)} loop stop *draw redraw 0:color 0,0,0:boxf:gmode 2 repeat 15:cnty=cnt: repeat 20: pos cnt*Bsize,cnty*Bsize: code=map(cnt,cnty) if code<4 {gcopy 1,code*Bsize,0,Bsize,Bsize:code=map(cnt,cnty)}else{code-=4:gcopy 2,code*Bsize,0,Bsize,Bsize}loop:loop pos px,py:gcopy 4,32,0,WsizeX,HsizeY :pxx(cnt\100)=int(px):pyy(cnt\100)=int(py):cnt_=cnt repeat 99,cnt_+2:color 255,255-(cnt-cnt_)*2,255:line pxx(cnt\100),pyy(cnt\100),pxx((cnt+99)\100),pyy((cnt+99)\100):loop redraw 1:return *foral lcdx=(dx+LSPsize)/Bsize ucdy=(dy+USPsize)/Bsize if fy=py{ dcdy=(dy+DSPsize+1)/Bsize repeat TLOOP rcdx=(dx+RSPsize-cnt*Bsize)/Bsize if map(lcdx,dcdy)!map(rcdx,dcdy) { dy=(dy/Bsize)*Bsize+HSPsize :dwnf=0 :break }else{ dwnf=1 ;縦移動が無くて下に床も無い } loop }else{ dcdy=(dy+DSPsize)/Bsize repeat TLOOP rcdx=(dx+RSPsize-cnt*Bsize)/Bsize if fy>py { if map(lcdx,dcdy)|map(rcdx,dcdy) {dy=(dy/Bsize)*Bsize+HSPsize :dwnf=0 :upf=0 :break} }else{ if map(lcdx,ucdy)|map(rcdx,ucdy) {dy=((dy+Bsize)/Bsize)*Bsize-HSPsize :dwnf=0 :jycnt=0 :upf=1:break} } loop } return



GENKI

リンク

2007/3/16(Fri) 00:38:10|NO.6322

>> KIMUさん
ここまで大きいとサンプル出すよりアルゴリズムを説明したほうがわかりやすいような気がします…。



KIMU

リンク

2007/3/16(Fri) 01:28:17|NO.6325

> GENKI さん
すいません・・・
分かりやすいアルゴリズムの説明書くのが、難しい気がして
サンプル作りに逃げるって、悪い癖が・・・



アキス

リンク

2007/3/16(Fri) 17:08:31|NO.6343

移動の軌道まで表示されていて分かりやすいのですが、少々アルゴリズムが分かりません。
教えてもらえるとありがたいです。



KIMU

リンク

2007/3/17(Sat) 01:09:51|NO.6355

>教えてもらえるとありがたいです。
場合によっては自分が意図した動きにならない所があるので
ちょっと作り直してみます。(NO.6321のを元にすると説明もし難いので・・・)



KIMU

リンク

2007/3/18(Sun) 05:19:59|NO.6403

NO.6321に完全な間違いがありました。

> if map(lcdx,dcdy)!map(rcdx,dcdy) {
「!」では無く「|」こっちでした・・・

他にも色々制限が出来てたので、それを解消(したつもり・・・)したのを作ってみました。

#define BS 16 ;1ブロックサイズ //キャラサイズ:ブロックと重なる範囲:右、下角からのキャラを置く位置:右、下の判定位置:壁より床天上の判定を優先する範囲(U:D) WS=20:WSP=3:RM=WS-WSP:RHS=RM-1:U=4:D=4 HS=40:HSP=4:DM=HS-HSP:DHS=DM-1 buffer 1:cls 4:color 50,20150:boxf 17,0,32+32*5,14 buffer 2:color 0,255:buffer 4:cls 4:color 255:circle BS*2,0,BS*2+WS,HS screen 0,16*20,16*15,20:mx=100:my=15:dim map,mx+1,my:randomize:x=10:y=1 repeat my*mx:map(cnt\mx,cnt/mx)=(((cnt+1)\mx)<2)|((((cnt+mx)/mx)\15)<2)|(rnd(6)=3)*((cnt/mx)>2):loop //Sspeed=移動速度: Gacr=重力加速度:fx、fy=実数での移動計算用:scx=スクロール座標 Spd=3.0:Gacr=0.1:fx=double(x*BS):fy=double(y*BS):scx=double(0) repeat:await 10:redraw 0:color 0,0,0:boxf:gmode 1 repeat 15:cnty=cnt: repeat 21,-scx/16:pos cnt*BS+scx,cnty*BS gcopy 1,map(cnt,cnty)*BS,0,BS,BS:loop:loop:gmode 2 px=fx:py=fy:scxb=scx ;前回のキャラ座標とスクロール座標を保存 stick k,$fff ;[←:↑:↓:→]移動 、 [Ctrl]+[←:→] でスクロール開始 if k&1 {scspd=Spd*((k&64)>0):fx-Spd-scspd}else{if k&4 {scspd=-Spd*((k&64)>0):fx+Spd+scspd}} scx=limitf(scx+scspd,-(mx-20)*BS,0) if k&2 {fy-Spd:dwnf=0}else{if k&8 {fy+Spd}} if dwnf>0 {dycnt+1:fy=limitf(fy+Gacr*dycnt,0,16*14)}else{dycnt=0} ;前回床が無ければ落下処理 //ffx,ppxに前回からどっちに動いたかを代入(ffxが小さい場合左に動いたとして左の壁だけ見る) ffx=fx-scx:ppx=px-scxb:dx=int(fx):dy=int(fy) gosub *collision if dx!fx {fx=double(dx)}:if dy!fy {fy=double(dy)} ;dx,dyに変化があったらfx,fyに代入 pos px,py:gcopy 4,32,0,WS,HS:redraw 1:loop:stop *collision ydif=dy-py ;上下速度が判定を優先する範囲より大きいと引っ掛かるので縦の移動量を上下判定範囲から削減 l_x=(dx+WSP-scx)/BS:r_x=(dx+RHS-scx)/BS:u_y=(dy+HSP-ydif+U)/BS:d_y=(dy+DHS-ydif-D)/BS:lpmax=d_y-u_y+1 if ffx<ppx {movx=l_x*16+BS-WSP+scx:c_x=l_x}else{movx=r_x*16-RM+scx:c_x=r_x} if map(c_x,u_y) {dx=movx}else{repeat lpmax,u_y:if (map(c_x,cnt)) {dx=movx:break}loop} l_x=(dx+WSP-scx)/BS:r_x=(dx+RHS-scx)/BS:u_y=(dy+HSP)/BS:lpmax=r_x-l_x+1 if fy=py {d_y=(dy+DHS+1)/BS:if map(l_x,d_y) {dy=d_y*BS-DM:dwnf=0}else{dwnf=1 repeat lpmax,l_x:if map(cnt,d_y) {dy=d_y*BS-DM:dwnf=0:break }else{dwnf=1}loop}}else{d_y=(dy+DHS)/BS if fy>py {movy=d_y*BS-DM:c_y=d_y:dup dyf,dwnf ;下側のブロック判定準備(ブロックが見つかったらdyにmovyが入る) }else{movy=u_y*BS+BS-HSP:c_y=u_y:dup dyf,dycnt} ;上側のブロック判定準備 if map(l_x,c_y) {dy=movy:dyf=0 ;左上か左下にブロックがあったら上で準備した座標をdyに入れて判定終了 }else{repeat lpmax,l_x ;右側-左側+1 でマス数の差異分+1繰り返す。スタートcntを左側のマス数にする if map(cnt,c_y) {dy=movy:dyf=0:break}loop}} ;ブロックがあったら上で準備した座標をdyに入れて判定終了break return
で、説明ですが、先ずは アキスさんのやり方では問題出るだろって所から書きます。
接触があったら移動をしない方法だと、1ブロックサイズで割り切れない位置だった場合
壁とキャラに隙間が開いたり1ブロック分しか空きがない通路を曲がれなくなったりします。

それを、解決する一つの方法として、今回のを説明します。
(やってる事は単純ですが・・・)
LABCD
K■□□E
JIHGF
この状態の場合■の位置はKの座標(左上角)から1ブロック右にあると考えられます。
左に動いてKに当たったらKを基準にすれば■の位置を決める事が出来ます。
右に移動してって、Eに衝突した場合は、Eの座標から■のサイズ分左の位置が
■の座標として使えます。
上下に関しても同じです。

・1ブロック右
・■のサイズ分左
この衝突ブロックからの位置を決めるサイズを調節すれば、少し壁に重なるような位置にも置けます。
(判定側も合わせる必要がありますが・・・)

後、キャラサイズが1ブロックより大きい時の判定は、キャラの左に重なってるマスと
右に重なってるマスの差異分繰り返して判定してます。(上下も同じように)
左側1 右側3 なら右側を減らしながら 2回繰り返して判定
(左右同じ場合でも1回は判定する)

ここに挙げたのは色々判定を省略したりしてるので、効率が悪くなってる所もあるので注意


#一度削除してスクリプトの修正しました
#やってる事は変わらないけど・・・



KIMU

リンク

2007/3/20(Tue) 20:28:02|NO.6446

>ydif=dy-py ;上下速度が判定を優先する範囲より大きいと引っ掛かるので縦の移動量を上下判定範囲から削減
>l_x=(dx+WSP-scx)/BS:r_x=(dx+RHS-scx)/BS:u_y=(dy+HSP-ydif+U)/BS:d_y=(dy+DHS-ydif-D)/BS:lpmax=d_y-u_y+1

ydifの解釈が自分でやっときながらちょっと違ってました。

>u_y=(dy+HSP-ydif+U)/BS
>d_y=(dy+DHS-ydif-D)/BS
これはydifは無くして

u_y=(int(px)+HSP+U)/BS d_y=(int(dy)+DHS-D)/BS
の方がしっくり来ます。
この部分は結構重要な部分なのに説明はしてなかった・・・
(自分でも完全には把握してなかったからだけど(^^;)

何故移動前のY座標を使うか説明をすると

AB
CD

これで、AからDに斜めに移動する場合Dにブロックあると、壁として見るか天上や床として見るか決められません。
そのまま、壁判定してから床天上判定って流れでした場合どんな当たりかたしてもCに移動してしまいます。
(AとBの間ぐらいからDに当たってもCに移動してしまう)

:Dにブロックある場合
これを、壁(左右の判定)する時は移動前のY座標を使うと、判定はBの位置を見ます。
Bに何もなければXはそのままで、次はX,Y共に移動先(D)の座標を使い判定します。
Dにブロックが有って下に移動中の場合DからキャラYサイズを引いた位置をYにします。
結果、キャラはBの位置になります。

:Bにブロックある場合
壁の判定時にBがブロックだったらXはBからキャラXサイズ引いた位置になり
この変化したXを使いYは移動先を使うので天上と床を判定するのは、Cのマスを調べる事になります。
Cに何もなければそのままなので、キャラはCの位置に決定します。
(Cにもブロックが有ればCからキャラYサイズを引いた位置になるのでAの位置に決定します)

こんな感じかな?



X

リンク

2007/3/21(Wed) 16:09:33|NO.6461

何回訂正するんだよ。質問者に迷惑だから
そんな何回も訂正が必要なものを投稿するな。



アキス

リンク

2007/3/22(Thu) 15:09:04|NO.6483

うーん・・・
やっぱり分かりません・・・・・・・
もうちょっとHSPが得意になったら再挑戦します・・・



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