上一节,我们已经实现了游戏的启动场景设计,本节,我们继续把游戏的进行场景和结束场景完成,并通过VUE组件的通讯机制,实现不同场景间的任意切换。我们先完成游戏进行场景的设置。

在src/component目录下,新建一个文件名为gamescenecomponent.vue,打开文件,先编写组件视图部分的代码:

<template>
  <div id="game-scene" :class="{'scene in': show,
              'scene out': !show}">
     <a href="#" id="scene-btn" class="button" ></a>
  </div>
</template>

这里值得说明的是VUE提供给我们的指令:class, show 是一个变量,这个变量来自组件的数据模块,代码:

:class="{'scene in': show,
         'scene out': !show}"

是什么意思呢,如果变量show的值是true的话,VUE就会把上面代码编译位:

class="scene in"

如果show的值是false, 那么上面的代码会被VUE框架编译位:

class="scene out"

于是我们可以在组件的逻辑模块中修改show的值,进而能修改div组件的class属性。我们先看看scene in 和scene out的定义,打开gamecontianer.vue,在style标签中添加如下css代码:

<style scoped>
...
.scene.out {
    transform: translate3d(100%, 0, 0);
  }

  .scene.in {
    transform: translate3d(0, 0, 0)
  }
...
</style>

当div的class属性是scene in 的时候,div控件就能在页面上显示出来,如果class属性是scene out,那么div控件在页面上就不可见。于是我们可以通过组件的逻辑模块修改数据模块中的show变量,把show设置成true,那么组件的视图就可以在页面上绘制出来,把show设置成false,那么组件的视图在页面上就看不到。接下来我们添加组件的逻辑模块和数据模块,继续在gamescenecomponent.vue中添加如下代码:

<script>
  import Constant from './constant'
  export default {
    data () {
      return {
        show: false
      }
    }
  }
</script>

data 接口是组件的原生接口,如果组件有自己的数据,那么它必须实现data接口,VUE框架加载组件时,会调用其data接口,组件实现data接口,返回一个数据集合对象,VUE框架拿到这个数据集合对象后,会把对象里面的数据跟视图结合起来,从上面代码可以看成,我们的组件实现了data接口,并返回了一个数据集合,这个集合只包含一个数据,也就是show,并且初始值是false.VUE框架拿到这个数据集合对象后,把里面的show变量跟视图结合起来,还记得上面:class指令中的show吗,那里的show跟data接口返回的数据集合中的show是一致的,这种一致性是由VUE框架保证的,所以当组件改变它show变量的值,这个改变会立马反应到视图中。

接着我们再添加组件的css代码,在gamescenecomponent.vue中继续添加如下代码:

<style scoped>
  #game-scene {
    background: url(../../static/images/battle-bg.png) no-repeat;
  }
  .button {
    position: absolute;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
  }
</style>

从css代码可以看出,该组件的视图部分只是显示一张背景图片。完成该组件后,我们要把它加载到页面里。打开gamecontianer.vue,在script部分先把组件引用进来:

<script>
  import StartScene from './startscenecomponent'
  import GameScene from './gamescenecomponent'
  export default {
    components: {
      StartScene,
      GameScene
    }
  }
</script>

接着在视图部分引用该引入的组件,回到gamecontianer.vue的template部分,添加一句代码:

<template>
  ...
  <section id="game" class="row">
        <start-scene></start-scene>
        <game-scene></game-scene>
  </section>
  ...
</template>

完成上面代码后,刚完成的游戏场景所对应的组件gamescene就被加载进页面了,这时候如果你刷新页面,你会发现GameScene组件的视图并没有在页面上显示出来,页面显示的仍然是StartScene组件的视图,也就是上一节的启动界面。要从StartScene组件的视图切换到GameScene组件的视图,就得涉及到VUE组件间的相互通讯。

从这几节的讨论来看,我们发现VUE的组件是相当独立的模块,模块化,重用性是VUE组件的最大特点。但是要完成一件复杂的任务,需要各个组件间相互配合,这就需要组件间能够顺畅的通讯。

VUE间组件的机制是通过消息发送和接收来实现的。VUE框架给我们提供了一个名为Vue的对象,该对象导出两个接口,一个是用于发送消息的接口$emit,另一个是用于接收消息的接口$on,我们看看这两个接口该如何使用。在代码目录src/components/下新建一个文件,名为Constant.vue,添加如下代码:

