dポイントプレゼントキャンペーン実施中!

Javascriptでテニスゲームを作っています。
ボールがマウスカーソル(ラケット)に当たると跳ね返るやつです。


マウスカーソル(ラケット)とボール(複数ある。tn_MaxBalls - 1 個。)が当たってから 500ミリ秒の間は、その当たった i 番目のボールは 接触判定をなくしたいので、
当たったとき hantei[i]=false;にしてしまって、
500ミリ秒後に 関数 hantei_relief() によって
hantei[i]=true;にしようとしました。

しかし、このやり方でコードを実行すると、
あるボールAに当たって0.5秒以内に他のボールBに当たると、
iPlus == ボールBのナンバー  となってしまうので、ボールAは
hantei[iPlus]=true に戻る機会を永久に失ってしまいます。
(ずっと hantei[Aのナンバー]==false のままです。)


他のボールに当たったときに全ボールの hantei[i]=true にしてしまうのは、手軽な解決策ですが、結局500ミリ秒以内に ボールAに2回当たる場合がなくせないので、そのやり方は避けたいです。

どうすれば 500ミリ秒の間 iPlusの値を複数保持できるのでしょうか?


コード(一部)は下記のようなもので、tn_main()が 常時 繰り返し処理されています。


function tn_main(){

for(var i=0;i<tn_MaxBalls;i++)
{
if(hantei[i]==true)
{
   if((-tn_by[i]+tn_ry-2<5)&&(-tn_by[i]+tn_ry-2>-5)&&(Math.abs(tn_bx[i]-tn_rx-8)<10)&&(hantei[i]==true))
   {
    // ↑ボールとラケットの接触判定
   hantei[i] = false;
   iPlus=i;
   setTimeout("hantei_relief(iPlus)", 500); //500ms後に実行
   }
}
}
tnTimeId=setTimeout("tn_main()",tn_interval);// 割込処理

}//Main終了

function hantei_relief(){ hantei[iPlus]=true;}




一案として、iPlus = i の代わりに

if (iPlus_A_dash == false){
iPlus_A = i ; iPlus_A_dash = true;
}

//既にiPlus_Aが使われている場合
else if (iPlus_B_dash == false){
iPlus_B = i ; iPlus_B_dash = true;
}

hantei_relief_A(){hantei[iPlus_A]=true; iPlus_A_dash =false;}


と、既に使われている場合には別の (iPlus_B) を使うというのもやってみて、
これならうまくいったのですが、
できれば配列を使うやり方を知りたいので お願いします(^^;)

A 回答 (6件)

ANo2~5です。


ひとまず無事に動作したようなので、あわてて書いた3~5は無いものにしてください(汗)


>クォーテーションの有無で 何が違うのでしょうか?
setTimeout()には概ね2種類の書式があります。
 https://developer.mozilla.org/ja/docs/Web/API/Wi …

ご質問文でご提示のクォーテーションで囲う書式は、第一引数に文字列を渡す書式で、実行する時に文字列がスクリプトとして評価されます。(実は非推奨です)
それなので、変数の値なども実行時に取られている値になります。
ということは、500ms経過後のインデックス(iやiPlus)の値が採用されることになるので、その結果、ご質問のような状況が生まれていたことになるかと。

ANo2で提示したものは
 setTimeout(func, delay)
形式の書式で、指定時間経過後にfunc()が実行されるという書式ですが、変数の参照は実行時に行われるので、そのまま関数内でiやiPlusを参照すると同じことが起こってしまい、実は、解決にはなりません。(書式を変えただけ)

ANo2は上記の書式ではありますが、第一引数が hantei_relief ではなく、hantei_relief(i)となっているところがミソで、setTimeoutの構文解釈時に hantei_relief(i)が評価されます。(この時点のiの値は使用して欲しい値をまだ持っていることはわかりますよね)
そして、hantei_relief(i)を実行して返される匿名関数 function(){ hantei[i] = true; } が500ms後に実行すべき関数として保持されることになります。

えっ、でも結局、同じことでは?・・・と思われるかも知れません。
(変数iを重複して使ってしまったので、分かりにくくなってしまっていて申し訳ない)
実はこちらのiは、hantei_relief(i)を実行した際にすでに評価しているので、関数hantei_relief内のローカル変数になっていて、匿名関数を実行する際にも実行した時の値を保持しているのです。
 function hantei_relief(jj){
  return function(){ hantei[jj] = true; }
 }
と置き換えて考えていただいた方が、多少は理解しやすいかもしれません。
main側でiやiPlusの値を変えても、匿名関数で参照するi(あるいはjj)は別の変数なので影響をうけないのです。

このような仕組みを内包(クロージャ)というらしいですが、私もたいして理解しているわけではないので、正しい説明ができないのと、誤解を与えてしまってはいけないので…
以下あたりをとっかかりに調べてみてください。
(javascriptの場合は、厳密な意味ではクロージャではないらしいですが…)
 http://www.atmarkit.co.jp/ait/articles/0708/21/n …

とりあえずなんとかなったようなので、良かったですね。
    • good
    • 0
この回答へのお礼

こんな丁寧な説明をいただけるとは
感謝感謝です(^-^)

クロージャについて事例を調べてみたところ、
これの場合の 変数i の遷移のイメージが掴めました。
応用が利きそうです。

おかげさまで解決です。
ほんとにありがとうございます。

お礼日時:2015/07/13 18:28

ANo4です。




度重ねての上に、恥の上塗り・・・(^_^;)ヤベッ


ANo2、3は無視して、以下で試してみてください。

function hantei_relief(i){
var tmp;
try{ tmp = hantei; } catch(err) { alert("スコープ外!"); };
return function(){ tmp[i] = true; }
}
    • good
    • 0

ANo3です。




たびたび失礼。
あわてて書いたので、参照チェックになっていないかったですね。

function hantei_relief(i){
var tmp;
try{ tmp = hantei[i];} catch(err) { alert("スコープ外!"); };
return function(){ tmp = true; }
}

アラートが表示されるようなら、変数が参照できないか引数の値が定義外かなので、スコープ等を再チェックしてみてください。
    • good
    • 0

ANo2です。




>できませんでした。
>やっぱり 500ミリ秒以内に他のボールに触れると、
>あたり判定がなくなりました。
おや、まったくあたりにならないのなら、まだしもですが。
全体像がわかってないのでなんですが、通常ならいけそうな気がするのですけれど…


念のためもう少し確実に、
function hantei_relief(i){
//ちゃんと動作したら「||」以降は削除してください。
var tmp = hantei[i] || alert("スコープ外!");
return function(){ tmp = true; }
}

でも、だめでしょうか?
(hantei[]ってグローバル変数になってますよね?)


ひょっとして、
setTimeout(hantei_relief(i), 500);
のhantei_relief(i)をクォーテーションで囲ったりしていませんよね?
    • good
    • 0
この回答へのお礼

>ひょっとして、
>setTimeout(hantei_relief(i), 500);
>のhantei_relief(i)をクォーテーションで囲ったりしていませんよね?

指摘ありがとうございます。まさにそうです。
囲わなくてよいのを見落としていました…。
No.2 のとおりにしたら、ちゃんと値が保持されました。

クォーテーションの有無で 何が違うのでしょうか?
ggっても全く出てきません。
もう一度お願いします。

お礼日時:2015/07/13 14:03

今試せる環境にないので、机上の回答ですが・・・



呼び出し側を、
setTimeout(hantei_relief(i), 500);
として、

関数のほうを、
function hantei_relief(i){
return function(){ hantei[i] = true; }
}
のようにすることで、固定できませんか?
    • good
    • 0
この回答へのお礼

できませんでした。
やっぱり 500ミリ秒以内に他のボールに触れると、
あたり判定がなくなりました。

お礼日時:2015/07/13 09:01

なんともいえませんが・・・


setTimeoutから戻るタイマーIDを配列でうけとれば上書きされません
ただし、clearTimeoutが面倒かも

またはプログラムとは別にsetIntervalかなにかで裏でカウンタを走らせておいて
二つのイベント間のカウンタの差で500ミリ秒を測るとか
いろいろやりようはあるかと
    • good
    • 0
この回答へのお礼

回答ありがとうございます。でも解決できません…。

新しくタイマーID 例えば my_timer=new Array(tn_MaxBalls) を宣言して、

my_timer[i] = setTimeout("hantei_relief(iPlus)", 500); //500ミリ秒後に当たり判定を復活

としたのですが、やはり iPlus が上書きされてしまいます。
「setTimeoutから戻るタイマーIDを配列でうけとる」の趣旨はこれで合っていますか?

お礼日時:2015/07/11 14:34

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!