24

Javascript コードに小さなサイコロを振る効果を追加したいと考えています。メソッドを使用するのが良い方法だと思いますsetInterval()。私の考えは次のコードでした(テストのためだけに):

function roleDice() {
    var i = Math.floor((Math.random() * 25) + 5);
    var j = i;
    var test = setInterval(function() {
        i--;
        document.getElementById("dice").src = "./images/dice/dice" + Math.floor((Math.random() * 6) + 1) + ".png";
        if (i < 1) {
            clearInterval(test);
        }

    }, 50);
}

setInterval が完了するまで待ちたいと思います。そこで、setTimeout を追加しました。

setTimeout(function(){alert("test")}, (j + 1) * 50);

このコードは問題なく動作します。しかし、私のメイン コードでは、roleDice()関数は値を返します。どうやってそれを処理できるかわかりません... から戻ることはできませんsetTimeout()。関数の最後に return を追加すると、return が高速になります。どうすればそれを修正できますか?

編集 うーん、コールバックの量を理解しました。それがどのように機能するかはわかっていると思いますが、まだ問題があります。それはもっと「インターフェース」の問題だと思います...ここに私のコード:

function startAnimation(playername, callback) {
    var i = Math.floor((Math.random() * 25) + 5);
    var int = setInterval(function() {
        i--;
        var number = Math.floor((Math.random() * 6) + 1);
        document.getElementById("dice").src = "./images/dice/dice" + number + ".png";
        if(i < 1) {
            clearInterval(int);
            number = Math.floor((Math.random() * 6) + 1);
            addText(playername + " rolled " + number);
            document.getElementById("dice").src = "./images/dice/dice" + number + ".png";
            callback(number);
        }
    }, 50);
}

function rnd(playername) {
    var callback = function(value){
        return value; // I knew thats pointless...
    };
    startAnimation(playername, callback);
}

関数rnd()は待機して値を返す必要があります... 少し混乱しています。現時点では、どうすればいいのかわかりません...コードは待ってvar callback...いますが、どうすればリターンと組み合わせることができますか? rnd()アニメーションを実行し、その後最後の数字を他の関数に返したいと思います。

4

5 に答える 5

46

あなたは、非同期プログラミングに触れたときにほとんどの人が陥る落とし穴に出くわしました。

タイムアウト/間隔が終了するのを「待つ」ことはできません。そうしようとすると、機能しないか、ページ/ブラウザー全体がブロックされます。遅延後に実行する必要があるコードは、setInterval「完了」したときに渡したコールバックから呼び出す必要があります。

function rollDice(callback) {
    var i = Math.floor((Math.random() * 25) + 5);
    var j = i;
    var test = setInterval(function() {
        i--;
        var value = Math.floor((Math.random() * 6) + 1);
        document.getElementById("dice").src = "./images/dice/dice" + value + ".png";
        if(i < 1) {
            clearInterval(test);
            callback(value);
        }
    }, 50);
}

次に、次のように使用します。

rollDice(function(value) {
    // code that should run when the dice has been rolled
});
于 2012-06-15T17:38:54.953 に答える
2

元のコードはすべてシーケンシャルでした。これは基本的なサイコロ ゲームで、2 人のプレーヤーが 1 つを転がし、どちらが大きいかを確認します。【同点の場合は二人目の勝ち!】

function roleDice() {
    return Math.floor(Math.random() * 6) + 1;
}

function game(){    
    var player1 = roleDice(),
        player2 = roleDice(),
        p1Win = player1 > player2;
    alert( "Player " + (p1Win ? "1":"2") + " wins!" );
}

game();

上記のコードは流れるだけなのでとてもシンプルです。サイコロを振るような非同期メソッドを入れると、処理を行うために物事をチャンクに分割する必要があります。

function roleDice(callback) {
    var i = Math.floor((Math.random() * 25) + 5);   
    var j = i;
    var test = setInterval(function(){
        i--;
        var die =  Math.floor((Math.random() * 6) + 1);
        document.getElementById("dice").src = "./images/dice/dice" + die + ".png";
        if(i < 1) {
                clearInterval(test);
                callback(die);  //Return the die value back to a function to process it
            }
        }, 50);
}

function game(){
    var gameInfo = {  //defaults
                       "p1" : null,
                       "p2" : null
                   },
        playerRolls = function (playerNumber) { //Start off the rolling
            var callbackFnc = function(value){ //Create a callback that will 
                playerFinishes(playerNumber, value); 
            };
            roleDice( callbackFnc );
        },
        playerFinishes = function (playerNumber, value) { //called via the callback that role dice fires
            gameInfo["p" + playerNumber] = value;
            if (gameInfo.p1 !== null && gameInfo.p2 !== null ) { //checks to see if both rolls were completed, if so finish game
                giveResult();
            }
        },
        giveResult = function(){ //called when both rolls are done
            var p1Win = gameInfo.p1 > gameInfo.p2;
            alert( "Player " + (p1Win ? "1":"2") + " wins!" );
        };            
    playerRolls("1");  //start player 1
    playerRolls("2");  //start player 2
}

game();

上記のコードは、より OOP タイプの方法で改善される可能性がありますが、機能します。

于 2012-06-15T18:34:00.020 に答える
1

上記の解決策が機能するには、いくつかの問題があります。プログラムを実行しても (少なくとも私の好みのブラウザでは) 画像が表示されないため、ゲームを実行する前に画像をロードする必要があります。

また、経験から、N 個の画像をプリロードしたり、N 人のプレイヤーにサイコロを投げさせたりする場合にコールバック メソッドを開始する最善の方法は、各タイムアウト関数でゼロまでカウントダウンを行い、その時点でコールバックを実行することです。これは魔法のように機能し、処理する必要があるアイテムの数に依存しません。

<html><head><script>
var game = function(images){
   var nbPlayers = 2, winnerValue = -1, winnerPlayer = -1;
   var rollDice = function(player,callbackFinish){
      var playerDice = document.getElementById("dice"+player);
      var facesToShow = Math.floor((Math.random() * 25) + 5);   
      var intervalID = setInterval(function(){
         var face =  Math.floor(Math.random() * 6);
         playerDice.src = images[face].src;
         if (--facesToShow<=0) {
            clearInterval(intervalID);
            if (face>winnerValue){winnerValue=face;winnerPlayer=player}
            if (--nbPlayers<=0) finish();
         }
      }, 50);
   }
   var finish = function(){
      alert("Player "+winnerPlayer+" wins!");
   }      
   setTimeout(function(){rollDice(0)},10);
   setTimeout(function(){rollDice(1)},10);
}
var preloadImages = function(images,callback){
   var preloads = [], imagesToLoad = images.length;
   for (var i=0;i<images.length;++i){
      var img=new Image();
      preloads.push(img);
      img.onload=function(){if(--imagesToLoad<=0)callback(preloads)}
      img.src = images[i];
   }
}
preloadImages(["dice1.png","dice2.png","dice3.png","dice4.png","dice5.png","dice6.png"],game);
</script></head><body>
<img src="" id="dice0" /><img src="" id="dice1" /></body></html>
于 2012-07-10T00:03:47.690 に答える