在函数调用之前执行非函数代码 [英] Execute non-function code before a function call

查看:34
本文介绍了在函数调用之前执行非函数代码的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个简单的 Vue 游戏应用程序,它带有按钮,当单击按钮时,可以在一定范围内以随机数降低玩家或恶魔"的健康状况.在我的 Vue 实例中,有一个方法 attack 计算伤害并检查游戏是否结束(如果它导致玩家或恶魔的健康状况为零/低于零).目前,如果它等于或低于零,则调用 JS 内置的确认函数来宣布玩家赢或输,并询问他们是否要开始新游戏.但是,我想首先检查玩家或恶魔的生命值是否 <= 0,如果是,则将生命值设置为 0,这样它就会反映在生命值条"中,因为现在它会显示它之前的状态/低于0.

 checkWin: function() {如果(this.demonHealth <= 0){//this.demonHealth = 0;<-- 在 confirm() 之后应用;if (confirm('你赢了!新游戏?')) {this.startGame();} 别的 {this.gameIsRunning = false;}返回真;} 别的 {//this.playerHealth = 0;<-- 在 confirm() 之后应用;如果(this.playerHealth <= 0){if (confirm('你输了.新游戏?')) {this.startGame();} 别的 {this.gameIsRunning = false;}返回真;}}}

完整代码:https://codepen.io/nataliecardot/pen/XWrMedm

这是完整的 Vue 实例:

new Vue({el: '#app',数据: {玩家健康:100,恶魔健康:100,游戏正在运行:假},方法: {开始游戏:函数(){this.gameIsRunning = true;this.playerHealth = 100,this.demonHealth = 100},攻击:函数(){this.demonHealth -= this.calculateDamage(3, 10);如果 (this.checkWin()) {返回;}this.playerHealth -= this.calculateDamage(5, 12);this.checkWin();},计算伤害:函数(最小,最大){返回 Math.max(Math.floor(Math.random() * max) + 1, min);},checkWin:函数(){如果(this.demonHealth <= 0){//this.demonHealth = 0;<-- 在 confirm() 之后应用;if (confirm('你赢了!新游戏?')) {this.startGame();} 别的 {this.gameIsRunning = false;}返回真;} 别的 {//this.playerHealth = 0;<-- 在 confirm() 之后应用;如果(this.playerHealth <= 0){if (confirm('你输了.新游戏?')) {this.startGame();} 别的 {this.gameIsRunning = false;}返回真;}}}}});

我还尝试删除 checkWin() 函数,添加一个新方法来检查分数是否 <= 0,如果是,则将其设置为零,并在回调中使用 checkWin() 中的代码.这导致应用了零,但没有调用 confirm(),游戏继续进行:

方法:{开始游戏:函数(){this.gameIsRunning = true;this.playerHealth = 100,this.demonHealth = 100},评分检查:函数(){如果(this.demonHealth <0){this.demonHealth = 0;}如果(this.playerHealth <0){this.playerHealth = 0;}},攻击:函数(){this.demonHealth -= this.calculateDamage(3, 10);this.scoreCheck(() => {如果(this.demonHealth === 0){if (confirm('你赢了!新游戏?')) {this.startGame();} 别的 {this.gameIsRunning = false;}返回;}});this.playerHealth -= this.calculateDamage(5, 12);this.scoreCheck(() => {如果(this.playerHealth === 0){if (confirm('你输了.新游戏?')) {this.startGame();} 别的 {this.gameIsRunning = false;}}});},计算伤害:函数(最小,最大){返回 Math.max(Math.floor(Math.random() * max) + 1, min);},}

我也在原始代码上尝试了 $nextTick(),但它的工作方式与以前相同:

checkWin: function() {如果(this.demonHealth <= 0){this.demonHealth = 0;this.$nextTick(function() {if (confirm('你赢了!新游戏?')) {this.startGame();} 别的 {this.gameIsRunning = false;}返回真;});} 别的 {如果(this.playerHealth <= 0){this.playerHealth = 0;this.$nextTick(function() {if (confirm('你输了.新游戏?')) {this.startGame();} 别的 {this.gameIsRunning = false;}返回真;});}}}

解决方案

实际上我想将其标记为重复 - 但后来我意识到大多数解决方案都建议使用 setTimout - 这正在创建一个javascript 和浏览器渲染之间不需要的竞争条件.

当您在 vue 范围内改变对象属性时(意味着它们是反应性的)并且您想等待 dom 更新和 dom 渲染,您可以执行以下操作:

首先 await vm.$nextTick() 它将计算 dom然后用 double requestAnimationFrame 给浏览器喘息的时间.

作为一个实现示例:

Vue.skipFrame = function(){返回新的承诺(解决 => {requestAnimationFrame(() =>请求动画帧(解决))})}让应用程序 = 新 Vue({el: '#app',数据: {怪物:10,英雄:100},方法: {攻击() {this.monster = Math.max(this.monster - (Math.random() * 10)|0,0)this.checkWin()},异步 checkWin(){等待这个.$nextTick()等待 Vue.skipFrame()if(this.monster == 0){确认('你赢了!又一次?')?this.start():this.quit()}},开始() {this.hero = 100;this.monster = 100;},退出(){this.monster = 1000;}}});

.as-console-wrapper{高度:0px!重要}

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script><div id="应用程序"><h1>怪物生命值:{{monster}}</h1><h1>英雄生命值:{{hero}}</h1><button @click="attack">Attack</button>

I'm working on a simple Vue game app with buttons to decrease player or "demon" health by a random number within a range when a button is clicked. In my Vue instance is a method attack that calculates the damage and checks if the game is over (if it results in the player or demon's health going at/below zero). Currently if it's at or below zero, the JS built-in confirm function is called to announce the player won or lost and asking them if they want to start a new game. However, I'd like to first check if player or demon health <= 0 and if so, set health to 0 so it will be reflected in a "health bar" because right now it displays what it was before it went to at/below 0.

 checkWin: function() {
      if (this.demonHealth <= 0) {
        // this.demonHealth = 0; <-- applied after confirm();
        if (confirm('You won! New game?')) {
          this.startGame();
        } else {
          this.gameIsRunning = false;
        }
        return true;
      } else {
        // this.playerHealth = 0; <-- applied after confirm();
        if (this.playerHealth <= 0) {
          if (confirm('You lost. New game?')) {
            this.startGame();
          } else {
            this.gameIsRunning = false;
          }
          return true;
        }
      }
    }

Full code: https://codepen.io/nataliecardot/pen/XWrMedm

Here's the full Vue instance:

new Vue({
  el: '#app',
  data: {
    playerHealth: 100,
    demonHealth: 100,
    gameIsRunning: false
  },
  methods: {
    startGame: function() {
      this.gameIsRunning = true;
      this.playerHealth = 100,
      this.demonHealth = 100
    },
    attack: function() {
      this.demonHealth -= this.calculateDamage(3, 10);
      if (this.checkWin()) {
        return;
      }

      this.playerHealth -= this.calculateDamage(5, 12);
      this.checkWin();
    },
    calculateDamage: function(min, max) {
      return Math.max(Math.floor(Math.random() * max) + 1, min);
    },
    checkWin: function() {
      if (this.demonHealth <= 0) {
        // this.demonHealth = 0; <-- applied after confirm();
        if (confirm('You won! New game?')) {
          this.startGame();
        } else {
          this.gameIsRunning = false;
        }
        return true;
      } else {
        // this.playerHealth = 0; <-- applied after confirm();
        if (this.playerHealth <= 0) {
          if (confirm('You lost. New game?')) {
            this.startGame();
          } else {
            this.gameIsRunning = false;
          }
          return true;
        }
      }
    }
  }
});

I also tried removing the checkWin() function, adding a new method that checks for the score being <= 0 and if so setting it to zero, and having the code that was in checkWin() in a callback. This resulted in zero being applied, but confirm() wasn't called, and the game kept going:

methods: {
    startGame: function() {
      this.gameIsRunning = true;
      this.playerHealth = 100,
      this.demonHealth = 100
    },
    scoreCheck: function() {
      if (this.demonHealth < 0) {
        this.demonHealth = 0;
      }
      if (this.playerHealth < 0) {
        this.playerHealth = 0;
      }
    },
    attack: function() {
      this.demonHealth -= this.calculateDamage(3, 10);
      this.scoreCheck(() => {
        if (this.demonHealth === 0) {
          if (confirm('You won! New game?')) {
            this.startGame();
          } else {
            this.gameIsRunning = false;
          }
          return;
        }
      });

      this.playerHealth -= this.calculateDamage(5, 12);
      this.scoreCheck(() => {
        if (this.playerHealth === 0) {
          if (confirm('You lost. New game?')) {
            this.startGame();
          } else {
            this.gameIsRunning = false;
          }
        }
      });
    },
    calculateDamage: function(min, max) {
      return Math.max(Math.floor(Math.random() * max) + 1, min);
    },
  }

Edit: I also tried $nextTick() on the original code, but it worked the same as before:

checkWin: function() {
  if (this.demonHealth <= 0) {
    this.demonHealth = 0;
    this.$nextTick(function() {
      if (confirm('You won! New game?')) {
        this.startGame();
      } else {
        this.gameIsRunning = false;
      }
      return true;
    });
  } else {
    if (this.playerHealth <= 0) {
      this.playerHealth = 0;
      this.$nextTick(function() {
        if (confirm('You lost. New game?')) {
          this.startGame();
        } else {
          this.gameIsRunning = false;
        }
        return true;
      });
    }
  }
}

解决方案

Actually I wanted to mark this as duplicate - but then i realized that most solutions suggest the usage of setTimout - wich is creating an unneeded racecondition between javascript and browser rendering.

When you mutatate object properties in the scope of vue - meaning they are reactive - and you want to wait for a dom update and a dom render there is following you can do:

First await vm.$nextTick() which will calculate the dom and then give the browser time to breath with double requestAnimationFrame.

And as an implementation example:

Vue.skipFrame = function(){

  return new Promise(resolve => {
    requestAnimationFrame(() => 
    requestAnimationFrame(resolve)
    )
  })
}
let app = new Vue({
  el: '#app',
  data: {
    monster: 10,
    hero: 100
  },

  methods: {
    attack() {
      this.monster = Math.max(
        this.monster - (Math.random() * 10)|0, 
        0
      )
      
      
      this.checkWin()
    },
    async checkWin(){
      await this.$nextTick()
      await Vue.skipFrame()
      if(this.monster == 0){
        confirm('You won! Again?') ?
        this.start():
        this.quit()
      }
    },
    start() {
      this.hero = 100;
      this.monster = 100;
    },
    quit(){
      this.monster = 1000;
      
    }
  }

  });

.as-console-wrapper{
  height: 0px !important
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  
  <h1>Monster HP: {{monster}}</h1>
  <h1>Hero HP: {{hero}}</h1>
  <button @click="attack">Attack</button>
</div>

这篇关于在函数调用之前执行非函数代码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