![](http://oshiete.xgoo.jp/images/v2/pc/qa/question_title.png?5a7ff87)
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) を使うというのもやってみて、
これならうまくいったのですが、
できれば配列を使うやり方を知りたいので お願いします(^^;)
No.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 …
とりあえずなんとかなったようなので、良かったですね。
こんな丁寧な説明をいただけるとは
感謝感謝です(^-^)
クロージャについて事例を調べてみたところ、
これの場合の 変数i の遷移のイメージが掴めました。
応用が利きそうです。
おかげさまで解決です。
ほんとにありがとうございます。
No.5
- 回答日時:
ANo4です。
度重ねての上に、恥の上塗り・・・(^_^;)ヤベッ
ANo2、3は無視して、以下で試してみてください。
function hantei_relief(i){
var tmp;
try{ tmp = hantei; } catch(err) { alert("スコープ外!"); };
return function(){ tmp[i] = true; }
}
No.4
- 回答日時:
ANo3です。
たびたび失礼。
あわてて書いたので、参照チェックになっていないかったですね。
function hantei_relief(i){
var tmp;
try{ tmp = hantei[i];} catch(err) { alert("スコープ外!"); };
return function(){ tmp = true; }
}
アラートが表示されるようなら、変数が参照できないか引数の値が定義外かなので、スコープ等を再チェックしてみてください。
No.3
- 回答日時:
ANo2です。
>できませんでした。
>やっぱり 500ミリ秒以内に他のボールに触れると、
>あたり判定がなくなりました。
おや、まったくあたりにならないのなら、まだしもですが。
全体像がわかってないのでなんですが、通常ならいけそうな気がするのですけれど…
念のためもう少し確実に、
function hantei_relief(i){
//ちゃんと動作したら「||」以降は削除してください。
var tmp = hantei[i] || alert("スコープ外!");
return function(){ tmp = true; }
}
でも、だめでしょうか?
(hantei[]ってグローバル変数になってますよね?)
ひょっとして、
setTimeout(hantei_relief(i), 500);
のhantei_relief(i)をクォーテーションで囲ったりしていませんよね?
>ひょっとして、
>setTimeout(hantei_relief(i), 500);
>のhantei_relief(i)をクォーテーションで囲ったりしていませんよね?
指摘ありがとうございます。まさにそうです。
囲わなくてよいのを見落としていました…。
No.2 のとおりにしたら、ちゃんと値が保持されました。
クォーテーションの有無で 何が違うのでしょうか?
ggっても全く出てきません。
もう一度お願いします。
No.1
- 回答日時:
なんともいえませんが・・・
setTimeoutから戻るタイマーIDを配列でうけとれば上書きされません
ただし、clearTimeoutが面倒かも
またはプログラムとは別にsetIntervalかなにかで裏でカウンタを走らせておいて
二つのイベント間のカウンタの差で500ミリ秒を測るとか
いろいろやりようはあるかと
回答ありがとうございます。でも解決できません…。
新しくタイマーID 例えば my_timer=new Array(tn_MaxBalls) を宣言して、
my_timer[i] = setTimeout("hantei_relief(iPlus)", 500); //500ミリ秒後に当たり判定を復活
としたのですが、やはり iPlus が上書きされてしまいます。
「setTimeoutから戻るタイマーIDを配列でうけとる」の趣旨はこれで合っていますか?
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- JavaScript 画像の表示位置 3 2022/12/23 08:25
- Visual Basic(VBA) 複数のcsvファイルをExcelに一括変換したい 2 2023/03/03 12:44
- Visual Basic(VBA) 【VBA】写真の貼り付けコードがうまく機能しません。 5 2022/09/01 18:43
- Java java 引数 戻り値のあるメソッド 3 2023/02/12 06:23
- Visual Basic(VBA) 【Excel VBA】自動メール送信の機能追加 5 2022/09/29 12:53
- Visual Basic(VBA) [Excel VBA] このコードでは行の挿入や行の消去をすると13のエラーが出てしまう。 3 2022/12/09 00:29
- JavaScript コードレビューをお願いします。 1 2022/07/16 05:38
- JavaScript スマフォではボタンを表示させたくない 2 2023/01/20 14:26
- Visual Basic(VBA) 動きっぱなしです。止め方とプロシージャの間違いを教えて下さい! 5 2022/08/15 23:08
- JavaScript 正規表現について質問です。条件に合う場合はtrueを返したい 3 2022/10/06 23:02
関連するカテゴリからQ&Aを探す
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
CreateFile、CloseHandleの繰り...
-
JavaScriptでPhotoshopのアクシ...
-
VBA ステータスバー DoEvents
-
毎日明け方4時にCGIを動かすJav...
-
VBA SORT Applyでエラー
-
if(1){...}とはどういうことで...
-
デザイン時のVisible=Falseは実...
-
1つのVBAコードをすべてのコア...
-
C#でボタン名を変更しても動く
-
VBAマクロ、パスがありませんで...
-
PowerBuilderのDOUBLEデータ型...
-
関数の呼び出しかた
-
(Netscape)Plug-in を実行制...
-
C言語のif文についての質問です...
-
マルチスレッドにする必要があ...
-
macにおけるCommon LISP以外のL...
-
innerHTMLなどの反映タイミング
-
このマクロを実行し、表示させ...
-
Schemeでのリスト操作
-
リクエスト結果が一瞬しか表示...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
JSPの処理の途中で、JavaScript...
-
デザイン時のVisible=Falseは実...
-
初心者です。gulpでコンパイル...
-
if(1){...}とはどういうことで...
-
リクエスト結果が一瞬しか表示...
-
resizeToメソッドが動作しません
-
C#でボタン名を変更しても動く
-
jQuery ui Datepicker 明日以降...
-
〔Excel:VBA〕マクロの実行が異...
-
PowerPointで時計表示
-
1つのVBAコードをすべてのコア...
-
VBA ステータスバー DoEvents
-
VBA SORT Applyでエラー
-
Excel VBA にて JavaScript の...
-
alert()が実行できない
-
GoTo文とかSelect文の処理の仕...
-
既存のwebサイトで、ローカルの...
-
一定時間ごとの実行
-
javascriptで最初のところに戻...
-
eval()の危険性の具体例を教え...
おすすめ情報