专栏首页Coding迪斯尼VUE+WebPack精美游戏设计:实现微信红包铜钱转动特性和页面数据的本地存储

VUE+WebPack精美游戏设计:实现微信红包铜钱转动特性和页面数据的本地存储

上一节,我们完成了建筑物的动态生成效果。在三种建筑物中,有一种建筑物,也就是商店,一旦它生成后,能产生一种特殊效果,那就是有一个钻石精灵会动态的漂浮在它下方,一旦用户点击后,玩家的钻石数量可以相应增加。本节的主要目的就是实现浮动精灵的动画特效,完成本节代码后,效果如下:

商店下方的钻石图片在页面上回呈现出反复转动的动态效果,具体特效请点击‘阅读原文’参看视频:

大家在接收微信红包时,点开红包后就会看到有一个空心铜钱转了好几圈后才出现红包里面的金额,上面实现的就是铜钱转圈的特效。

在实现上述效果前,我们需要对代码做些许改动。当玩家点击右下角的Building按钮时,弹出来的选择面板,必须根据用户当前的钱币数和人口数来决定哪一种建筑物是可以建造的,我们以前的代码并没有考虑到这一点,先打开buildingpanelcomponent.vue进行相应的代码修改:

export default { 
    data () { 
      return { 
      .... 
      buttonsMap: {} 
      } 
    }, 
   .... 
   decideWhetherButtonDisableOrNot () { 
        // 判断当前是否有足够的钱币,电量和人口去建造建筑物 
        for (var i = 0; i < 3; i++) { 
          var b = this.buildings[i] 
          var hasEnoughPowerSupplies = Constant[b.name].needPopulations === 0 || (this.gameSceneComponent.powerSupplies - this.gameSceneComponent.populations >= Constant[b.name].needPopulations) 

          var hasEnoughCoins = (this.gameSceneComponent.coins >= Constant[b.name].needCoins) 
          var button = this.buttonsMap[b.name].enableButton 
          var buttonDisabled = this.buttonsMap[b.name].disableButton 

          if (hasEnoughPowerSupplies && hasEnoughCoins) { 
            button.visible = true 
            buttonDisabled.visible = false 
          } else { 
            button.visible = false 
            buttonDisabled.visible = true 
          } 
        } 
      }, 
      setupBuildingButton (i) { 
      .... 
     // change here 
      var buttons = {} 
      var _this = this 
      // change here 
      buttons['enableButton'] = button 
      .... 
      // change here 
      buttons['disableButton'] = buttonDisabled 
      this.buttonsMap[b.name] = buttons 
      } 
}

玩家在点击右下角的Building按钮,打开建筑物选择面板时,程序必须根据玩家当前的人口数,能源数以及钱币数来决定到底哪种建筑物是允许玩家选择的。上面代码中的decideWhetherButtonDisableOrNot所实现的逻辑就是做相应的判断,它计算玩家当前的各种资源,然后跟建筑物需要的资源数相比对,如果满足条件,那么在控制面板的相应建筑物按钮上就会有一个Build按钮,如果资源不满足,那么相应建筑物上就没有Build按钮。

setupBuildingButton用来实现控制面板上的按钮,它先为每个建筑物设计两种按钮,当建筑物可以建造时,采用代码中button对应的按钮对象,当建筑物不可以建造时采用代码中的disableButton对象。上面的代码完成后,每次点击Building按钮,控制面板显示出来的建筑物总会根据当前的资源条件来表明建筑物是否可以被玩家选择:

接着我们要实现的是城市的资源生长逻辑。建造一个发电厂,游戏就可以增加人口数量,每隔一定时间,城市的钱币数就可以自动增加,如果建造了造钱厂,那么钱币增加的时间间隔就会减少,如果建筑了商城,那么当钱币数目超过200,并且经过一定的时间间隔后,商城旁边就会跳动出一个旋转的钻石,点击钻石,城市的钻石数目就可以增加。由此回到gamescenecomponent.vue中增加相应代码:

export default { 
    data () { 
      return { 
      .... 
      // change here 
        // 经过800时间单位能创造一个钻石 
        // change here for test 
        tickCountForMerchantDiamond: 800, 
        // 200金币换一个钻石 
        coinsNeededForDiamond:  200, 
        // 每隔90个时间单位增加一个金币 
        coinGenerationCountDown: 90, 
        // 当前总共增加几个金币 
        coinGenerationCount: 0 
        } 
    } 
    .... 
    tick () { 
    .... 
    this.coinsTick() 
    .... 
    } 
    .... 
    calculateBuildingsEffects () { 
        // 根据当前建筑物,计算金币,钻石和人口数量 
        this.powerSupplies = 10 
        this.populations = 0 
        this.coinGenerationCountDown = 90 

        for (var i = 0, len = this.buildingList.length; i < len; i++) { 
          var b = this.buildingList[i] 
          var data = Constant[b.name] 
          // 计算总人口 
          this.evaluatePopulation(data.needPopulations) 

          if (b.isConstructionDone) { 
            this.evaluatePowerSupply(data.power) 
            if (b.name === 'CoinsGenerator') { 
              this.evaluateCoinsGeneration() 
            } 

            if (b.name === 'Merchant') { 
              this.evaluateMerchant(b) 
            } 
          } 
        } 
      }, 
      evaluatePopulation (value) { 
        this.populations += value 
      }, 
      evaluatePowerSupply (value) { 
        this.powerSupplies += value 
      }, 
      evaluateCoinsGeneration () { 
        this.coinGenerationCountDown -= 3 
      }, 
      evaluateMerchant (building) { 
        var b = building 
        if (!b.diamonTick) { 
          b.diamonTick = 0 
        } 

        b.diamonTick += 1 
        if (b.diamonTick >= this.tickCountForMerchantDiamond) { 
          if (this.coins >= this.coinsNeededForDiamond) { 
            this.coins -= this.coinsNeededForDiamond 
            this.popDiamond(b) 
            b.diamondTick = 0 
          } 
        } 
      }, 
      coinsTick () { 
        this.coinGenerationCount += 1 
        if (this.coinGenerationCount >= this.coinGenerationCountDown) { 
          this.coins += 1 
          this.coinGenerationCount = 0 
        } 

        this.calculateBuildingsEffects() 
      },

calculateBuildingsEffects函数的作用是根据当前建筑物的情况来决定城市系统资源的变化,如果当前建筑物有发电厂,那么就调用evaluatePowerSupply增加城市能源的数量,如果有造币厂,那么就调用evaluateCoinsGeneration,把钱币生成的时间间隔减短3个时间单位,如果有商城,那么就调用evaluateMerchant函数,它会判断当前钱币是否足够多,当钱币满足时,还需等待一段时间后,才会在商城附近调用popDiamond函数,实现钻石精灵旋转的特效。coinsTick负责每过一段时间后就给城市增加一个钱币,它会调用calculateBuildingsEffects函数来统计城市系统当前的资源数量。

在tick函数中会调用coinsTick,由于tick函数是每秒被调用40次,因此游戏在每一秒都会多次触发前面提到的各种资源计算函数。接下来我们就需要看看如何实现钻石转动的精灵特效,它的实现代码如下:

diamondSprite () { 
        var container = new this.cjs.Container() 
        var data = { 
          framerate: 16, 
          images: ['../../static/images/diamond-spritesheet.png'], 
          frames: {width: 90, height: 90} 
        } 
        var spriteSheet = new this.cjs.SpriteSheet(data) 
        var diamondSprite = new this.cjs.Sprite(spriteSheet) 
        diamondSprite.gotoAndPlay(0) 
        diamondSprite.scaleX = diamondSprite.scaleY = 0.5 
        container.addChild(diamondSprite) 
        container.on('click', function () { 
          this.diamonds += 1 
          this.stage.removeChild(container) 
        }.bind(this)) 

        return container 
      }, 
      // 在页面上弹出一个钻石 
      popDiamond (building) { 
        var screenCoord = this.isoToScreenCoord(building.x, building.y) 
        var globalScreenCoord = this.cityLayer.localToLocal(screenCoord.x, screenCoord.y, this.stage) 
        var diamond = this.diamondSprite() 
        diamond.x = globalScreenCoord.x 
        diamond.y = globalScreenCoord.y 
        this.stage.addChild(diamond) 
      },

diamondSprite函数用来创建一个钻石转动的动画精灵,他会把精灵特效对应的图片加载到页面上,我们要实现的精灵特效图片如下:

动画精灵的本质是把一系列图片连续显示,进而展现出一种动画效果。我们的钻石精灵就是把上面图片中的五个图案在单位时间内多次连续显示,上面图片连续显示后就会在页面上展现出一种转动不停的特效。

diamondSprite函数先创建一个动画精灵对象,它定义了一个变量叫framerate,这个值用来指定一秒内连续展示几幅图片,我们规定一秒内展示16幅,在我们上面的图片中,总共有5幅图案,一旦图案展示完了后,代码会重新从第一幅开始,再次循环展示。里面的gotoAndPlay调用就开启了图片连续显示过程。

当我们在转动的钻石精灵上点击后,程序会让游戏的总钻石数加一,并且把钻石精灵从舞台容器,也就是stage中拿掉,这样钻石精灵就从页面上消失了。函数popDiamond被调用时,他就会调用diamondSprite函数创建一个转动的钻石精灵,然后设置精灵在页面上的坐标轴,同时把精灵对象加入舞台容器,这样转动着的钻石精灵就会出现在页面上。

至此,游戏的设计进入到尾声阶段。最后我们要实现的是游戏数据的本地存储。我们这个游戏是一个较为消耗时间的过程,如果玩家玩到一半暂时不想玩了,那么他可以把页面关闭,下次打开页面上,页面上显示的情况要和上次关闭时一模一样,这就要求我们的游戏在页面关闭时,把各种数据,例如当前的页面上已经有的建筑物,游戏的钱币数,人口值等相关信息存储到本地,当下次页面开启时,将存储的数据再次读入页面,代码根据存储的数据把页面上次关闭时的情况再次重现出来。

最新的HTML5提供了相关机制,让我们能实现页面数据的局部保存,相应代码如下:

init () { 
    .... 
// change here 
        if (localStorage['city.buildinglist']) { 
          this.buildingList = JSON.parse(localStorage['city.buildinglist']) 
        } else { 
          this.buildingList = [] 
        } 
        // *1 将字符强行转换成数字 
        this.coins = localStorage['city.coins'] * 1 || 10 
        this.diamonds = localStorage['city.diamonds'] * 1 || 0 
        .... 
} 
.... 
tick () { 
.... 
 // change here 
  this.autoSave() 
} 
autoSave () { 
        if (this.cjs.Ticker.getTicks() % 100 === 0) { 
          localStorage['city.coins'] = this.coins 
          localStorage['city.diamond'] = this.diamonds 
          localStorage['city.buildinglist'] =       JSON.stringify(this.buildingList) 
        }

在autoSave中,代码利用HTML5提供的localStorage对象将相关信息存储起来,它保存了游戏当前的钻石数和钱币数,并调用JSON.stringify把buildingList中存储的建筑物信息全部转换成JSON格式的字符串后,存储在localStorage的city.buildinglist字段下。

在init函数中,也就是页面刚刚被浏览器加载时,他会被调用,在初始化时,代码会先从localStorage对象中读取city.buildinglist字段,获得存储的建筑物信息,把这些信息转换为二进制后重新存储在数组buildingList 变量中。然后分别读取city.coins 和 city.diamonds字段,获得上次页面关闭时游戏存储的钱币数和钻石数,并把他们恢复到本次游戏进程中来。在tick函数中会调用autoSave函数,后者会判断,每过100个时间单位后,才会把当前数据存储到localStorage对象中。完成这部分代码后,我们可以尝试着关闭或刷新当前页面,当下次再次打开页面时,我们可以看到,页面上的情形与上一次关闭时是一模一样的。

至此,整个游戏的设计就结束了,其实游戏本身还有很多需要改进的地方。例如例如不同的建筑物,它的建筑完成时间是不一样的,有些建筑物例如商城需要玩家等待很长时间,这时候我们可以提供一个功能,玩家只要缴纳一点钱,我们便能开个后门,减少建筑物建筑的时间。同时我们还可以增加更多种类,功能也独特的建筑物,只有付费玩家才能选择等等。

本文分享自微信公众号 - Coding迪斯尼(gh_c9f933e7765d),作者:陈屹

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-12-15

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 使用物理引擎Box2D设计类愤怒小鸟的击球游戏--基本架构设置

    望月从良
  • 实现红警式的建筑物拖拽生成特效

    望月从良
  • 实现小球在弹射前的拉伸特效和动态障碍物特效

    当前我们实现小球弹射时,会先用鼠标点击小球,然后移动鼠标,当松开鼠标时,小球会弹射向鼠标松开的位置。我们按住小球的时间越长,小球弹射的力度就越大,但有一个问题是...

    望月从良
  • RocketMQ 生产者 Producer 启动过程

    从类关系中可以看出,MQProducer 有两种实现方式。一个是 DefaultMQProducer,另一个是 TransactionMQProducer。

    java404
  • 【被玩坏的博客园】之canvas装饰博客园侧边栏

    用户1749219
  • 《你不知道的JavaScript》:js委托设计的真实案例与总结

    实际需求,web开发中有一个典型的前端场景,创建UI控件(按钮、下拉列表等)。用jq的选择器来简化选择过程,与实现思路不冲突。

    前端_AWhile
  • 腾讯原生小程序框架 OMIX 2.0 发布

    ? 好的设计只有一种,我们认为 OMIX 2.0 的设计刚刚好。 OMIX 2.0 是 WeStore 的进化版,WeStore 使用的是数据变更前后的 d...

    腾讯开源
  • SpringFramework之@Configuration/@Import注解如何解析的

        Spring版本是5.0.9.release,Springboot版本是2.0.3.release

    克虏伯
  • 使用物理引擎Box2D设计类愤怒小鸟的击球游戏--基本架构设置

    望月从良
  • 【被玩坏的博客园】之canvas装饰博客园侧边栏

    用户1749219

扫码关注云+社区

领取腾讯云代金券