专栏首页Coding迪斯尼VUE+WebPack:开发一款太空版植物大战僵尸的前端页游

VUE+WebPack:开发一款太空版植物大战僵尸的前端页游

从本节开始,我们探讨如何使用VUE和WebPack开发一款类似于植物大战僵尸的前端游戏,当游戏完成后,情况如下:

游戏的设定如下,一系列外星飞船从天而降入侵地球,为了保护地球,玩家需要使用各种道具防止外星飞船落入底部的地球。如图所示,这些道具可以是箱子,也可以是卫星。当外星飞船被成功阻挡时,界面会弹出一系列奖章,也就是图片里面的”E”,点击这些奖章后,玩家可以获得积分,一旦积分达到要求,玩家就可以选择炮台,也就是图片中红色的物体,炮台可以发射子弹,一旦子弹打中外星飞船,飞船就会从界面上消失,游戏的玩法其实和植物大战僵尸是如出一辙。本节,我们先完成代码基本架构的设计。

先在本地目录新建一个VUE工程,在工程目录下打开index.html做如下修改:

<!DOCTYPE html> 
<html> 
  <head> 
    <meta charset="utf-8"> 
    <meta name="viewport" content="width=device-width, user-scalable=no, minimal-ui"> 
  <meta name="apple-mobile-web-app-capable" content="yes"> 
    <script type="text/javascript" src="./static/tweenjs-0.5.1.min.js"></script> 
    <script type="text/javascript" src="./static/easeljs-0.7.1.min.js"></script> 
    <script type="text/javascript" src="./static/movieclip-0.7.1.min.js"></script> 
    <script type="text/javascript" src="./static/assets.js"></script> 
    <script type="text/javascript"> 
      window.createjs = createjs 
      window.assetsLib = lib 
    </script> 
    <title>Space Defender</title> 
  </head> 
  <body> 
    <div id="app"></div> 
    <!-- built files will be auto injected --> 
  </body> 
</html>

在代码中,我们先把需要使用的若干类库给加载进来,在本项目中,我们新增了两个类库,一个是movieclip-0.7.1.min.js,另一个是assets.js,后者是一个资源类库,我们游戏所有的图片资源都压缩在这个类库里,后面我们会详细解读它的作用。

接着进入src/目录,在里面修改App.vue:

<template> 
  <div id="app"> 
    <game-container></game-container> 
  </div> 
</template> 

<script> 
import GameContainer from './components/gamecontainer' 
export default { 
  name: 'app', 
  components: { 
    GameContainer 
  } 
} 
</script> 

<style> 
#app { 
  font-family: 'Avenir', Helvetica, Arial, sans-serif; 
  -webkit-font-smoothing: antialiased; 
  -moz-osx-font-smoothing: grayscale; 
  text-align: center; 
  color: #2c3e50; 
  margin-top: 60px; 
} 
</style>

它的主要作用是引入GameContainer组件,游戏的启动将由GameContainer组件加载如页面后开始,我们再看看该组件的实现,进入component/目录,在里面新增一个文件名为:gamecomponent.vue,然后添加如下代码:

<template> 
  <div> 
    <header> 
      <div class="row"> 
        <h1>Space Defender</h1> 
      </div> 
    </header> 
    <div> 
      <game-scene></game-scene> 
    </div> 
  </div> 
</template> 

<script> 
  import GameScene from './GameSceneComponent' 
  export default { 
    components: { 
      GameScene 
    } 
  } 
</script> 

<style scoped> 
  body, h1, h2, p { 
    margin: 0; 
    padding: 0; 
  } 

</style>

它的逻辑简单,主要是在页面显示游戏标题,它最重要的作用是将GameScene引入页面,游戏的所有特效,场景都将由GameScene组件来完成,接着,我们重点查看GameScene的实现,在目录下新建一个文件名为gamescenecomponent.vue,打开后先完成以下代码:

<template> 
  <section id="game" class="row"> 
    <canvas id="canvas" width="640" height="1000"> 
    </canvas> 
    ... 
</template>

template部分的代码主要用来设计游戏界面,在上面代码中,我们现在页面加载一个html5的’画布‘组件,也就是canvas,游戏所有的特效显示将依赖canvas组件来完成。继续在template部分添加如下代码:

<template> 
  .... 
   <div id="hud"> 
      <div>Lives: <span id="lives"></span></div> 
      <div>E: <span id="energies"></span></div> 
      <div>Waves: <span id="waves"></span></div> 
    </div> 
</template>

上面代码的作用是在页面头部显示与游戏相关的数据,上面代码完对应的就是前面游戏界面截图中的头部显示内容:

在游戏舞台的底部,我们添加按钮,以便玩家在页面上添加各种能消灭或阻止外星飞船入侵地球的障碍物,相关代码如下:

