新しく発言する EXIT インデックスへ
点と壁

  点 と 壁 たかし 2005/09/18 12:12:36 
  こんにちは。AやBも解決できそうですが、... 青木太一 2005/09/18 22:25:55 
  │└参考まで(@、B) 山中和義 2005/09/18 22:53:43 
  │ └この方法だとBにちょっと問題が…… 青木太一 2005/09/18 23:49:09 
  つぎにB 青木太一 2005/09/18 23:36:26 
  >A緑色の点が壁(kabeとkabe2)に当たったら... 青木太一 2005/09/19 00:33:37 
  │└縦壁のプロトタイプ 山中和義 2005/09/19 07:35:34 
  │ └なるほど。衝突時は点が動けなくなるという... 青木太一 2005/09/19 13:43:56 
  もしかしてのちのち、点を複数にするつもり... 青木太一 2005/09/19 00:41:02 
   └皆さんたくさんの助言をしていただいて、ど... たかし 2005/09/19 20:05:36 

  点 と 壁 たかし 2005/09/18 12:12:36  ツリーへ

点 と 壁 返事を書く
たかし 2005/09/18 12:12:36
動く点と壁を使って面白くない迷路を作っているのですが、お聞きしたいことがあります。

@壁(kabe と kabe2 二つとも黒色)が画面の上下左右の端にきたら、跳ね返るようにしたいのですがうまくいきません。
A緑色の点が壁(kabe とkabe2)に当たったら跳ね返るようにしたいのですが、どうすればいいのか分かりません。
B緑色の点が画面の上下左右の端にきたら、跳ね返るようにしたいのですがうまくいきません。
上の3点について以下のプログラムをじっさいに実行していただいて、解決策や「ここをこうすればもっくと面白くなる」などの助言
をいただきたい。


rem ** 点と壁
randomize
set window 0,1,0,1

LET n=25
dim x(1 to n),y(1 to n) !壁の位置
dim vx(1 to n),vy(1 to n)!壁の速度(velocity)
dim zx(1),zy(1)!点
dim xx(1),yy(1)! 点が進む?

!初期値設定
for i=1 to n
LET x(i)=rnd
LET y(i)=rnd
LET vx(i)=(rnd-0.5)/50
LET vy(i)=(rnd-0.5)/50
LET zx(1)=0
LET zy(1)=0
let xx(1)=0.05
let yy(1)=0.05
next i


!メインループ開始

DO



! キー操作の設定
WAIT DELAY 0.01 ! 0.01秒待機する
IF GetKeyState(27)<0 THEN EXIT DO ! ESCを押すと終了
IF GetKeyState(37)<0 THEN LET zx(1)=zx(1)-xx(1)
IF GetKeyState(38)<0 THEN LET zy(1)=zy(1)+yy(1)
IF GetKeyState(39)<0 THEN LET zx(1)=zx(1)+xx(1)
IF GetKeyState(40)<0 THEN LET zy(1)=zy(1)-yy(1)



!壁が外壁にぶつかったら反射する
for i=1 to n
if x(i)<-0.5 or 0.5<x(i) then LET vx(i)=-vx(i)
if y(i)<-0.5 or 0.5<y(i) then LET vy(i)=-vy(i)
next i

!壁を速度によって位置を進める
for i=1 to n
LET x(i)=x(i)+vx(i)
LET y(i)=y(i)+vy(i)
next i

!壁を描く
for i=1 to n
draw kabe WITH SHIFT (x(i),y(i))
draw kabe2 with shift (x(i),y(i))
next i




!点を描いて動かす
DRAW tenn WITH SHIFT(zx(1),zy(1))

!外壁にぶつかったら反射する
IF zx(1)<0 THEN LET xx(1)= -xx(1)
IF zx(1)>1 THEN LET xx(1)=+xx(1)
IF zy(1)<-1 THEN LET yy(1)=-yy(1)
IF zy(1)>1 THEN LET yy(1)=+yy(1)




!ちらつき防止
set draw mode explicit
set draw mode hidden
clear
draw grid(0.1,0.1)

LOOP




END

external picture tenn
set area color 3
plot area : 0.01,0.95;0.04,0.95;0.04,0.92;0.01,0.92
end picture


external picture kabe
set area color 1
plot area: 0.5,0.5;0.55,0.5;0.55,0.9;0.5,0.9
END picture


external picture kabe2
set area color 1
plot area:0.3,0.1;0.3,0.15;0.6,0.15;0.6,0.1
END picture



  こんにちは。AやBも解決できそうですが、... 青木太一 2005/09/18 22:25:55  ツリーへ

Re: 点 と 壁 返事を書く
青木太一 2005/09/18 22:25:55
こんにちは。AやBも解決できそうですが、とりあえず@
>壁(kabe と kabe2 二つとも黒色)が画面の上下左右の端にきたら、
>跳ね返るようにしたいのですがうまくいきません。
について自分の思うところを書いてみます。
「うまくいきません」とはなんのことを指しているのかわからないので、
たかしさんの思っている点と違っていたら失礼。

<<<< 概要 >>>>
うまくいってない点
「右上の方で縦横どちらかに、または両方向ともに移動せず、
 ただ振動するだけの壁がいる」
原因
「振動している壁は初期位置が跳ね返り条件の範囲外なので、
 つねに跳ね返り状態になってしまっている」
対処
「初期位置の範囲(または跳ね返り条件の範囲)を調整する」

<<<< 詳細 >>>>
表示範囲
set window 0,1,0,1

set window -1,2,-1,2
に変え、
壁の表示方法を
draw kabe WITH SHIFT (x(i),y(i))
draw kabe2 with shift (x(i),y(i))
から
plot points:x(i),y(i)
に変えると、なにが起きているのか、
ちょっとわかりやすくなると思います。

壁の位置を表す配列xやyは初期化時に
LET x(i)=rnd
LET y(i)=rnd
としているので、最初に0〜1の間の値をとるはずです。
(rndは0以上、1未満の値を返します)
ところが、壁との跳ね返りは
if x(i)<-0.5 or 0.5<x(i) then LET vx(i)=-vx(i)
if y(i)<-0.5 or 0.5<y(i) then LET vy(i)=-vy(i)
と書いているので、最初に0.5より大きく位置を初期化された壁は
毎ループ毎に進行方向を逆転されて、ただ振動するしかありません。
(イメージとしては跳ね返り範囲の外に埋まってしまって、もがいている感じです)

ということで、位置の初期化の範囲(0〜1)が、
跳ね返り条件の範囲(-0.5〜0.5)から外れていたのが原因です。
位置の初期化の範囲は跳ね返りの条件の範囲の中に入らないといけません。

おそらく、初期化の方を
LET x(i)=rnd-0.5
LET y(i)=rnd-0.5
とすれば、たかしさんのイメージ通りになると思います。
(0〜1までだった初期化範囲を-0.5〜0.5に調整しています)

  │└参考まで(@、B) 山中和義 2005/09/18 22:53:43  ツリーへ

Re: こんにちは。AやBも解決できそうですが、... 返事を書く
山中和義 2005/09/18 22:53:43
参考まで(@、B)

1.点、壁の作画情報(頂点位置)を原点を中心に作成する
2.点、壁は同一配列で処理する
  添え字0を自キャラ、1〜を敵キャラ
3.キー処理、位置算出(含む当り判定)、描画に分ける

以上から

DRAW WITH SHIFT命令(平行移動)の座標は、この原点がSET WINDOW命令の座標の
どこに配置するのかになる。

プログラムが簡潔になる。


rem ** 点と壁
randomize
set window 0,1,0,1

LET n=25
DIM x(0 TO n),y(0 TO n) !点、壁の位置
DIM vx(0 TO n),vy(0 TO n) !点、壁の速度(velocity)

!初期値設定
LET x(0)=0.5 !点の位置
LET y(0)=0.5

FOR i=1 TO n !壁の位置、速度
LET x(i)=RND
LET y(i)=RND
LET vx(i)=(rnd-0.5)/50
LET vy(i)=(rnd-0.5)/50
NEXT i


!メインループ開始

DO

LET vx(0)=0 !点の速度
LET vy(0)=0


! キー操作の設定
WAIT DELAY 0.01 ! 0.01秒待機する
IF GetKeyState(27)<0 THEN EXIT DO ! ESCを押すと終了
IF GetKeyState(37)<0 THEN LET vx(0)=-0.05
IF GetKeyState(38)<0 THEN LET vy(0)=0.05
IF GetKeyState(39)<0 THEN LET vx(0)=0.05
IF GetKeyState(40)<0 THEN LET vy(0)=-0.05



!点、壁が外壁にぶつかったら反射する
FOR i=0 TO n
IF x(i)<0 OR 1<x(i) THEN LET vx(i)=-vx(i)
IF y(i)<0 OR 1<y(i) THEN LET vy(i)=-vy(i)
NEXT i

!点、壁を速度によって位置を進める
FOR i=0 TO n
LET x(i)=x(i)+vx(i)
LET y(i)=y(i)+vy(i)
next i


!ちらつき防止
SET DRAW mode hidden
CLEAR

DRAW grid(0.1,0.1)



!壁を描く
for i=1 to n
draw kabe WITH SHIFT (x(i),y(i))
!draw kabe2 with shift (x(i),y(i))
NEXT i

!点を描く
DRAW tenn WITH SHIFT(x(0),y(0))



!ちらつき防止
set draw mode explicit

LOOP


END

external picture tenn
set area color 3
PLOT AREA : 0.015,0.015;-0.015,0.015;-0.015,-0.015;0.015,-0.015
end picture


external picture kabe
set area color 1
PLOT AREA: 0.025,0.2;-0.025,0.2;-0.025,-0.2;0.025,-0.2
END picture


external picture kabe2
SET AREA COLOR 1
PLOT AREA:0.2,0.025;0.2,-0.025;-0.2,-0.025;-0.2,0.025
END picture

  │ └この方法だとBにちょっと問題が…… 青木太一 2005/09/18 23:49:09  ツリーへ

Re: 参考まで(@、B) 返事を書く
青木太一 2005/09/18 23:49:09
この方法だとBにちょっと問題が……

最初この書き込みを見たとき、
「なるほど、こうやれば壁と点を統一的に扱えてきれいなプログラムになるのか!」
と思ったのですが、操作するとバグを発見しました。

点をある方向に移動していき(たとえば右カーソルをおしつづけて)
画面外に出た瞬間に逆のカーソルを押すと、そのままどんどん画面外に出てしまいます。
(画面外に出る直前のカーソル方向を、画面外に出た直後も押し続けているとは限らないので「vx(0)=-vx(0)」が有効な手段でない場合があるのです)

表示範囲を
set window -1,2,-1,2
にして実行するとわかりやすいと思います。

反射の部分のコードを以下のように書き換えれば問題はなくなります。

!点、壁が外壁にぶつかったら反射する
FOR i=0 TO n
IF x(i)<0 THEN LET vx(i)=abs(vx(i))
IF y(i)<0 THEN LET vy(i)=abs(vy(i))
IF 1<x(i) THEN LET vx(i)=-abs(vx(i))
IF 1<y(i) THEN LET vy(i)=-abs(vy(i))
NEXT i

  つぎにB 青木太一 2005/09/18 23:36:26  ツリーへ

Re: 点 と 壁 返事を書く
青木太一 2005/09/18 23:36:26
つぎにB
>緑色の点が画面の上下左右の端にきたら、
>跳ね返るようにしたいのですがうまくいきません。

について書いてみます。
これは、「跳ね返る」をどのような意味で書いているかによるのですが、
「画面外に出ようとしたら、強制的に画面内にひきもどされる」
という効果をつけたいのでしたら

!外壁にぶつかったら反射する
IF zx(1)<0 THEN LET xx(1)= -xx(1)
IF zx(1)>1 THEN LET xx(1)=+xx(1)
IF zy(1)<-1 THEN LET yy(1)=-yy(1)
IF zy(1)>1 THEN LET yy(1)=+yy(1)



!外壁にぶつかったら反射する
IF zx(1)<0 THEN LET zx(1)=0-xx(1)
IF zx(1)>1 THEN LET zx(1)=1-xx(1)
IF zy(1)<0 THEN LET zy(1)=0-yy(1)
IF zy(1)>1 THEN LET zy(1)=1-yy(1)

に変えればいいと思います。

もとのプログラムは以下の点でおかしかったです
・三行目の条件がなぜか0でなく-1でした
・どうも、「画面外に出たら、速度を画面内に引き戻す方向に変更」
 という意図のようですが(誤解だったらごめんなさい)
 xxやyyは負の値になるかもしれないので、あの書き方では無意味です
 (abs関数を使って大きさのみ取り出せばまだ意味がわかるかも)、
 そもそもキーの操作によって移動方区はxxやyyの値の方向にも、
 その逆方向にも動かせるので、
 画面内に引き戻す効果がある方向に移動するとは限らない。


修正後のプログラムでは、画面外に出たとき
移動距離xxやyyをいじるのでなく、
単純に位置zx,zyを画面内に引き戻しています。


なお上の修正を施したあとに点の表示位置を修正する必要があります。

external picture tenn
set area color 3
plot area : 0.01,0.95;0.04,0.95;0.04,0.92;0.01,0.92
end picture



external picture tenn
set area color 3
plot area : 0.01,0.05; 0.04,0.05; 0.04,0.02; 0.01,0.02
end picture

に変更してください。
(なぜもとのtennでは原点よりかなり高い位置に点を描いていたのでしょう?)

山中さんのやり方でもいいと思いますが、せっかく書いていたので一応書き込んでみます。

  >A緑色の点が壁(kabeとkabe2)に当たったら... 青木太一 2005/09/19 00:33:37  ツリーへ

Re: 点 と 壁 返事を書く
青木太一 2005/09/19 00:33:37
>A緑色の点が壁(kabe とkabe2)に当たったら跳ね返るようにしたいのですが、どうすればいいのか分かりません。
ですが、けっこう面倒そうですね。
そもそもどう跳ね返させるのかいろいろな考え方があります。

ごめんなさい、時間ないのでコードは書きません。
一応つらつらと思ったことを書いてみます。

・壁と点の反射を扱うのなら、絵定義tennやkabeで直接数値を書くのでなく、変数で行った方がよい。
 そうでないと、それらの寸法を変えたいときにプログラムの複数カ所で変更が必要になる。

・外壁にくらべて、壁との衝突を扱うのは以下の点で面倒くさい
1.外壁と違って、壁は一ステップで通り過ぎてしまうことがある(壁の大きさにくらべて、壁と点の一ステップ当たりの最大移動距離がじゅうぶん小さくなければならない)
そこをどうプログラムするか。
2.壁は複数あるので、壁に挟まれたときどう跳ね返るのか、または跳ね返らずほかの挙動をしめすのか(点が壊れる、壁を貫通するなど)考えておかなくてはいけない。
3.壁のどちら側からぶつかったのか判定しなければならない。
 たとえば、「まずは点が壁に突入しているかどうかを判定し、その次に、壁の速度と点の速度の差をとって相対速度を得る。相対速度の方向によって点の位置を壁のどの外側に出すか決める」
という方法が思いつく。

他にも、相対速度を用いずに、突入している位置のみによって跳ね返りの方向を決める方法も考えられる。

  │└縦壁のプロトタイプ 山中和義 2005/09/19 07:35:34  ツリーへ

Re: >A緑色の点が壁(kabeとkabe2)に当たったら... 返事を書く
山中和義 2005/09/19 07:35:34
縦壁のプロトタイプ
青木さんの検討項目を
 1.「通り抜け現象」は無視する。
 2.3.は、停止する。点の中心が壁の枠内による衝突判定。
として、コード化すると、、、


