在上一节,我们设计了游戏的背景图层和UI图层,这一节开始,我们把精力集中到城市图层的开发和设计上,因为整个游戏的动画特效和游戏逻辑都发送在这个图层,因此它的开发是整个项目的难点所在。

我先把中间的城市背景图贴到页面中间,接着要在城市背景图上绘制9*9=81个网格,所有的城市建筑都必须拜访在网格中,代码完成后,页面加载显示如下:

VUE+WebPack游戏设计:欲望都市城市图层的设计_游戏设计

注意,画面上的网格是我们用代码绘制上去的,不是图片自身带有的。首先打开gamescenecomponent.vue,添加如下代码:

create2DArray (rows, cols, initialValue) {
        var array = []
        for (var i = 0; i < rows; i++) {
          array[i] = []
          for (var j = 0; j < cols; j++) {
            array[i][j] = initialValue
          }
        }
        return array
      },
 tile () {
    var imagePath = '../../static/images/tile.png'
    var obj = new this.cjs.Bitmap(imagePath)
    obj.regX = 0
    obj.regY = 21
    obj.width = 86
    obj.height = 43
    return obj
 }

create2DArray的作用是生成一个二维数组,我们将用这个函数生成一个9*9的二维数组,这个数组将对应于上图中的网格。tile函数加载网格对应的图案如下:

VUE+WebPack游戏设计:欲望都市城市图层的设计_RPG_02

我们后面会把这个网格图案绘制到页面上,最终形成我们看到的那样大片网格区域。接着我们回到cityLayer函数的实现中添加相应代码:

cityLayer () {
        var obj = this.layer()
        var bg = new this.cjs.Bitmap('../../static/images/city-bg.png')
        bg.regX = 370
        bg.regY = 30
        obj.addChild(bg)
        // 9 * 9 grids
        obj.cols = obj.rows = 9
        var tiles = new this.cjs.Container()
        obj.addChild(tiles)

        obj.viewMap = this.create2DArray(obj.rows, obj.cols)
        obj.x = this.gameWidth / 2 - this.tileWidth / 2
        obj.y = this.gameHeight / 2 - (obj.rows - 1) * this.tileHeight / 2
        this.redraw(obj, tiles)
        return obj
      },
      redraw (layer, tiles) {
        for (var i = 0; i < layer.rows; i++) {
          for (var j = 0; j < layer.cols; j++) {
            var t = this.tile()
            t.x = (j - i) * this.tileWidth / 2
            t.y = (j + i) * this.tileHeight / 2
            tiles.addChild(t)
            layer.viewMap[i][j] = 0
          }
        }
      },

在cityLayer函数中,它先加载城市图案作为背景图,绘制到页面中间,然后创建一个用于绘制网格区域的容器叫tiles,并通过调用create2DArray,生成一个9*9的二维数组,然后我们生成81个网格位图对象,redraw除了把网格绘制到页面上外,还把这些网格对象存储在这个二维数组里,以便后续管理和使用。完成这些代码后,我们就可以看到带有网格的城市图层背景图会显示到页面上了。

我们先把一些代码调整下,在gamescenecomponent中,添加如下代码:

data () {
      return {
         ...
        coins: 100,
        diamonds: 0,
        powerSupplies: 100,
        populations: 0,
        consIndicator: null,
        diamondsIndicator: null,
        populationIndicator: null
      }     
  },
  ....
  tick () {
        this.coinsIndicator.text = this.coins + ''
        this.diamondsIndicator.text = this.coins + ''
        this.populationIndicator.text = this.populations + ''
        this.powerSupplyIndicator.text = this.powerSupplies + ''
        this.stage.update()
      },

这些代码的作用是,一旦我们程序逻辑修改了coins, diamonds, populations这三个变量,在UI图层上的控件会根据变化后的数值里面显示到页面上。

接下来我们要实现右下角按钮,玩家点击这个按钮后,系统会根据实际条件,显示出当前玩家可以建造的建筑物。要显示的界面如下:

我们将创建一个新组建来实现这个功能,在component路径下新建一个文件名为buildingpanelcomponent.vue并添加如下代码:

<template>
</template>

<script>
  export default {
    data () {
      return {
        show: false,
        buildingData: {},
        cjs: null
      }
    },

    mounted () {
      this.cjs = window.createjs
      this.init()
    },

    methods: {
      init () {
        this.buildingData['CoinsGenerator'] = {
          className: 'ConsGenerator',
          needCoins: 20,
          needPopulations: 10,
          power: 0
        }
        this.buildingData['PowerSupply'] = {
          className: 'PowerSupply',
          needCoins: 10,
          needPopulations: 0,
          power: 15
        }
        this.buildingData['Merchant'] = {
          className: 'Merchant',
          needCoins: 150,
          needPopulations: 20,
          power: 0
        }
      }
    }
  }
</script>

<style scoped>
</style>

buildingData 用来包含要显示在界面上的三种建筑物的信息。接着我们要创建几个按钮,由于按钮有三种形态,正常状态,鼠标滑动状态,点击状态,不同的状态需要显示不同的背景图,这样按钮看起来才有动感,因此我们专门为按钮的生成设计相关代码:

createButton (spriteImage, width, height) {
        var data = {
          images: [spriteImage],
          frames: {width: width, height: height}
        }
        var spritesheet = new this.cjs.SpriteSheet(data)
        var button = new this.cjs.Sprite(spritesheet, 1)
        this.cjs.ButtonHelper(button, 0, 1, 2)
        return button
      }

为了让按钮具备鼠标移动时的变换效果,需要对stage容器调用enableMouseOver()函数,默认情况下,绘图容器是不支持按钮的鼠标移动变换效果的,因为它会消耗很多CPU资源。先回到gamescenecomponent.vue,在init函数中添加代码:

init () {
  ....
  this.stage.enableMouseOver()
  ....
}

为了让buildingPanel组件把界面绘制出来,我们需要向它发送一个消息,这就需要用到VUE的组件通讯机制,因此我们像以前一样,创建一个Constant组件,代码如下:

<template>
</template>

<script>
  import Vue from 'vue'
  export default {
    Event: new Vue(),
    MSG_CREATE_BUILDINGS: 'msg-create-buildings'
  }
</script>

<style>
</style>

回到BuildingPanelComponent.vue, 我们在组件里添加绘制建筑选择界面的代码, 先为三个按钮初始化相关数据:

data () {
      return {
        ....
        buildings: []
      },
      ....
      init () {
        ....
        this.buildings[0] = {
          name: 'PowerSupply',
          image: 'power-supply',
          x: 20
        }
        this.buildings[1] = {
          name: 'CoinsGenerator',
          image: 'coins-generator',
          x: 338
        }
        this.buildings[2] = {
          name: 'Merchant',
          image: 'merchant',
          x: 650
        }
        this.setupBuildingPanel()
      }
    },

接下来,我们根据按钮数据,分别构造出按钮对象,代码如下:

setupBuildingPanel () {
        this.buildingPanel = new this.cjs.Container()
        this.buildingPanel.visible = false
        for (var i = 0; i < 3; i++) {
          this.setupBuildingButton(i)
        }
      },
setupBuildingButton (i) {
        var b = this.buildings[i]
        var button = this.createButton('../../static/images/build-' + b.image + '-sprite.png', 286, 396)
        button.x = b.x
        button.y = 16
        this.buildingPanel.addChild(button)

     var _this = this
     button.on('click', function () {
       _this.buildingTypeToBePlaced = b.name
       _this.readyToPalceBuilding()
     })

     var buttonDisabled = new this.cjs.Bitmap('../../static/images/build-' + b.image + '-disabled.png')
     buttonDisabled.x = button.x
     buttonDisabled.y = button.y
     buttonDisabled.visible = false
     this.buildingPanel.addChild(buttonDisabled)
      }

setupBuildingButton函数用来在建筑选择面板上显示不同建筑的选择按钮,例如setupBuildingButton(0), 那么函数将从数组buildings[0]中取出信息来构建按钮,于是b.imange的内容是’power-supply’,于是代码从static目录下加载图片build-power-supply-sprite.png作为按钮背景,如果你进入static/images目录,打开对应的图片,你会看到如下情况:

VUE+WebPack游戏设计:欲望都市城市图层的设计_游戏设计_03

我们在createButton函数中使用createjs库提供的函数来创建按钮:

var spritesheet = new this.cjs.SpriteSheet(data)
var button = new this.cjs.Sprite(spritesheet, 1)
this.helper = new this.cjs.ButtonHelper(button, 0, 1, 2)

第一句代码把图片加载到页面,作为按钮的背景图,第二句代码把图片中的中间图案作为按钮在正常状态,也就是鼠标没有滑动到按钮时,按钮该显示的状态,第三句设置按钮状态,当鼠标滑动到按钮上时,显示上图的第一部分作为按钮背景,当鼠标被点击时,选取第三部分作为按钮的状态背景,于是当我们的鼠标滑动到按钮上或者点击按钮时,按钮会显示不同状态,因此整个过程就有一种动态的感觉。

完成上面代码后,我们就可以尝试显示建筑选择面板的显示了,回到gamescenecomponent.vue, 把Constant组件加载进来,并依靠它向buildingPanel组件发送一个消息,让后者把面板在页面上显示出来:

import Constant from './constant'
  export default {
  ....
  mounted () {
  this.init()
      Constant.Event.$emit(Constant.MSG_CREATE_BUILDINGS, this.stage)
    },

接着再buildingpanelcomponent.vue中,也把Constant组件引入,并接收消息,然后把面板容器加入到舞台容器,也就是stage对象:

import Constant from './constant'
  export default {
  ....
  mounted () {
      this.cjs = window.createjs
      this.init()
      Constant.Event.$on(Constant.MSG_CREATE_BUILDINGS, function (stage) {
        console.log('receive panel msg')
        this.buildingPanel.visible = true
        stage.addChild(this.buildingPanel)
      }.bind(this))
    },
    ....   
}

上面代码完成后,加载页面看到的效果如下:

VUE+WebPack游戏设计:欲望都市城市图层的设计_WebPack_04

我们原本设计是在页面的右下角有个按钮,点击后才会出现选择面板的,所以我们在把上面的代码修改一下,我们先创建右下角按钮,代码如下:

mounted () {
      this.cjs = window.createjs
      this.init()
      Constant.Event.$on(Constant.MSG_CREATE_BUILDINGS, function (stage) {
        stage.addChild(this.buildingPanel)
        this.createCancelBuildButton(stage)
      }.bind(this))
    },

    ....

createCancelBuildButton (stage) {
        this.cancelBuildBtn = this.createButton('../../static/images/cancel-sprite.png', 128, 62)
        this.cancelBuildBtn.x = 820
        this.cancelBuildBtn.y = 400
        this.cancelBuildBtn.visible = false
        var _this = this
        this.cancelBuildBtn.on('click', function () {
          _this.cancelBuildBtn.visible = false
          _this.buildingPanel.visible = false
          _this.newBuildingBtn.visible = true
        })
        stage.addChild(this.cancelBuildBtn)

        this.newBuildingBtn = this.createButton('../../static/images/add-building-sprite.png', 124, 42)
        this.newBuildingBtn.x = 820
        this.newBuildingBtn.y = 420
        this.newBuildingBtn.on('click', function () {
          _this.buildingPanel.visible = true
          _this.cancelBuildBtn.visible = true
          _this.newBuildingBtn.visible = false
        })
        stage.addChild(this.newBuildingBtn)
      }

上面代码加载后,页面显示如下:

VUE+WebPack游戏设计:欲望都市城市图层的设计_HTML5_05


右下角多了一个按钮,点击后建筑选择面板就会出现在页面上:

VUE+WebPack游戏设计:欲望都市城市图层的设计_WebPack_06

如果再次点击’cancel’按钮,面板就会消失,同时按钮会恢复成原来的’+Building’按钮。

更详细的讲解和代码调试演示过程,请点击链接

下一节我们将实现建筑选择后的拖拽效果以及建筑在页面上建造时的渐变效果。

更多技术信息,包括操作系统,编译器,面试算法,机器学习,人工智能,请关照我的公众号: