• ベストアンサー

線と点の当り判定

(ax,ay)と(bx,by)を結ぶ線上に点(cx,cy)があるかを判定するプログラムを教えてください。

質問者が選んだベストアンサー

  • ベストアンサー
noname#35109
noname#35109
回答No.2

#1です。 > (ax,ay)と(bx,by)を“結ぶ”線上に点(cx,cy)が > あるかの判定をしたい場合はどうしたらよいでしょう? それは簡単と言えば簡単ではないでしょうか。 次の通りなのですから。    Y  □■□□□回□□□□□回□□□□□□  □■□□□回□□□□□★□□□□□□  □■□□□回□□□□■回□□□□□□  □■□□□回□□□■□回□□□□□□  □■□□□回□□■□□回□□□□□□  □■□□□回□■□□□回□□□□□□  □■□□□回■□□□□回□□□□□□  □■□□□●□□□□□回□□□□□□  □■□□□回□□□□□回□□□□□□  ■■■■■■■■■■■■■■■■■■ X  □■□□□回□□□□□回□□□□□□        x=ax      x=bx cx >= ax で cx <= bx という条件が増えるだけです。 if (c._y == (b._y-a._y)*(c._x-a._x)/(b._x-a._x)+a._y && c._x>=a._x && c._x<=b._x) ただし, ax<bx の場合です。 ただ, どういう状況で使うかによって工夫(=ごまかし)のしかたが変わると思いますよ。 ※以下 この #2 の「工夫」とは,   「ごまかし」の意味も多々多々含むとして見てください。 上のままでいいかもしれないしダメかもしれません。 ax<bx の場合に限定できなければ, 大きいほうの値を返す, Math.max(a._x,b._x) や 小さいほうの値を返す, Math.min(a._x, b._x) を使用するべきかもしれません。 if (c._y == (b._y-a._y)*(c._x-a._x)/(b._x-a._x)+a._y && c._x>=Math.min(a._x, b._x) && c._x<=Math.max(a._x, b._x)) 使用例  #1と同じインスタンスの条件で使用方法を 工夫 した場合。 ///////////////////////////////////////////////////////////////////////////////// on (press) { // ドラッグ開始 this.startDrag(); } on (release, releaseOutside) { // ドラッグ終了 this.stopDrag(); } onClipEvent (mouseMove) { if (_root.c._y+2>=(_root.b._y-_root.a._y)*(_root.c._x-_root.a._x)/(_root.b._x-_root.a._x)+_root.a._y && _root.c._y-2<=(_root.b._y-_root.a._y)*(_root.c._x-_root.a._x)/(_root.b._x-_root.a._x)+_root.a._y && _root.c._x>=Math.min(_root.a._x, _root.b._x) && _root.c._x<=Math.max(_root.a._x, _root.b._x)) { trace("ヒット"); } } ///////////////////////////////////////////////////////////////////////////////// 一応実験のために使用例も書いていますが, このような使い方で良いのか悪いのかはわかりません。 どういう状況で使うかによって 工夫 のしかたが変わると思います。 ±2 などを使用しているのも 工夫 です。 とにかく, if (c._y == (b._y-a._y)*(c._x-a._x)/(b._x-a._x)+a._y && c._x>=Math.min(a._x, b._x) && c._x<=Math.max(a._x, b._x)) で理想上の直線と点の当たり判定にはなります。 他は色々な条件が加わると思います。 その都度 工夫 してください。 -・-・-・-・-・-・-・-・-・-・-・-・-・-・-・-・-・-・-・-・-・-・-・-・-・-・ そもそも当たり判定は 工夫 の美学(ちょっと言い過ぎ!)です。 厳密に考えていると, 人間の脳みその CPU も,PCのCPUもフルに使いすぎてオーバーフローを起こしてしまいます。 もとから, (ax,ay) も (bx,by) も決まっている定点であれば, #1のような複雑な一次方程式の変形もしないで良いわけですし, 傾きだけは決まっていて,切片だけが変わるものでしたら, もっと数式はシンプルになります。 #1の回答は, 単に (ax,ay)と(bx,by)を結ぶ線上(直線上と解釈) に点がヒットするかしないかというスクリプトを, 大まじめに考えたものです。 しかし,本当に大まじめに考えすぎると, 本来の当たり判定ができないので, 勝手に 工夫(←注:ごまかしですよ) したサンプルスクリプトを書いてみただけです。 どんなものを作ろうとされているのかがわからないので, 究極のところ,工夫 のしかたもわかりません。 場合によりますが,私が自分で作るなら, > (ax,ay)と(bx,by)を“結ぶ”線上に点(cx,cy)が > あるかの判定 ということをそもそも考えないような気がします。 Flashゲーム講座&ASサンプル集【当たり判定について】  →点とインスタンスとで当たり判定を取る http://hakuhin.hp.infoseek.co.jp/main/as/hittest.html#HITTEST_00 ここにあるように↑, 星形と点の当たり判定が hitTest で取れるのですから, 線( 工夫 した場合は 長方形)と点の当たり判定くらい簡単に取れるでしょう。 ですから > (ax,ay)と(bx,by)を“結ぶ”線上に点(cx,cy)が > あるかの判定 ではなく, ムービークリップの中に長方形をなんらかの形で作図して, その長方形と点との hitTest を使うと思います。 ですから, 根本的に (ax,ay) や (bx,by) を出す方法自体を変えてしまいます。 しかし, 作ろうとされているものがどういうものかがわからないので, 工夫 のしかたがわからないのです。 上記と同じサイトにこんな当たり判定サンプルもあります↓。 「円同士の衝突を計算する」 http://hakuhin.hp.infoseek.co.jp/flash/as/collide_00_00.html 見てスゴイと思いませんか? もし,思えばそれで良いのです。 円同士の当たり判定を真面目にすると, 人間の脳みそ CPU も,PCのCPUも使い切ってしまって, 動作が超重くなってしまいます。 そこで,工夫 しまくっています。 上のURLのリンク元はこちら↓。 hakuhin.hp.infoseek.co.jp/main/as/collide.html#COLLIDE_00 動きを見て, 人間が見てスゴイと思えばそれで良いのです。 工夫 に 工夫 を重ねた 工夫 の美学です。 そんなものですよ。 「よく見るとミサイルが当たる前に爆発している敵キャラ」 とかをゲームで多々見ませんか? 普通のゲームも工夫(←注:ごまかですよ)しまくっています。 昔々, hitTest など無い時代それも CPU の超スペック時代に, ゴキブリたたきゲームを Flash で作ったことがあります。 ランダムなときにゴキブリがランダムに出没して, 定点に置いてあるゴキブリたたきをクリックすると, ゴキブリたたきが振り下ろされて, ゴキブリに命中したら,ゴキブリが死んで得点が加算されるというような内容のものです。 hitTest などなくても, また,座標計算などしなくても, 工夫次第で当たり判定は取れます。 種を明かせば原理は簡単で, 「ゴキブリが出は消える」 というムービークリップをたくさん作って ランダムな時間に出没させるのです。 ゴキブリ自体は, ムービークリップの中でモーショントゥイーンによって動いているだけです。 ですから, 最初から,ゴキブリたたきにゴキブリがヒットするムービークリップもあれば, ゴキブリたたきにゴキブリがヒットしない(ゴキブリたたきが届く寸前でゴキブリが逃げるようにモーショントゥイーンさせている)ムービークリップもあるのです。 ゴキブリたたきにゴキブリがヒットするムービークリップは, そのムービークリップが出没してから, ゴキブリたたきがあるタイミングでクリックされないと, ゴキブリにヒットしないというフレームもわかっています。 ゴキブリがヒットするムービークリップの場合は, そのゴキブリムービークリップの○フレームから△フレーム内で, ゴキブリたたきがクリックされたとき, 「ゴキブリが死ぬ」というフレームに gotoAndPlay すれば良いのです。 原理は簡単ですが実際の 工夫 は難しいですよ。 でも, そういう手の込んだ 工夫 をすれば, そもそも当たり判定自体も不要になる可能性はあります。 変な話も書きましたが,以上で...。

kingfruits
質問者

お礼

作りたいもの作れました。 moveToとlineToで書いた線なので、hitTestでは上手く出来なかったので、質問しました。 ありがとうございました。

その他の回答 (1)

noname#35109
noname#35109
回答No.1

(ax,ay) と (bx,by) とを結ぶ一次方程式を導き出して, その一次関数の直線上に点(cx,cy)があるかないかを判定すれば良いと思いますよ。    Y  □■□□□□□□□□□□□□□□□□  □■□□□□□□□□□★□□□□□□  □■□□□□□□□□■□□□□□□□  □■□□□□□□□■□□□□□□□□  □■□□□□□□■□□□□□□□□□  □■□□□□□■□□□□□□□□□□  □■□□□□■□□□□□□□□□□□  □■□□□●□□□□□□□□□□□□  □■□□□□□□□□□□□□□□□□  ■■■■■■■■■■■■■■■■■■ X  □■□□□□□□□□□□□□□□□□  仮に ● を (ax,ay)   ★ を (bx,by) とします。  一次方程式を Y=AX + B として,  傾き A は (by-ay)/(bx-ax)  したがって,  Y=(by-ay)X/(bx-ax)+B  (ax,ay) を通る直線であることから切片 B は  ay=(by-ay)ax/(bx - ax)+B  B=ay-(by-ay)ax/(bx - ax)  よって,  Y=(by-ay)X/(bx-ax)+ay-(by-ay)ax/(bx-ax)  Y=(by-ay)(X-ax)/(bx-ax)+ay ---実際のFlash-------------------- インスタンス名 「a」 の MC(ムービークリップ) と インスタンス名 「b」 の MC があって, インスタンス名「c」の MC をドラッグして動かすとします。  if (c._y == (b._y-a._y)*(c._x-a._x)/(b._x-a._x)+a._y) これが true になれば,直線 (ax,ay) (bx,by) 上に「c」があることになります。 しかし,これは理想上のはなしで, 実際に直線にピッタリ重なるということは普通はありません。 余分にプラスマイナスの範囲を少し取って,次のようにすれば,実験ができます。 ステージ上にインスタンス名「a」と「b」と「c」という 小さめのムービークリップを任意の位置に作成します。 そして, 「c」に次のようなスクリプトを書きます。 /////////////////////////////////////////////////////////////////////////// on (press) { // ドラッグ開始 this.startDrag(); } on (release, releaseOutside) { // ドラッグ終了 this.stopDrag(); } onClipEvent (mouseMove) { if (_root.c._y+2 >= (_root.b._y-_root.a._y)*(_root.c._x-_root.a._x)/(_root.b._x-_root.a._x)+_root.a._y && _root.c._y-2 <= (_root.b._y-_root.a._y)*(_root.c._x-_root.a._x)/(_root.b._x-_root.a._x)+_root.a._y) { trace("ヒット"); } } /////////////////////////////////////////////////////////////////////////// 「修正」→「ムービープレビュー」で, ムービークリップ「c」をドラッグしてみると, 直線「a」-「b」上に「c」を移動させたとき, 「ヒット」がトレースされます。 if文内の _root.c._y+2 と _root.c._y-2  の ±2 が,余分の範囲です。 ※私は数学が苦手なので,   さらに何かを求められても回答できない自信があります。

kingfruits
質問者

お礼

ありがとうございます。 中学高で習った事で解けるのですね。 非常に判りやすい解説でした。 ベクトルの考え方でも解けるのでしょうか・・・

kingfruits
質問者

補足

試してみましたが、解答して頂いた方法ですと、(ax,ay)と(bx,by)の延長線上に(cx,cy)が存在しても「当り」と判定してしまいます・・・ (ax,ay)と(bx,by)を“結ぶ”線上に点(cx,cy)があるかの判定をしたい場合はどうしたらよいでしょう?