REM ** 点と壁
RANDOMIZE
SET WINDOW -1,2,-1,2

LET n=15
DIM x(0 TO n),y(0 TO n) !点、壁の位置
DIM vx(0 TO n),vy(0 TO n) !点、壁の速度(velocity)

!初期値設定
LET x(0)=0.5 !点の位置
LET y(0)=0.5

FOR i=1 TO n !壁の位置、速度
LET x(i)=RND
LET y(i)=RND
LET vx(i)=(rnd-0.5)/50
LET vy(i)=(rnd-0.5)/50
NEXT i


!メインループ開始

DO

LET vx(0)=0 !点の速度(キーが押されてない)
LET vy(0)=0


! キー操作の設定
WAIT DELAY 0.01 ! 0.01秒待機する
IF GetKeyState(27)<0 THEN EXIT DO ! ESCを押すと終了
IF GetKeyState(37)<0 THEN LET vx(0)=-0.05
IF GetKeyState(38)<0 THEN LET vy(0)=0.05
IF GetKeyState(39)<0 THEN LET vx(0)=0.05
IF GetKeyState(40)<0 THEN LET vy(0)=-0.05



!点と壁との当り判定
FOR i=1 TO n
LET xx=ABS(x(i)-x(0))
LET yy=ABS(y(i)-y(0))
IF xx<0.025 AND yy<0.2 THEN !縦壁の幅、高さ
LET vx(0)=0
LET vy(0)=0
EXIT FOR !ひとつ見つかれば十分!
END IF
NEXT i


!点、壁について
FOR i=0 TO n
LET x(i)=x(i)+vx(i) !点、壁を速度によって位置を進める
LET y(i)=y(i)+vy(i)

IF x(i)<0 THEN !外壁にぶつかったら、
LET x(i)=0 !端に位置付ける
LET vx(i)=-vx(i) !反射する
END IF
IF 1<x(i) THEN
LET x(i)=1
LET vx(i)=-vx(i)
END IF
IF y(i)<0 THEN
LET y(i)=0
LET vy(i)=-vy(i)
END IF
IF 1<y(i) THEN
LET y(i)=1
LET vy(i)=-vy(i)
END IF
NEXT i


!ちらつき防止(開始)
SET DRAW mode hidden
CLEAR

DRAW grid(0.1,0.1)


!壁を描く
FOR i=1 TO n
DRAW kabe WITH SHIFT (x(i),y(i))
NEXT i

!点を描く
DRAW tenn WITH SHIFT(x(0),y(0))


!ちらつき防止(終了)
SET DRAW mode explicit

LOOP


END


EXTERNAL PICTURE tenn
SET AREA COLOR 3
PLOT AREA : 0.015,0.015;-0.015,0.015;-0.015,-0.015;0.015,-0.015
END PICTURE


EXTERNAL PICTURE kabe
SET AREA COLOR 1
PLOT AREA: 0.025,0.2;-0.025,0.2;-0.025,-0.2;0.025,-0.2
END PICTURE

  │ └なるほど。衝突時は点が動けなくなるという... 青木太一 2005/09/19 13:43:56  ツリーへ

Re: 縦壁のプロトタイプ 返事を書く
青木太一 2005/09/19 13:43:56
なるほど。衝突時は点が動けなくなるというのは、面倒がすくなくていい割り切り方ですね。

ちょっとコードを書き加えてタイムアタックのゲームにしてみました。
n番目の壁(青)をゴールとするおいかけっこゲームです。
参考までに、私のちょっとやってみた最短記録は3.6秒でした。
それなりにおもしろくなったかも?

REM ** 点と壁
RANDOMIZE
SET WINDOW 0,1,0,1
LET n=10
DIM x(0 TO n),y(0 TO n) !点、壁の位置
DIM vx(0 TO n),vy(0 TO n) !点、壁の速度(velocity)

!初期値設定
LET x(0)=0 !点の位置
LET y(0)=0

FOR i=1 TO n !壁の位置、速度
LET x(i)=RND
LET y(i)=RND
LET vx(i)=(rnd-0.5)/50
LET vy(i)=(rnd-0.5)/50
NEXT i

LET x(n)=0.9
LET y(n)=0.9


!メインループ開始
LET t=time
DO

LET vx(0)=0 !点の速度(キーが押されてない)
LET vy(0)=0

! キー操作の設定
WAIT DELAY 0.101 ! 0.01秒待機する
IF GetKeyState(27)<0 THEN EXIT DO ! ESCを押すと終了
IF GetKeyState(37)<0 THEN LET vx(0)=-0.05
IF GetKeyState(38)<0 THEN LET vy(0)=0.05
IF GetKeyState(39)<0 THEN LET vx(0)=0.05
IF GetKeyState(40)<0 THEN LET vy(0)=-0.05

!点と壁との当り判定
LET xx=ABS(x(n)-x(0))
LET yy=ABS(y(n)-y(0))
IF xx<0.025 AND yy<0.2 THEN !縦壁の幅、高さ
exit do
END IF

FOR i=1 TO n-1
LET xx=ABS(x(i)-x(0))
LET yy=ABS(y(i)-y(0))
IF xx<0.025 AND yy<0.2 THEN !縦壁の幅、高さ
LET vx(0)=0
LET vy(0)=0
EXIT FOR !ひとつ見つかれば十分!
END IF
NEXT i

!点、壁について
FOR i=0 TO n
LET x(i)=x(i)+vx(i) !点、壁を速度によって位置を進める
LET y(i)=y(i)+vy(i)

IF x(i)<0 THEN !外壁にぶつかったら、
LET x(i)=0 !端に位置付ける
LET vx(i)=-vx(i) !反射する
END IF
IF 1<x(i) THEN
LET x(i)=1
LET vx(i)=-vx(i)
END IF
IF y(i)<0 THEN
LET y(i)=0
LET vy(i)=-vy(i)
END IF
IF 1<y(i) THEN
LET y(i)=1
LET vy(i)=-vy(i)
END IF
NEXT i

!ちらつき防止(開始)
SET DRAW mode hidden
CLEAR

DRAW grid(0.1,0.1)

!壁を描く
SET AREA COLOR 1
FOR i=1 TO n-1
DRAW kabe WITH SHIFT (x(i),y(i))
NEXT i
SET AREA COLOR 2
DRAW kabe WITH SHIFT (x(n),y(n))

!点を描く
DRAW tenn WITH SHIFT(x(0),y(0))

set text color 4
plot text ,at 0,0:"経過時間"&str$(time-t)&"秒"

!ちらつき防止(終了)
SET DRAW mode explicit

LOOP

END

EXTERNAL PICTURE tenn
SET AREA COLOR 3
PLOT AREA : 0.015,0.015;-0.015,0.015;-0.015,-0.015;0.015,-0.015
END PICTURE

EXTERNAL PICTURE kabe
PLOT AREA: 0.025,0.2;-0.025,0.2;-0.025,-0.2;0.025,-0.2
END PICTURE

  もしかしてのちのち、点を複数にするつもり... 青木太一 2005/09/19 00:41:02  ツリーへ

Re: 点 と 壁 返事を書く
青木太一 2005/09/19 00:41:02
もしかしてのちのち、点を複数にするつもりがあって、その拡張性のためかもしれませんが、

dim zx(1),zy(1)!点
dim xx(1),yy(1)! 点が進む?

のように点の位置と移動距離を配列にしているのは意味ないですよね。
普通の変数にした方が見やすいと思います。

   └皆さんたくさんの助言をしていただいて、ど... たかし 2005/09/19 20:05:36  ツリーへ

Re: もしかしてのちのち、点を複数にするつもり... 返事を書く
たかし 2005/09/19 20:05:36
皆さんたくさんの助言をしていただいて、どうもありがとうございます。自分は10進ベーシックを始めて1年経つか経たないかなので、難しいところはよく分かりません。
少しずつ理解していきたいです。


インデックスへ EXIT
新規発言を反映させるにはブラウザの更新ボタンを押してください。