<template> 
  .... 
  <div class="add-buttons"> 
      <a class="add-button" title="space Junk" data-type="SpaceJunk">J</a> 
      <a class="add-button" title="Satellite" data-type="Satillite">S</a> 
      <a class="add-button" title="Satellite+" data-type="Satellite2">S+</a> 
      <a class="add-button" title="Castle" data-type="Castle">C</a> 
      <a class="add-button" title="Castle+" data-type="Castle2">C+</a> 
    </div> 
</template>

上面代码完成后,在游戏界面的下方会出现一系列按钮,情形如下:

接着,我们添加style标签代码,这部分代码其实是一段css,用来对template部分的html代码进行界面美化:

<style scoped> 
  #game { 
    position: relative; 
    width:  640px; 
    height: 1000px; 
    border: 1px solid black; 
  } 
  #canvas { 
    position: absolute; 
    top: 0px; 
    left: 0px; 
    background-color: #94A9B0; 
  } 

  #hud { 
    position: absolute; 
    width: 100%; 
    height: 60px; 
    background: rgba(0,0,0,0.5); 
    color: white; 
  } 

  .add-buttons { 
    position: absolute; 
    width: 100%; 
    height: 60px; 
    bottom: 0; 
    background: rgba(0, 0, 0, 0.5); 
  } 

  .add-button { 
    display: inline-block; 
    width: 50px; 
    height: 50px; 
    background-color: rgba(255, 255, 255, 0.3); 
    color: white; 
    text-decoration: none; 
    text-align: center; 
    line-height: 50px; 
    curosr: pointer; 
  } 

  .add-button:hover { 
    background-color: rgba(255, 255, 255, 0.6); 
  } 
</style>

现在我们开始集中精力完成程序的主逻辑代码,也就是script标签部分的代码,在文件中先添加如下代码:

