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

这里写图片描述

商店下方的钻石图片在页面上回呈现出反复转动的动态效果,具体特效请参看视频:
更详细的讲解和代码调试演示过程,请点击链接

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

在实现上述效果前,我们需要对代码做些许改动。当玩家点击右下角的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对象中。完成这部分代码后,我们可以尝试着关闭或刷新当前页面,当下次再次打开页面时,我们可以看到,页面上的情形与上一次关闭时是一模一样的。

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

后续我们还有更多好玩的精彩游戏要设计,请继续关注后续的内容。

欢迎关注公众号,让我们一起学习,交流,成长:
文章公众号.jpg

Logo

前往低代码交流专区

更多推荐