• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:キー入力で操作しているMCを障害物で止める方法)

キー入力で操作しているMCを障害物で止める方法

このQ&Aのポイント
  • キー入力で操作しているMCが障害物内に飛んでしまう問題や、障害物の上を流される現象が起こる場合の解決策を求めています。
  • 衝突判定が実行されると障害物から離れられない問題や、障害物に接触した後に違うキーを押すと障害物の反対側に飛んでしまう現象などに困っています。
  • WinXPとFlash8Proでオーサリングしており、障害物のMCに関連するスクリプトも記載しています。何か良い解決策があれば教えてください。

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

  • ベストアンサー
  • DPE
  • ベストアンサー率85% (666/776)
回答No.6

どのように・・・と言われましても、そのような怪現象は初めてです。 プレイヤーがマップの階層内にあるのなら、子として列挙されて自分自身と当たりをとってしまう可能性は考えられますけれど、ターゲットパスを見た限りではそういう構成ではありませんよね。 fot in を使わなければ、正常に衝突判定をとってその手前で立ち止まれるのでしょうか? Hit_Check 関数内の for in をコメントにして、何か特定の障害物のムービークリップとプレイヤーで hitTest を実行し、true の時に obje_hit 関数を呼び出すと、どうなりますでしょうか。 あとはデバッガを使って調べることでしょうか。 デバッガにはいろいろな機能があり、スクリプトを1行ずつコマ送りのように実行させることもできます。 スクリプトを1行ずつ実行させるには、まず、”ブレークポイント”を定義します。 その名の通りスクリプトを一時的に停止させるポイントのことで、この行を基点にコマ送りなどを実行します。 「アクション」パネルを開き、Hit_Check 関数を書いた部分を表示してください。 2箇所ある obje_hit を呼び出している行を選び、右クリック→「ブレークポイントの設定」を選択します。 すると、パネルの左端に赤い●が付きます。これがブレークポイントが設定されている印です。 なお、行番号の表示位置を直接クリックすることでもブレークポイントを設定できます。 デバッガを利用するには、ムービーをデバッグ用に書き出す必要があります。 「制御」→「ムービーのデバッグ」と選んでください。デバッグ情報を含んだムービーが書き出され、「デバッガ」パネルが表示されます。 デバッガは最初は一時停止状態になっています。 パネルの上部にある三角の再生ボタンを押すと再生されます。 プレイヤーを動かし、障害物に接してみてください。 正常に衝突判定がとれているならば、どちらかの obje_hit 関数を呼び出す直前でスクリプトが停止し、パネルの右側に停止した付近のスクリプトが表示されます。 スクリプトが停止している状態では、() に矢印が付いたアイコンの3つのボタンが有効になります。 中央の”ステップイン”を押すたびにスクリプトが1行ずつ実行されて、右側に今実行している箇所が表示されます。 obje_hit 関数が呼び出される時、今回定義した obje_hit 関数に移動していますでしょうか? 何か違う関数の定義箇所に移動しているようなら、その箇所をコメントにするか、今回の関数を別の名前にしてみてください。 ちなみに関数内で var で定義したローカル変数は、「ローカル」というタブを開いておくと値を確認できます。 中には定義した覚えのないものも混ざっていますが、とりあえず気にしないでください。 ただし、参照が入っている変数の値は確認できません。(これが確認できれば便利だったのですが、、、 ^^;) このタブはパネルの左側の、ムービークリップの構造が表示されているスペースのすぐ下にあります。 パネルの大きさや項目の表示スペースによっては見えない場合があるので、仕切りをドラッグして見やすい大きさに調節してください。 デバッガでも原因が突き止められないようですと、申し訳ございませんがまさに怪現象でお手上げです。 ところで、今のシステムではなく新規に作ってみた場合も、このような怪現象が起きるのでしょうか? 同じような階層構造でざっと作ってみてこちらが正常に動くのなら、既にムービーに組み込まれていて今まで出てこなかった何かが悪さをしているのかもしれません。 それが何かは分かりませんが... ------------------------------------------------------------ 違う階層に onEnterFrame を定義して利用すること自体は問題ではありませんし、いつの間にか混ざってお互いの処理を妨害し合うこともありません。 onClipEvent(enterFrame) を多くのムービークリップに書いても動作するのと、同じことです。 ただ、今回の件では、例えば _root.onEnterFrame と ply.onEnterFrame の両方で移動処理をしていると、移動処理が2回行われて移動速度が2倍になります。 単に速くなるだけならまだしも、移動→衝突判定とその後の処理→移動というような順に処理されると、衝突しっぱなしになったり、位置情報が破壊されて異常な位置に行ってしまうなどの不具合の原因になります。 _root で衝突判定・ ply で移動処理をしているといった場合ですと、関数が呼び出されるタイミングの違いで障害物を突き抜けたり、くっついたまま離れなくなってしまうことがあります。 競合するという言葉は何か違うかもしれませんが、お互いが影響しあって変な現象が起きる可能性はあります。 スクリプトの保守の面から考えてみても、移動や衝突判定はできるだけあちこちの階層に分散しない方がいいかと思います。

sato777
質問者

お礼

いつもお世話になっております。 DPE様、有難う御座います!やっと正常動作させることができました^^ 感激です♪ 外部SWFも最初からシンプルなものを作り、ひとつずつ動作検証を行いました。 また、メーンのシーンのスクリプトも 一度単純なものをテスト用として作り、最初から一ずつ検証する事でデバッグを成功させる事ができました。 ちなみに、原因は二つありまして、 外部SWF内のMCにonClipEvent(enterFrame) が書かれていたのですが、それがなぜか?妨害していたようです。 また、plyの衝突処理を case 'w': ply._y = edge.yMax + adjust; break; //下に移動していた時:障害物の上端で停止 case 's': ply._y = edge.yMin - ply._height +10 - adjust; break; //左に移動していた時:障害物の右端で停止 case 'a': ply._x = edge.xMax + adjust; break; //右に移動していた時:障害物の左端で停止 case 'd': ply._x = edge.xMin - ply._width - adjust; break; とする事で正常に動作しました。 この様に書かずに一つでもちがうと プレイヤーが跳ね返ったり、ワープしたりしてしまいます。 跳ね返り現象やワープ現象は、 プレイヤーと障害物の距離を正しく補正する事で解消できた見たいです。 今まで教えて頂いた内容はすべて出力して 大切に保管させて頂いております。 また、書籍などでは目にする事が出来ないスクリプトもこれから意欲的に応用していけそうです。 何度も何度も多大な時間をかけて親切丁寧な解説と スクリプトを頂き、本当に感謝しております^^ 本当に有難うございました! 正直、ここまで協力頂いたのに完成した際に お見せできないのが残念でなりません。 教えてGooのシステム上、大変残念です。 ですが、また何かありましたらその際は、 どうぞ宜しくお願い致します。

その他の回答 (5)

  • DPE
  • ベストアンサー率85% (666/776)
回答No.5

まず、外部ファイルを読み込んだ時の階層ですが、ムービークリップに読み込んだ場合は、このムービークリップのタイムラインが外部ファイルのメインのタイムラインに置き換えられます。 例えば map.swf の階層構成が  _root   └ a    └ b     └ c 読み込むメインムービーは  _root   ┗ A    ┗ B     ┗ C だとします。 C のムービークリップに map.swf を読み込むと、メインムービーの階層構成は次のように変化します。  _root   ┗ A    ┗ B     ┗ C      └ a       └ b        └ c イメージとしては、C のタイムラインと map.swf のメインのタイムラインを糊しろにしてメインムービーに貼り付けるという感じです。 マップの swf に main_map_mc があり、_root.main_mc.move_map_mc.move_map_mc_mc に読み込んだのでしたら、Hit_Check 関数の呼び出しは  _global.Hit_Check( _root.main_mc.move_map_mc.move_map_mc_mc.main_map_mc ); です。 ---------------------------------------------------------------- Hit_Check 関数に引数である衝突判定をとるムービークリップの参照は、正しく渡されてきていますでしょうか。 Hit_Check 関数の先頭に  trace( base ); を入れてチェックしてみてください。 これがそもそも undefined になるようなら、ここから先の話に進めません。 なお、onEnterFrame イベントハンドラの定義をフレーム1で行うと、マップの読み込みが済むまでは map.swf 内にあるムービークリップは未定義扱いになっています。 読み込みが済めば undefined になることはなく、Hit_Check 関数にも読み込んだムービークリップの参照が渡るようになりますが、念のため、読み込みの完了を待ってから定義した方がいいかもしれません。 base が正常に渡されてきているのであれば、次は for in による列挙の部分です。 外側の for in に  trace( base[ clip ] ); 内側の for in に  trace( base[ clip ][ sub ] ); を入れて、正常に列挙されるかどうか確認してみてください。 ムービークリップが列挙されているならば、hitTest も実行されるかと思います。 hitTest をとる if 文内に traceア クションを書いておくと、当たりがとれているかどうかを調べることができます。 障害物の参照だけ正しくても相方のプレイヤーの参照が正しくなければ、hitTest の結果は常に false になります。 ply に入れる参照に誤りがないかも、合わせてご確認ください。 ******************************** ここまで問題がなければ、obje_hit の方が怪しいと言えそうです。 以前の obje_hit 関数が残っていると、関数名が衝突して正常に呼び出されません。 同名だとややこしいので、新しく作った方を別の名前に変更した方が安全かもしれませんね。 関数名を変えた時は、呼び出すところの変更もお忘れなく。 ただ、obje_hit 内の ply に入る参照が正常で Hit_Check 内の変数名のタイプミスなどもないとなれば、Hit_Check に渡されてくる最も大元になるムービークリップの参照が正しく受け取れていないことが原因ではないかと思います。 また、他のムービークリップで hitTest を行っていたり別のところでプレイヤーの移動処理を行っていると、競合して正常に動かなくなります。 特にプレイヤーの移動や衝突判定関連の処理が他にも残っているようでしたら、とりあえずコメントにしてみてください。 「ムービーエクスプローラ」(「ウィンドウ」メニュー→「ムービーエクスプローラ」で開けます)で ActionScript を表示させると、スクリプトが書かれている場所を簡単に探すことができます。 簡単ながら動作確認をしましたので、全く動かないことはないと思います。 申し訳ありませんが、とりあえず思いつく原因やデバッグ方法はこのくらいです。 あとは現物を見ないことには何とも言えません。 ---------------------------------------------------------------- for in で列挙させて総当たりで当たりをとると、実は弱点もあります。 例えば、あるムービークリップに子が2つ含まれていて、片方は通れる地形で片方は通れない地形だとします。 通れない地形の方は問題ないのですが、通れる地形の場合も立ち止まらせる処理が行われ、通れなくなってしまいます。 シェイプの上にムービークリップを重ねた障害物を作り、シェイプの部分も含めて通行不可にしたい場合は、本来ならばこのムービークリップ自身の大きさで当たりをとらなければならないはずです。 しかし、今回のシステムでは”障害物が子を持っている時はその子の前で立ち止まる”という設計になっているため、シェイプの部分は通り抜けてしまいます。 ただ、この弱点を逆手にとって、先のように”通行不可の地形と通行可能な地形が混在している障害物”を作ることもできそうです。 ******************************** マップデータの容量と当たり判定の多さは、いつの時代でも苦労するところです。 例えば城壁のような障害物は同じ模様がずっと続くので、大きな1枚の画像を持つよりも、レンガの模様を1つ作って敷き詰めるようなシステムを作ると、大きな画像を持って容量を浪費することもなくなります。 Flash にはムービークリップという便利なものがあります。 これと同じ発想で、レンガのムービークリップを作り、これを敷き詰めたムービークリップを障害物として使うことで、容量の節約はできます。 しかし、Hit_Check 関数では子の持っている子階層まで調査が行われるため、このような作りの障害物では非常に多くのムービークリップとの衝突判定が実行されてしまいます。 1つでも接触していた場合は判定が途中で打ち切られるようにはしてありますが、接触しているものがなかなか見付けられないような大きな障害物では、無駄な処理が延々と続いてしまうことになります。 ただの模様ならば、何もムービークリップにする必要はありません。 レンガやタイルを敷き詰めたような障害物は、レンガやタイルをグラフィックシンボルで作り、これを敷き詰めた障害物をムービークリップとして作ると、このムービークリップに子は存在しないので障害物自身で衝突判定が行われるようになります。 グラフィックシンボルを利用すれば容量の節約にもつながります。

sato777
質問者

お礼

いつもお世話になっております。 補足です。 たった今、外部SWFの構造が複雑だったので、 それが怪しいと思い、単純な構造で作成した所 衝突判定と衝突処理ともに動作確認できました! ですが、まだ微妙に問題がありまして、 画面向かって右と下から衝突した際に 跳ね返ってしまう事と、 左と上から衝突した際に、衝突するとちゃんととまるのですが 例えば、左から衝突して止まったとします。 そのまま、右キーを押すと障害物の右側にプレイヤーがワープしてしまう現象が起こっております。 これだけ何とかできれば、あとは微調整の段階に 入れるかと思いますが、アドヴァイス頂けると幸いです。 どうぞ宜しくお願い致します。

sato777
質問者

補足

いつも大変お世話になっております。 外部SWFの読み込みに関するパスに関して、 認識が間違っていなかったので安心しました。 有難う御座います。 衝突判定の不具合は、どうやらonEnterFrame=functionが競合して起こっていたようです。 _root.onEnterFrame=function(){ ----------------- 正常動作 } ply.onEnterFrame=function(){ ----------------- *実際にはこのスクリプトの方は  動作していませんでした。 } これが競合していた箇所ですが、 パスが違っていてもonEnterFrame=function() は競合してしまうものなのでしょうか? きちんと意味や構造がわかっていなかったと実感 しております。 そして、訂正すると今度は無条件に衝突判定が起こってしまっております。 デバッグすると以下のとおりでした。 base=_level0.main_mc.move_map_mc.move_map_mc_mc.main_map_mc obstacle=_level0.main_mc.room_mc.room_petbox1_mc plyがobstacleとして渡されている所がおかしいと感じています。 ここには障害物のMCが入るはずだろうと思います。 何も改造せずにそのままのスクリプトで実行しておりますが、この様なデバッグ結果が表示されております。 どの様に対処すればよいのでしょうか。 アドヴァイス頂けたら幸いです。 また、グラフィックシンボルで障害物を作り、衝突判定の対象となるMC数を減らすというのは、大変勉強になりました。 有難う御座います。 最近とてもご教授頂いたスクリプトが明るく見えるようになってまいりました。 これも常々詳しい解説とサンプルスクリプトを ご教授頂いている賜物で御座います。 本当に心から感謝しております。 今後ともどうぞ宜しくお願い致します。

  • DPE
  • ベストアンサー率85% (666/776)
回答No.4

obje_hit の中で仮引数 obstacle を表示させてこれが undefined になるということは、obje_hit を呼び出すところに問題があると思われます。 Hit_Check 関数内の for in の中に、obje_hit 関数を呼び出しているところが2箇所あります。 内側の for in にある   //子が持っているムービークリップを列挙   //何か1つとでも接触したら終了   for( sub in base[ clip ] )   {    //衝突判定    //接触時にプレイヤーを手前で立ち止まらせる    if( base[ clip ][ sub ].hitTest( ply ) )    {  ⇒  _global.obje_hit( base[ clip ][ sub ] ); この部分と、外側の for in の下の方にある   //孫が1つもない時は子で衝突判定をとる   else if( ! g_childs )   {    if( base[ clip ].hitTest( ply ) )    {  ⇒  _global.obje_hit( base[ clip ] );       の、2箇所です。 最初の obje_hit は2階層下のムービークリップの参照を渡すので、  _global.obje_hit( base[ clip ][ sub ] ); と2階層になりますが、次の呼び出しでは1階層下にあるムービークリップですから、[ sub ] の部分が消えて  _global.obje_hit( base[ clip ] ); となります。 [ ] でくくって指定している clip や sub は変数なので、" " でくくらないでください。 " " でくくると文字列と解釈され、違う意味になります。 変数名のタイプミス・大文字/小文字の誤りなどがないかも、チェックしてみてください。

sato777
質問者

補足

_global.Hit_Check = function( base:MovieClip ) { var ply:MovieClip , clip:String , sub:String , g_childs:Number , hit_flg:Boolean; ply = _root.main_mc.room_mc.room_petbox1_mc; //指定の階層に含まれるムービークリップを列挙 //何か1つとでも接触したら終了 hit_flg = false; for( clip in base ) { //基準から見て孫にあたるムービークリップの数を数える g_childs = 0; //子が持っているムービークリップを列挙 //何か1つとでも接触したら終了 for( sub in base[ clip ] ) { //衝突判定 //接触時にプレイヤーを手前で立ち止まらせる if( base[ clip ][ sub ].hitTest( ply ) ) { _global.obje_hit( base[ clip ][ sub ] ); //ループを終了 hit_flg = true; break; } //孫の数を更新 g_childs++; } //孫の中のどれか1つとでも接触していれば終了 if( hit_flg ) { break; } //孫が1つもない時は子で衝突判定をとる else if( ! g_childs ) { if( base[ clip ].hitTest( ply ) ) { _global.obje_hit( base[ clip ] ); //ループを終了 break; } } } }; お返事有難う御座います。 教えて頂いたスクリプトをそのままコピーし、全角余白を半角余白に変換して使用させて頂いております。 衝突判定は正常に出来ているので、これをどう改良してよいのか解らないでいる所です。 あと!、重大なミスにたった今、気が付いてしまったのですが、 衝突判定は以前に外部SWF(MAP)のMCに直接記載した衝突判定スクリプト(_global.obje_hit();)が生きていたようです。 すぐにコメント化しましたが、すると衝突判定が されなくなってしまいました。 _root.main_mc.move_map_mc.move_map_mc_mcに 外部SWF(MAP)をLoadしているのですが、 MAPのパスは、以下の様になっております。 _root.main_map_mc このMC下にbricks_mcが多数シンボルMCとして存在している状態です。 このbricks_mcがすべて列挙されれば、衝突判定も 衝突処理もうまくいくのかなと思いますが、 _global.Hit_Check( _root.main_mc.move_map_mc.move_map_mc_mc.main_map_mc ); のパスをあれこれ変えて試して見ましたが、 なかなかうまくいきませんでした。 パスの確認なのですが、 _root.main_mc.move_map_mc.move_map_mc_mcにLoadしている場合、 move_map_mc_mcが外部SWF⇒_root.main_map_mcの_rootに当たるという認識で大丈夫でしょうか? また振り出しに戻ってしまった感がありますが、 なんとか何卒宜しくお願い致します。

  • DPE
  • ベストアンサー率85% (666/776)
回答No.3

#1、2です。 obje_hit 関数内で edge(障害物の端の座標)が取得できない原因は2つ考えられます。 1つは、引数で受け取るムービークリップが存在せず、最初から undefined が渡されてきている可能性です。 obje_hit の冒頭に  trace( obstacle ); と入れて、引数の内容を表示させてみてください。 undefined が出力されるようであれば、Hit_Check 関数内で obje_hit 関数を呼び出すところ(2箇所あります)に誤りがあると思われます。 Hit_Check では、for in で列挙したムービークリップで衝突判定をとり、衝突していたと判断される時だけ障害物のムービークリップの参照を obje_hit に渡して呼び出します。 衝突判定が正常に動作し obje_hit が呼び出されていても、obje_hit を呼び出す際に引数の書き方に誤りがあると障害物の情報が渡らなくなってしまいます。 もう1つは getBounds に渡す基準の座標系が存在しない可能性です。  ply = _root.main_mc.room_mc.room_petbox1_mc; の後に  trace( ply._parent ); と入れてみてください。 undefined が表示されるなら、ply に入れる座標系に誤りがあります。 edge の値が全く取得できない( undefined になる)原因は、このどちらかだと思います。 edge に何らかの値が入っていて、プレイヤーが変な位置で止まる・障害物を突き抜ける場合は、基準にする座標系が違っている(プレイヤーの参照がそもそも違っている)可能性が考えられます。 また、obje_hit 内でのプレイヤーの位置の計算はプレイヤーの基準点が中央にあるものとして行っています。 左上など別の場所にある場合は計算式の変更が必要です。 基本的には、基準点の座標が障害物の端から(少し隙間を空けて)離れればいいということです。図を描いて考えながら計算式を変更してください。 #2に書きましたように、ムービークリップのサイズは絵の内容によって変化します。 試験的に丸や四角で試していると気が付きませんが、実際にキャラクターの絵を入れた時にこの仕様が意外な大問題を起こすことがあります。 一般的な2DのRPGでも、普通はキャラクターのサイズはどのアニメのコマも全て同じサイズで扱えるように作ります。 どのコマも同じサイズなら当たりも全て同じ大きさで考えることができるため、ある特定のアニメのコマを表示していた時だけ衝突判定がおかしくなるといった不具合も起こりません。 ---------------------------------------------------------------- ところで、#1のプレイヤーの onEnterFrame 関数ですが。 ローカル変数を多数宣言していますが、実際に使っているのは spd だけで、残りはこの関数内で衝突判定を作っていた時の残骸です ^^; spd 以外は不要ですので、削除してください。

sato777
質問者

補足

いつも大変お世話になっております。 デバッグした所、 plyは正常に表示されました。 しかし、  trace( obstacle ); こちらはundefinedと表示されています。 どのように変更すれば正常に動作するのか アドヴァイス頂けると幸いです。 どうぞ宜しくお願い致します。

  • DPE
  • ベストアンサー率85% (666/776)
回答No.2

#1です。 長~いスクリプトや複雑なスクリプトが一発で動くなんてことは極めて稀で、今回の試作品を作った時もいろいろな失敗がありました。 その原因は、  1) 関数が、そもそも呼び出されていない  2) ムービークリップの列挙が上手くいっていない  3) プレイヤーのムービークリップが参照できていない といったものでした。 今回はこれらのデバッグ方法をご紹介します。 この中に何かヒントや参考になるものがあれば幸いです。 1) については、関数の冒頭部分に trace アクションで何かメッセージでも表示させてみて、表示されなかったら関数がそもそも呼び出されていなかったと分かります。 関数が呼び出されないのは、私の場合ですと、関数名を変えたのに呼び出す時は変更する前のままだったというケースが多いです。 また、関数にもターゲットパスがありますのでご注意ください。 2) は for in ループの中で参照を trace アクションで表示してみると分かります。 Hit_Check 関数内で  for( clip in base )  {   trace( base[ clip ] );    :   for( sub in base[ clip ] )   {    trace( base[ clip ][ sub ] );     : というように、trace アクションを入れてチェックしてみてください。 インスタンス名を付けなかったムービークリップには、instance○○ という仮の名前が付けられます。 for in ではこの名前を使って列挙されますから、インスタンス名の有無はさしあたって関係ありません。 3) はグローバルレベルの関数に変更した時にやってしまったミスで、意外にクセモノでした。 衝突判定および接触後の処理を行う関数はグローバルレベルの持ち物です。 (匿名関数の参照を持っているのが、_global 階層にある変数のためです) この関数内での this は、関数の参照を持っている変数自身を指しています。 つまり、衝突判定をとる関数内では Hit_Check 、接触後の処理内では obje_hit を指します。 これが何の関係があるかと言いますと、関数内のローカル変数 ply にプレイヤーのムービークリップの参照を入れていますが、これを  ply = this.main_mc.player_mc.player_mc_mc.player_mc_mc; と this を使ってターゲットパスを書くと、this がグローバルレベルにある変数自身を指すためにプレイヤーのムービークリップを正確に参照できなくなってしまいます。 ターゲットパスを自動で入力してくれる機能ではデフォルトが相対パスになっており、this から始まるターゲットパスが入力されるので、ご利用の際はご注意ください。 マップを外部から読み込んでざっと試してみましたが、大丈夫でした。 _root( _level0 )にある loading_mc にマップの swf ファイルを読み込むのでしたら、  _global.Hit_Check( _root.loading_mc ); です。 ただし、Hit_Check を呼び出す時点で loading_mc がタイムライン上に存在していることが条件です。 ”後で現れる予定の loading_mc ”という呼び出し方は通用しません。 例えばフレーム1のスクリプト内で上記のように呼び出すのなら、フレーム1に既に loading_mc が存在している必要があります。 ******************************** ところで、複数のフレームから成るムービークリップの幅や高さは一定ではなく、フレームに描かれた絵の内容によって微妙に変化しています。 例えばキャラクターが横向きの場合を考えてみますと、静止している時と歩行中に手足が出ている時とでは、歩行中のパターンの方が手足が出ている分だけムービークリップの幅が広くなります。 幅が狭い絵のフレームが再生されている時に障害物の左右の端から接触すると、この時の幅を元に立ち止まらせる計算が行われてしまうので、障害物の前ではね返って見えたり衝突判定が狂ったりすることがあります。 手っ取り早い解決方法は、プレイヤーのムービークリップにレイヤーを1つ追加して1番下に配置し、ここに全ての絵が収まる大きさの透明な四角形を描画しておくことです。 (この四角形はムービークリップでなくても構いません。透明色は「カラーミキサー」パネルで作成できます) キャラクターの絵の下に大きな四角形を1枚敷いておくとムービークリップのサイズはこの四角形の大きさになり、絵の内容に関係なく一定のサイズに保つことができます。 -------------------------------------------------------------- かなりの大作のようですね。 No.2055332 の#4にも書きましたが、擬似3Dにするならば2次元配列を使ったマップの方が作りやすいと思います。 2Dのマップなら、もう少し構造を統一して仕様を作ってはいかがでしょうか。 一般的なRPGでも、ある程度の制限を設けた仕様内でマップやキャラクターを作り、極力例外を出さないシステムを作ります。 その結果、どことなく規則性が感じられる動きやマップになってしまいますが、これはデジタルのゲームである以上は仕方がありませんね。 例えば、先の Hit_Check 関数では2階層下まで調査して衝突判定を行っていますが、入れ子になっている障害物がないと予め分かっているならば、for in を二重にするなどのややこしい処理をしなくても済みます。 プレイヤーのムービークリップも、なぜこんなに階層を深くしなければならないのでしょうか? プレイヤーは一番よく使うムービークリップなので、階層はあまり深くしない方が扱いやすいと思います。 階層が深くなるほどターゲットパスの誤りが発生しやすくなりますし、hitTest や getBounds の計算も重くなります。 ******************************** ちなみに、2Dのゲームでも2次元配列でマップデータを持ってマップパーツを動的に配置し、プレイヤーの移動を制限したりイベントを発生させる方法があります。 むしろ当たり判定をとって障害物ごとに立ち止まらせるよりも、一般的で理にかなった手法かもしれません。 例えばプレイヤーの1歩前にアイテムが落ちている時、普通のRPGでは接しただけでは何も起こらず、接してなおかつ「調べる」ボタンを押してはじめて入手したことになります。 このような場合も、”「調べる」ボタンが押された時にはプレイヤーの1歩前(配列変数の隣の要素)を見る”というシステムを予め用意しておくと、あとは調べた結果何か落ちているのならアイテムを拾う処理をすればいいだけです。 アイテムを拾った後は配列変数の値を床や地面のデータに書き換えておけば、続けて何度もアイテムを拾えてしまうような不具合も防げます。 今は大容量のメモリが当たり前になりましたが、それでも限られた資源(リソース)で、果てしなく巨大な配列変数を確保することはできません。 昔のコンピュータでは特にこの制約が厳しかったため、内容的にマップを大きくしなくても済む演出になっていました。 ある程度移動すると絶対に通れない地形(山や河川・湖沼・海など)に突き当たったり、ダンジョンの出口・違う世界への入口などがあって、そこから先のマップデータを常にメモリ内に確保しておかなくても済むようになっています。 障害物を置いておけば、そこから先には進めないのは明らかですからマップデータは不要ですし、違う世界につながる地形に来た時は、今のデータを捨てて配列変数の値を次のマップデータに書き換え、フェードアウト等のエフェクトでも入れて画面を切り替えてしまえばいいのです。 港で船に乗るといつの間にか違うマップに切り替わっていたり、怪しげなマークを踏んで別世界に行ってしまうといった演出も、ハードの制約上メモリ内に小さなマップデータしか持てなくても広い世界を冒険しているように見せかける、ゲームの内容面での工夫だと思います。 巨大な配列変数も確保できる余裕がある今の時代から見るとちょっと貧乏くさい話ではありますが、このような演出もアリですね。 今でもこのような演出は使われていますが、メモリの容量が増えた分だけ、より大きく精密なマップが表現できるようになったという感じです。 なお、この手の演出は、通れない地形の向こうに平原や建物・ダンジョンの入口などを少しだけ見せるところがミソです。 ”障害物に邪魔されて行けそうで行けない”という演出は、物語が進めばそこに行けるようになるという伏線になりますし、今まで行けなかったところに行けるようになるのもRPGの楽しさの1つです。 半分は余談になりましたが、機会がありましたら配列変数を使ったマップについても研究してみてください。

sato777
質問者

補足

いつも有難う御座います。 Hit_Checkが正常に動いて、 外部SWF内のMCを_rootから列挙出来た事に感動しております。 これはさまざまな応用が出来ると思いました。 プレイヤーの階層ですが、プレイヤーはRPGの世界とヴァーチャルワールドの自分の部屋にいる時と、またさまざまなシチュエーションによって、描写の仕方が変わってくる設定になっています。 RPGで遊ぶユーザーには装備をつけた状態のぷれいやーでRPGをしないユーザーは普段着でヴァーチヤルワールドないでの生活を楽しみます。 また、ペットも操作できるので、現在は一番処理が少ないペットで衝突判定の検証を行っています。 ユーザーだとスキル、健康状態、魔法、ステータス、階級、ギルド、クラン、転職などによってもそれぞれトリガーやMySQlとの連携や、ストリーミングサーバー、FMS2との連携などに応じて動きが100以上異なり、処理が複雑になっています。 でも、今回大分悩まされた事もあって、 プレイヤーの階層などをもっとシンプルにする事は 必須事項と考えました。 いろいろ勉強になる事ばかりで、最初から効率よくシンプルに作り直す事も、逆に早道かなとも考えています。 あとは_global.obje_hitなのですが、 デバッグにより、edgeの値が取得できていない事が解りました。 これが原因かと思われますが、いろいろ考えましたが 打開策が思い当たりません。 何かアドヴァイス頂けましたら幸いです。 どうぞ宜しくお願い致します。 _global.obje_hit = function( obstacle:MovieClip ) { var ply:MovieClip , edge:Object , adjust:Number; _root.main_mc.test_txt.text="_global.obje_hit=OK!";//debug _global.hit_sound_start();//for this debug ply = _root.main_mc.room_mc.room_petbox1_mc; edge = new Object; //障害物の手前で止めるための補正値 adjust = 2; //接触した障害物の上下左右端の座標を取得 //基準はプレイヤークリップの親座標系 edge = obstacle.getBounds( ply._parent ); //edge = obstacle.getBounds( ply ); trace("edge.yMax="+edge.yMax);

  • DPE
  • ベストアンサー率85% (666/776)
回答No.1

基本的な考え方は、プレイヤーが障害物に接触した時、障害物の上下左右端の座標(障害物が占める領域の情報)を求め、連続で接触していると見なされないように少し隙間を空けて止まるというものです。 障害物の上下左右の端の座標は getBounds で求められます。 この座標から”プレイヤーの幅や高さを考慮した分だけ”移動させることで、障害物の前で立ち止まるように見えます。 例えばプレイヤーのムービークリップの基準点が中央にあるのなら、この基準点が障害物の端から離れればいいことになりますから、”プレイヤーの幅および高さの半分だけ”手前にプレイヤーの座標を戻します。(図を描いて考えてみてください) ご質問文のスクリプトでは、getBounds で求めた座標、つまり障害物の端にプレイヤーを移動していると思われますが、これでは障害物とプレイヤーが重なってしまい、接触したまま離れなくなります。 同様の理由で、getBounds で求めた端の座標からプレイヤーの大きさを考慮した分だけ離れただけでは、障害物とプレイヤーはやはりくっついたままになってしまいます。 これは前回 No.2055332 の#1で説明しました通り、少し隙間を空けて移動させることで連続して接触していると判断されないようにします。 障害物が占める領域をもとにプレイヤーの位置を決めるには、ムービークリップの階層関係が問題になります。 プレイヤーのムービークリップが何かの入れ子になっている場合、プレイヤーの座標は親の基準点を原点とした数値になっています。 従って、障害物の端の座標もプレイヤーの親にあたる座標系の数値として求めなければ、プレイヤーの移動処理には利用できません。 getBounds は基準にする座標系を指定できます。 引数にプレイヤーの親のムービークリップの参照を渡すと、プレイヤーと同じ座標系の座標として計算されて返ってきます。 この値や障害物・プレイヤーのムービークリップの座標を、localToGlobal 等の座標変換メソッドで変換する必要はありません。 ---------------------------------------------------------- 今回は処理の順番も重要なポイントです。 Flash では、2つ以上の enterFrame イベントを同時にさばくことができません。 複数存在するムービークリップに onClipEvent(enterFrame) でアクションを書き、その中に trace アクションを書いて調べてみると、同時にではなく1つずつ順番に処理されていく様子が分かります。 ざっと見たところ、処理の順番はムービークリップの階層の深さ(浅い階層のものほど先)やオブジェクトの重ね順(上に重なっているものほど先)などから決まるようです。 今回の作品では移動処理をした後で衝突判定が行われないと、位置が狂い正常に判定できなくなってしまいます。 しかし、先述の通り、実は enterFrame イベントは処理される順番が内部で決められています。 障害物に onClipEvent(enterFrame) を書くと、プレイヤーの移動処理より後に衝突判定が行われる保証はなく、先に衝突判定が行われる可能性があります。 確実に移動→衝突判定の順番に処理させるには、個々の障害物に onClipEvent(enterFrame) を書くのではなく、プレイヤーの移動処理に続けて衝突判定および接触時の後処理を済ませることです。 ********************************************* スクリプトにしてみますと、大体、次のようになります。 このスクリプトはメインのタイムラインのフレームに設定してください。 プレイヤーの基準点は中央にあるものとします。 マップはステージにある” map ”というムービークリップで、for in ループを利用しこの中にある障害物全てと当たりをとります。 for in を2重にして、2階層下まで含まれるムービークリップを列挙します。map の子の中に更に子を持っているムービークリップが含まれていても構いません。 (↓各行頭に全角のスペースが入っています。コピーする際はご注意ください)  ////////////////////////////////////////////////////////////  //障害物との接触後の処理  //引数 obstacle:接触した障害物ムービークリップの参照  ////////////////////////////////////////////////////////////  _global.obje_hit = function( obstacle:MovieClip )  {   var ply:MovieClip , edge:Object , adjust:Number;   ply = _root.main_mc.player_mc.player_mc_mc.player_mc_mc;   edge = new Object;   //障害物の手前で止めるための補正値   adjust = 2;   //接触した障害物の上下左右端の座標を取得   //基準はプレイヤークリップの親座標系   edge = obstacle.getBounds( ply._parent );   //移動方向に応じて位置調整   //連続で接触しないように、障害物の一歩手前で止める   switch( _global.key_code2 )   {    //上に移動していた時:障害物の下端で停止    case 'w':     ply._y = edge.yMax + ply._height / 2 + adjust;     break;    //下に移動していた時:障害物の上端で停止    case 's':     ply._y = edge.yMin - ply._height / 2 - adjust;     break;    //左に移動していた時:障害物の右端で停止    case 'a':     ply._x = edge.xMax + ply._width / 2 + adjust;     break;    //右に移動していた時:障害物の左端で停止    case 'd':     ply._x = edge.xMin - ply._width / 2 - adjust;     break;    default:     break;   }   //静止状態を表示   ply.gotoAndStop( 1 );  };  ////////////////////////////////////////////////////////////  //障害物との衝突判定(2階層下まで調査)  //引数 base:基準にする座標系  ////////////////////////////////////////////////////////////  _global.Hit_Check = function( base:MovieClip )  {   var ply:MovieClip , clip:String , sub:String , g_childs:Number , hit_flg:Boolean;   ply = _root.main_mc.player_mc.player_mc_mc.player_mc_mc;   //指定の階層に含まれるムービークリップを列挙   //何か1つとでも接触したら終了   hit_flg = false;   for( clip in base )   {    //基準から見て孫にあたるムービークリップの数を数える    g_childs = 0;    //子が持っているムービークリップを列挙    //何か1つとでも接触したら終了    for( sub in base[ clip ] )    {     //衝突判定     //接触時にプレイヤーを手前で立ち止まらせる     if( base[ clip ][ sub ].hitTest( ply ) )     {      _global.obje_hit( base[ clip ][ sub ] );      //ループを終了      hit_flg = true;      break;     }     //孫の数を更新     g_childs++;    }    //孫の中のどれか1つとでも接触していれば終了    if( hit_flg )    {     break;    }    //孫が1つもない時は子で衝突判定をとる    else if( ! g_childs )    {     if( base[ clip ].hitTest( ply ) )     {      _global.obje_hit( base[ clip ] );      //ループを終了      break;     }    }   }  };  ////////////////////////////////////////////////////////////  //入力キー判定処理  //////////////////////////////////////////////////////////////  key_obj = new Object();  key_obj.onKeyDown = function()  {   var code;   //入力されたキーを保存   //移動に用いられるキー以外は無視する   code = String.fromCharCode( Key.getAscii() );   if( code == 'w' || code == 's' || code == 'a' || code == 'd' )   {    _global.key_code2 = code;    //歩行アニメを再生    _root.main_mc.player_mc.player_mc_mc.player_mc_mc.play();   }  };  key_obj.onKeyUp = function()  {   //キーコードをクリア   _global.key_code2 = "";   //静止状態を表示   _root.main_mc.player_mc.player_mc_mc.player_mc_mc.gotoAndStop( 1 );  };  Key.addListener( key_obj );  ////////////////////////////////////////////////////////////  //プレイヤーの移動処理  //////////////////////////////////////////////////////////////  _root.main_mc.player_mc.player_mc_mc.player_mc_mc.onEnterFrame = function()  {   var spd:Number , i:Number , ply:MovieClip , child:Number , hit_flg:Boolean;   //移動速度を保持   spd = 15;   //押されているキーに応じて移動   switch( _global.key_code2 )   {    //上に移動    case 'w':     this._y -= spd;     break;    //下に移動    case 's':     this._y += spd;     break;    //左に移動    case 'a':     this._x -= spd;     break;    //右に移動    case 'd':     this._x += spd;     break;    default:     break;   }   //障害物との接触判定   _global.Hit_Check( _root.map );  }; ********************************************* 紙面の都合上、1つ1つを詳しくは説明できないのですが。 障害物の手前で立ち止まらせる obje_hit 関数は、前回ご紹介したものと同じです。 ただ、switch 文で判断する変数名が違うことと、プレイヤーのムービークリップのフレームを切り替える処理を入れてあります。アニメのフレームが違う場合は、適宜変更してください。 Hit_Check 関数は、前回#2で説明しました for in でムービークリップの子を列挙する部分を関数にしたものです。 指定されたムービークリップに含まれる子の、更に子(基準から見ると孫)の有無を調べ、総当たりで衝突判定をとります。 接触している障害物が見付かった時は、その時点で for in ループを打ち切ります。 予め最も末端にあたるムービークリップを調べて配列変数に格納しておけば、どのような階層構造のマップでも総当たりで衝突判定をとれるのではないかとも思うのですが、その探索ルーチンが思い付きませんでした ^^ゞ 移動処理はプレイヤーのムービークリップの enterFrame イベントを借りています。 移動処理の後に衝突判定が行われるように、この中で衝突判定の関数である Hit_Check を呼び出します。 古典的な2Dのマップですと、大体、このような形で立ち止まらせることができるかと思います。

sato777
質問者

お礼

衝突判定がうまくうきました!(><) やはり外部SWFが複雑だったので、パスに問題が あったようです。 あとは_global.obje_hit()ですが、無反応で すり抜けてしまいます・・・ ですが、諦めずじっくり考えてみたいと思います! 前回のパスの様に何か怪しい所がありましたら アドヴァイス頂けると幸いです。 どうぞ宜しくお願い致します。

sato777
質問者

補足

いつも詳しく解りやすいご説明有難う御座います。 _global.Hit_Check()なのですが、列挙できていなようです。 もみ込む外部SWFのMCにはすべて名前がつけられていないのですが、これは大丈夫なのでしょうか? また、列挙できない理由もちんぷんかんぷんです^^; あとはパスの問題しかかんがえられないのですが、 loading_mcに外部SWF(MAP)を読み込んでいるとしたら、これをターゲットとして   _global.Hit_Check( _root.loading_mc );とする 方法で間違いはないでしょうか? スクリプト全体としても、現在の実際の複雑なオーサリング内容を 単純なFlashドキュメントに根本から作り直した場合には理論的にできそうだなという所までは解る様になったのですが、まずは列挙して衝突判定させる所までもっていかないと その先の問題点以前の話になっているので、 何が原因として怪しいか、ヒントだけでも頂けないでしょうか。 現在、教えて頂いたスクリプトで動く単純なFlashドキュメントを最初から構築し、そこに現在のドキュメントを移植しようかとも考えています。 膨大で巨大な開発なので、こんな所で衝突処理に さじを投げるとは想像もしていませんでしたが、 この度、以外に厄介な案件だという事が身にしみて解りました(苦笑) DPE様の多大なお時間と労力にいつも感謝しております。 どうぞ宜しくお願い致します。

関連するQ&A