<script> 
  export default { 
    data () { 
      return { 
        gameWidth: 640, 
        gameHeight: 1000, 
        cjs: null, 
        canvas: null, 
        stage: null, 
        lives: 20, 
        energies: 120, 
        assetsLib: null 
      } 
    }, 

    mounted () { 
      this.init() 
    },

data()接口用于设置组件的内部数据,当组件被页面加载后,mounted()函数就会被执行,一旦它执行后,它会调用init()函数执行组件的初始化工作。我们看看init()初始化函数的实现:

methods: { 
      init () { 
        this.cjs = window.createjs 
        this.assetsLib = window.assetsLib 
        this.canvas = document.getElementById('canvas') 
        this.stage = new this.cjs.Stage(this.canvas) 

        this.bgLayer = new this.cjs.Container() 
        this.bgLayer.addChild(new this.assetsLib.Background()) 
        this.stage.addChild(this.bgLayer) 

        this.boardLayer = this.Board() 
        this.stage.addChild(this.boardLayer) 

        this.effectLayer = new this.cjs.Container() 
        this.stage.addChild(this.effectLayer) 

        this.setHud() 

        this.cjs.Ticker.setFPS(40) 
        this.cjs.Ticker.addEventListener('tick', this.stage) 
        this.cjs.Ticker.addEventListener('tick', this.tick) 
      }, 

      tick () { 
        if (this.cjs.Ticker.getPaused()) { 
          return 
        } 

        this.livesSpan.textContent = this.lives 
        this.energiesSpan.textContent = this.energies 
        this.wavesSpan.textContent = 1 
      }, 

      Board () { 
        var board = new this.cjs.Container() 
        board.x = 10 
        board.y = 60 

        board.rows = 10 
        board.cols = 7 

        this.tileWidth = 87 
        this.tileHeight = 83 

        var sprite = new this.assetsLib.Board() 
        board.addChild(sprite) 
        sprite.y = board.tileHeight 

        return board 
      },

在init函数里,我们先获取createjs对象,图片的显示和特性都需要该类库提供支持。这里我们需要了解一下assetsLib,这个对象来自于index.html里面引入的类库assets.js,该游戏所使用的各种图形例如红色的炮台,入侵的宇宙飞船,阻挡飞船的箱子,卫星等,全是由flash制作出来的,这些图片资源全部打包在一个名为assets.fla文件中,这个文件必须使用flash相关软件才可以查看,为了能够在js代码中使用fla文件中的资源,通过flash软件就可以把.fla文件转为js代码文件,通过这个代码文件我们就可以获取由flash创建的各种图片资源。assetsLib就是由assets.js导出来的一个对象,通过调用该对象的接口,我们可以把flash创建的图片资源加载到页面上。

我们看下面这段代码片段:

this.bgLayer = new this.cjs.Container() 
this.bgLayer.addChild(new this.assetsLib.Background()) 
this.stage.addChild(this.bgLayer)

代码先创建了一个图层容器bgLayer,该图层主要用来绘制游戏的背景图,而背景图片就是assets.Background()接口返回的,我们把背景图绘制到bgLayer对象里,然后把该对象加入舞台容器控件,也就是stage,这样背景图片就可以显示在页面上了, 背景图的部分显示如下:

我们接着看下面的代码片段:

        this.boardLayer = this.Board() 
        this.stage.addChild(this.boardLayer) 

        this.effectLayer = new this.cjs.Container() 
        this.stage.addChild(this.effectLayer)

这部分代码用给页面添加两个图层,一个是boardLayer,这个图层的作用是绘制玩家在页面上添加的物件,例如箱子,卫星,以及炮台。而effectLayer这个图层则用来绘制动态特效,例如飞动的E奖章,以及炮台射出的子弹。当我们把boardLayer图层添加到舞台容器后,我们就会发现页面背景图上方添加了一系列网格图案,玩家选择的所有物件都必须放置在网格里:

最后的代码片段:

        this.setHud() 

        this.cjs.Ticker.setFPS(40) 
        this.cjs.Ticker.addEventListener('tick', this.stage) 
        this.cjs.Ticker.addEventListener('tick', this.tick)

setHud()的作用是初始化在页面头部的相关信息,例如玩家有多少条命,还剩多少能量值等,同时为底层的按钮点击设置响应函数。同时代码启动了一个定时器,每秒调用组件提供的tick函数刷新页面,实现页面的更新效果。我们继续阅读和解析余下的代码:

setHud () { 
        var addButtons = document.querySelectorAll('.add-button') 
        this.livesSpan = document.getElementById('lives') 
        this.energiesSpan = document.getElementById('energies') 
        this.wavesSpan = document.getElementById('waves') 

        for (var i = 0, len = addButtons.length; i < len; i++) { 
          var button = addButtons[i] 
          button.onmousedown = this.addButtonOnMouseDown 
        } 
      }, 

      addButtonOnMouseDown (e) { 
        if (this.cjs.Ticker.getPaused()) { 
          return 
        } 

        var buildingType = this.dataset.type 
        var cost = this.getBuildingCostByType() 
        if (cost && this.energies >= cost) { 
          this.energies -= cost 
          this.buildingType = buildingType 
          this.readyToPlaceBuilding() 
        } 
      }, 

      getBuildingCostByType (type) { 
        // TODO 
        return 0 
      }, 

      readyToPlaceBuilding () { 
        // TODO 
      }

在setHud中,我们通过getElementById获得html控件的对象,以便我们后面改变他们该显示的信息。同时给底部几个按钮设置点击响应函数,当按钮被点击是,组件的addButtonOnMouseDown()就会被调用,在该函数里,代码判断用户点击的按钮对应哪种物件,并判断当前玩家所有的资源是否足够建筑对应的建筑物,如果资源足够,那么就调用readyToPlaceBuidling()函数实现建筑物在页面上的显示。最后两个函数我们我们还没有实现,他们的代码将在下节课程中再实现。完成上面代码后,在控制台运行npm run dev命令,代码被浏览器加载后显示如下:

本节我们搭建了游戏的基本框架,加载了游戏背景图以及一些基本控件,下节我们进进入到游戏主流程的开发中。

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

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

原始发表时间:2018-01-31

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • VUE+WebPack游戏设计:欲望都市,构建类RPG游戏的开发

    望月从良
  • 寿司开卖:实现寿司制作特效和音响特效

    本节我们将继续上一节完成若干个小功能。首先要完成的是,当客户动画在主页面出现时,它左上角会冒泡,显示它想购买何种寿司,此时玩家可以点击左下角面板中各种元素,组合...

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

    望月从良
  • VUE+WebPack游戏设计:欲望都市,构建类RPG游戏的开发

    望月从良
  • Vue某些情况下 v-model绑定数据不实时更新解决办法

    有的时候我们变化data内的内容,console.log打印的时候是显示已经变化了的,但并没有渲染到界面上去。受 ES5 的限制,Vue.js 不能检测到对象属...

    kirin
  • 520特辑———旋转爱

    520:网络情人节是信息时代的爱情节日,定于每年的5月20日和5月21日。该节日源于歌手范晓萱的《数字恋爱》中“520”被喻成“我爱你” ,以及...

    流眸
  • JavaScript 设计模式学习第二篇-关于this、new、bind、call、apply

    虽然标题关于this、new、bind、call、apply,但实际上这些都离不开 this,因此本文将着重讨论 this,在此过程中分别讲解其他知识点。

    越陌度阡
  • vue实现分页组件

    分页需要的字段:当前页(curPage),每页大小(pageSize),总页数(total) 作为一个组件,所以以上这些参数最好是从父组件传递过来,可以如下定义...

    陨石坠灭
  • 当我开始使用React 时,我希望我知道这些知识

    自2013年5月29日首次发布以来,React.js已经占领了互联网。我和许多其他开发人员将他们的成功归功于这个了不起的框架,这已经不是什么秘密了。

    前端小智@大迁世界
  • vue实现分页组件

    分页需要的字段:当前页(curPage),每页大小(pageSize),总页数(total) 作为一个组件,所以以上这些参数最好是从父组件传递过来,可以如下定义...

    陨石坠灭

扫码关注云+社区

领取腾讯云代金券