<template>
</template>

<script>
  import Vue from 'vue'
  export default {
    MSG_START_SCENE: 'msg-start-scene',
    MSG_OVER_GAME_SCENE: 'msg-over-game-scene',
    Event: new Vue()
  }
</script>

<style>
</style>

Constant.vue 实现的也是一个组件,该组件的Event是一个Vue对象,其他组件将依赖这个Vue对象来收发消息,打开startscenecomponent.vue对其中代码做如下修改:

<template>
  <div id="start-scene" class="scene">
    <a href="#" id="start-btn" class="button" @click="startBtnClick()"></a>
  </div>
</template>

这里我们给链接对象增加了一个点击响应函数,其中@click是VUE框架提供的事件响应绑定指令,当用户点击链接时,VUE会替我们调用函数startBtnClick来响应用户的点击。我们再看看startBtnClick的实现:

<script>
  import Constant from './constant'
  export default {
    methods: {
      startBtnClick () {
        Constant.Event.$emit(Constant.MSG_START_SCENE)
      }
    }
  }
</script>

前面我们说过,一个组件除了视图模块外,还有数据和逻辑模块,上面的methonds对应就是组件的逻辑模块部分,一个组件对外导出的所有方法都要实现在这个部分,这里我们添加了函数startBtnClick的实现,代码首先将刚实现的Constant组件导入,然后在startBtnClick函数的实现中,调用Constant组件的Event对象的$emit接口向外广播一个消息,这个消息的名称为MSG_START_SCENE,其他组件如果想要想要这个消息,那么就可以通过Constant组件的Event对象监听该消息。

打开gamescenetcomponent.vue,修改如下代码:

<script>
  import Constant from './constant'
  export default {
    data () {
      return {
        show: false,
        userWon: true
      }
    },
    mounted () {
      Constant.Event.$on(Constant.MSG_START_SCENE, function () {
        this.show = true
      }.bind(this))
    }
}

代码同样先把Constant组件的实例给导入,这里值得注意的是mounted接口,该接口时组件的原生接口,当组件被页面加载的时候,VUE框架会调用该接口通知我们组件被加载了。这里我们使用了Constant组件Event对象的$on接口来监听MSG_START_SCENE消息,同时提交一个响应处理函数,一旦该消息被广播出来后,函数会被VUE框架调用,以便响应消息。在响应函数里,我们把组件数据模块的show变量的值设置成true,这样gamescenecomponent组件的视图部分的div控件,它的class属性就由’scene out’ 变成’scene in’, 这样的话,div组件就能在页面上渲染出来。这里要注意的是,消息响应函数末尾一定得使用bind(this)把函数的this对象跟组件本身绑定起来,要不然响应函数被VUE框架回调时,里面的this对象就不能正确的指向组件本身。

打开gamecontainer.vue,我们把gamescenecomponent.vue定义的组件加载到页面里:

<template>
 ....
 <section id="game" class="row">
    <start-scene></start-scene>
    <game-scene></game-scene>
 </section>
 ....
</template>

完成如上代码后,刷新页面加载修改后的代码,首先出现的仍然是启动场景的背景图:

VUE+WebPack前端游戏设计:依赖VUE组件通讯机制实现场景游戏切换_VUE


此时我们在页面上用鼠标单击,于是startBtnClick函数被调用,根据前面代码的实现,MSG_START_SCENE消息会被发送,由于GameScene组件在一加载时就监听了该消息,所以当消息一发出,它就能把自己的画面给显示出来,于是点击后页面从启动场景的背景图切换成了游戏进行场景的背景图:

VUE+WebPack前端游戏设计:依赖VUE组件通讯机制实现场景游戏切换_VUE_02

基于同样的道理,我们可以实现游戏结束场景,然后再从游戏进行场景切换到游戏结束场景,在目录下新建一个文件,名为gameovercomponent.vue,其代码实现如下:

<template>
  <div id="gameover-scene" :class="{'scene in': show,
                                    'scene out': !show,
                                    'won' : isWon,
                                    'loss': !isWon}">
   </div>
</template>

<script>
  import Constant from './constant'
  export default {
    data () {
      return {
        show: false,
        isWon: false
      }
    },
    mounted () {
      Constant.Event.$on(Constant.MSG_OVER_GAME_SCENE, function (userWon) {
        this.isWon = userWon
        this.show = true
      }.bind(this))
    }
  }
</script>

<style scoped>
  #gameover-scene.won {background:url(../../static/images/you-won.png) no-repeat;}
  #gameover-scene.loss {background:url(../../static/images/you-loss.png) no-repeat;}
</style>

注意组件视图部分的代码:

<div id="gameover-scene" :class="{'scene in': show,
                                    'scene out': !show,
                                    'won' : isWon,
                                    'loss': !isWon}">

如果组件的show变量值为true, 那么class属性的值就是’scene in’, 同时如果组件的isWon变量的值是true的话,class属性还可以具备属性值’won’,也就是说如果show等于true, isWon等于true, 那么VUE会把指令:class编译为:

<div id="gameover-scene" class="scene in won">

如果变量isWon的值是false,那么div组件的class属性编译后情况为:

<div id="gameover-scene" class="scene in loss">

我们再看css部分的代码就可以发现,如果class属性含有won,那么组件的背景图对应于图片:
../../static/images/you-won.png
如果class属性含有loss,那么组件的背景图片对应于:

../../static/images/you-loss.png

也就是说,通过设置组件的isWon变量的值,我们就可以更换组件在页面上显示的背景图。

代码中还有一处需要注意的是,组件加载时的消息响应函数:

mounted () {
      Constant.Event.$on(Constant.MSG_OVER_GAME_SCENE, function (userWon) {
        this.isWon = userWon
        this.show = true
      }.bind(this))
    }

该响应函数相比于前面我们实现的消息响应函数不同的是,函数多了一个输入参数userWon,这表示消息的发送和接收是可以附带参数的。消息的发送者在发出消息时,可以附带指定格式的数据连同消息一起发送出去。消息的接收者监听到消息的时候,可以获得消息发送者在发送消息时的附带数据。

回到gamescenecomponent.vue我们对代码进行相应修改,先修改它的视图部分代码:

<template>
  <div id="game-scene" :class="{'scene in': show,
              'scene out': !show}">
     <a href="#" id="scene-btn" class="button" @click="showGameOverScene()"></a>
  </div>
</template>

代码修改后,当我们在游戏进行场景的背景图上点击时,就会触发函数showGameOverScene()。接着我们对组件的逻辑模块部分进行相应修改:

<script>
  import Constant from './constant'
  export default {
    data () {
      return {
        show: false,
        userWon: true
      }
    },
    mounted () {
      Constant.Event.$on(Constant.MSG_START_SCENE, function () {
        this.show = true
      }.bind(this))
    },
    methods: {
      showGameOverScene () {
        Constant.Event.$emit(Constant.MSG_OVER_GAME_SCENE, this.userWon)
      }
    }
  }
</script>

当showGameOverScene函数被调用时,它会向外广播消息MSG_OVER_GAME_SCENE, 同时把组件变量userWon的值也附带着消息发送出去,而接收这个消息的正好是我们前面实现的gameoverscenecomponent.vue所定义的组件。

代开gamecontainer.vue,我们把gameoverscenecomponent.vue定义的组件加载到页面里:

<template>
 ....
 <section id="game" class="row">
        <start-scene></start-scene>
        <game-scene></game-scene>
        <game-over-scene></game-over-scene>
 </section>
 ....
</template>

完成上面代码后,当我们进入到游戏进行场景时,在游戏进行场景的背景图上用鼠标单击,就能切换到游戏结束场景,于是GameOverScene组件的背景图片会加载到页面上:

VUE+WebPack前端游戏设计:依赖VUE组件通讯机制实现场景游戏切换_html5_03

如果我们把GameScene组件userWon变量的值改成false,那么切换到游戏结束场景时情况如下:

VUE+WebPack前端游戏设计:依赖VUE组件通讯机制实现场景游戏切换_游戏设计_04

到此cardBattle游戏的三个场景我们就实现完毕,接下来我们将把精力集中到游戏流程的实现之上。更详实的讲解和演示请参看视频,本文代码可以从以下链接:
http://pan.baidu.com/s/1hr6XGXI

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

VUE+WebPack前端游戏设计:依赖VUE组件通讯机制实现场景游戏切换_游戏设计_05