简体   繁体   中英

Player interaction in a while javascript

i've encountered a few errors while working on a round by round script for a game on browser, sorry for my spelling mistakes.

I have a function with a while separated in three parts as the following (full javascript):

function turn(player, monster){
    //player and monster are object

    turns=2;
    //few other unimportant variable
    while(//as long as they have >0 hp){
        
        if(turns==2){
             //listen to click on html to know if player used a spell
             // if he didn't in a few seconds, then he'll just use a normal 
             // attack
             startingTurn = settimeout(startTurn, 2000);
             sleep(2000);
             turns=1;
        }
        if(turns==1 ){
             //Turn of the player (working perfectly)
             ...
             turns=0;
        }
        if(turns==0 and hp>0){
             //Turn of the monster (working perfectly)
             ...
             turns=2;
        }
    }
 }

The problem that i've encountered is that in the listening of an action, i have to set a pause so that the player will have time to click on one of the spell.

To do this, i've tryed using "sleep" function to let 2 seconds for the player to act and a settimeout() to set a normal attack if there were no spell clicked.

The sleep function that i used looked like this one: https://www.sitepoint.com/delay-sleep-pause-wait/

Both those methods gave me errors:

-The settimeout() were effective only after the whole script(while) was finished (all at once).

-The "sleep" function caused the whole page to freeze, not only the while but also the html wasn't clickable.

So I'm looking for a way of leting the player interact while the function is running, if this isn't possible i will probably have to give up the interaction part.

I've looked online for quite a long time and didn't found any solution so i hope you can help me there!.

EDIT

I have tried to use the methods from Jacob (with async function and a loop() )because it was what looks the most clear for me.

But then i found the same first problem wich is that the settimeout() are executed after the loop() function here's where i think the problem is:

 function attaqueBasic (attacker, defender) {
  console.log("basic attack");
  return new Promise((res, rej) => {
    setTimeout(() => {
        var dodge = dodge(defender);
        if(dodge == false){
            console.log("attack damage :"+attacker.dmg);
            defender.life = defender.life - attacker.dmg;
            console.log("life defender: "+defender.vie);
            if(attacker.hasOwnProperty("bonus")==true && defender.life>0){
                attackBonus(attacker, defender);
            }
        }
        res()
    }, 2000);
  })

} This is the function that let the player do a basic attack but it doesn't wait for the resolve to continue.

In the console i find the "basic attack" while the loop is running but never the following wich is in the settimeout and is executed only after all loops are done. Sorry if i made a big mistake here.

Here's the function loop() just in case i made some mistake:

function loop(perso, monstre){
    nbtour += 1
    if(nbtour>=6){
        nbtour=0;
        return;
    }
    console.log('---New tour'+nbtour+'---');
    Turn(perso,monstre)
        .then(checkWin(perso,monstre))
        .then(Turn(monstre,perso))
        .then(checkWin(perso,monstre))
        .then(loop(perso, monstre))
        .catch(() => {
            console.log('End of combat, number of tours ='+nbtour);
            nbtour=0;
        })

}

Thanks for your time.

I assume this is on a browser or similar platform.

You can't have reasonably have meaningful input from the user within a while loop (I'm intentionally ignoring the unreasonable prompt function). Instead, handle an event that occurs when a player moves, and have that event handler process the changes the event causes. At the end of the handler's logic, check what used to be the loop termination condition ("as long as they have >0 hp") and if the termination condition has been reached, modify the page to say that the game is over or whatever (and probably disable the buttons).

For instance, here's a simple example using number guessing:

 var number = Math.floor(Math.random() * 10) + 1; var guesses = 3; var btn = document.getElementById("guess"); var input = document.getElementById("guess-value"); btn.addEventListener("click", function() { var value = input.value.trim(); if (!value) { guessError("Please fill in a value"); return; } var guess = +value; if (isNaN(guess)) { guessError("Please only fill in simple numbers"); return; } if (guess < 1 || guess > 10) { guessError("What part of 'between 1 and 10, inclusive' did you not understand? ;-)"); return; } if (guess === number) { console.log("Congrats! You won!"); input.disabled = btn.disabled = true; } else { console.log("Nope, it's not " + guess); if (--guesses === 0) { console.log("You're out of guesses, the computer wins."); input.disabled = btn.disabled = true; } else { input.value = ""; input.focus(); } } }); console.log("Guess a whole number between 1 and 10 inclusive"); input.focus(); function guessError(msg) { console.log(msg); input.focus(); }
 <input type="text" id="guess-value"> <input type="button" id="guess" value="Guess">

That's very quick-and-dirty, but the key thing is that instead of a while loop with the number of times the user is allowed to guess, we just respond to them guessing and count the number of times they guess, and change the state of the page when they run out (or win).

First, we can not use while for game loop, because it will freeze browser, user would can not interact with game.

We will use recursion , something like this:

function loop () {
  loop()
}

Second, we have to use async to stop loop to wait user operation, otherwise the recursion would have no difference with iteration , something like this:

setTimemout(attack, 2000)

Third, we have to chain all of the code in loop to make it run one by one, because they are async , otherwise the order cann't be guaranteed, like this:

firstAttack()
  .then(secondAttack)
  .then(thirdAttack)
  .catch(gameOver())

Here is a simple demo.

 const php = document.querySelector('.p-hp') const mhp = document.querySelector('.m-hp') const output = document.querySelector('.output') const fireballEl = document.querySelector('.fireballEl') let isAbility = false let hp = '' fireballEl.addEventListener('click' , e => { isAbility = true fireballEl.disabled = true e.preventDefault() }) function playerTurn () { if (isAbility) { isAbility = false return fireball() } return slash() } function slash () { return new Promise((res, rej) => { setTimeout(() => { const dmg = getRandomInt(1, 200) hp.m -= dmg if (hp.m < 0) { hp.m = 0 } mhp.textContent = hp.m output.innerHTML += `<li class="player-info">You slashed monster which damaged ${dmg} hp</li>` res() }, 2000); }) } function fireball () { return new Promise((res, rej) => { setTimeout(() => { const dmg = getRandomInt(260, 400) hp.m -= dmg if (hp.m < 0) { hp.m = 0 } mhp.textContent = hp.m fireballEl.disabled = false output.innerHTML += `<li class="player-info ability">You thrown a fireball to monster which damaged ${dmg} hp</li>` res() }, 2000); }) } function bite () { return new Promise((res, rej) => { setTimeout(() => { const dmg = getRandomInt(6, 20) hp.p -= dmg if (hp.p < 0) { hp.p = 0 } php.textContent = hp.p output.innerHTML += `<li class="monster-info">Monster bite you for ${dmg} hp</li>` res() }, 2000); }) } function check () { if (hp.p <= 0) { output.innerHTML += '<li class="lose">System terminated, rebuild enviroment, monster is ready,experiment can be continued... </li>' return Promise.reject(1) } if (hp.m <= 0) { output.innerHTML += '<li class="win">Hooray! Now the princess is yours.</li>' return Promise.reject(0) } return Promise.resolve(1) } function init () { output.innerHTML = '' php.textContent = 100 mhp.textContent = 1000 return { p: 100, m: 1000 } } function updateDom () { php.textContent = hp.p mhp.textContent = hp.m } function loop () { output.innerHTML += '<li class="bar">=====================</li>' playerTurn() .then(updateDom) .then(check) .then(bite) .then(updateDom) .then(check) .then(loop) .catch(() => { startEl.disabled = false fireballEl.disabled = true }) } function getRandomInt(min, max) { min = Math.ceil(min) max = Math.floor(max) return Math.floor(Math.random() * (max - min)) + min } const startEl = document.querySelector('.start') startEl.addEventListener('click', e => { hp = init() fireballEl.disabled = false startEl.disabled = true loop() })
 * { margin: 0; padding: 0; } ul { list-style: none; } .stage { overflow:hidden; margin: 0 auto; max-width: 600px; padding: 15px; } .player, .monster, .split { width: 33%; float: left; } .split { font-size: 50px; font-weight: 900; } h1 { font-size: 16px; margin-top: 0; } .output { max-width: 600px; margin: 0 auto; border-top: 1px solid #333; } .output .player-info { background-color: forestgreen; color: #333; } .output .monster-info { background-color:fuchsia; color: #333; } .output .bar { color: #eee; /* margin: 3px 0; */ } .output .ability { background-color:yellow; font-size: 16px; } .output .win { font-size: 30px; } .output .lose { font-size: 30px; }
 <div class="stage"> <div class="player"> <h1>Self-confident Player</h1> <p>HP: <span class="p-hp">100</span></p> <div class="abl"> Spell: <button class="fireballEl" disabled>Inaccurate fireball</button> </div> </div> <div class="split"> VS <div> <button class="start">Start to fight</button> </div> </div> <div class="monster"> <h1>Young Monster</h1> <p>HP: <span class="m-hp">1000</span></p> </div> </div> <ul class="output"> </ul>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM