从本节开始,我们探讨如何使用VUE和WebPack开发一款类似于植物大战僵尸的前端游戏,当游戏完成后,情况如下:
游戏的设定如下,一系列外星飞船从天而降入侵地球,为了保护地球,玩家需要使用各种道具防止外星飞船落入底部的地球。如图所示,这些道具可以是箱子,也可以是卫星。当外星飞船被成功阻挡时,界面会弹出一系列奖章,也就是图片里面的”E”,点击这些奖章后,玩家可以获得积分,一旦积分达到要求,玩家就可以选择炮台,也就是图片中红色的物体,炮台可以发射子弹,一旦子弹打中外星飞船,飞船就会从界面上消失,游戏的玩法其实和植物大战僵尸是如出一辙。本节,我们先完成代码基本架构的设计。
先在本地目录新建一个VUE工程,在工程目录下打开index.html做如下修改:
代码语言:javascript复制<!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:
代码语言:javascript复制<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,然后添加如下代码:
代码语言:javascript复制<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,打开后先完成以下代码:
代码语言:javascript复制<template>
<section id="game" class="row">
<canvas id="canvas" width="640" height="1000">
</canvas>
...
</template>
template部分的代码主要用来设计游戏界面,在上面代码中,我们现在页面加载一个html5的’画布‘组件,也就是canvas,游戏所有的特效显示将依赖canvas组件来完成。继续在template部分添加如下代码:
代码语言:javascript复制<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>
上面代码的作用是在页面头部显示与游戏相关的数据,上面代码完对应的就是前面游戏界面截图中的头部显示内容:
在游戏舞台的底部,我们添加按钮,以便玩家在页面上添加各种能消灭或阻止外星飞船入侵地球的障碍物,相关代码如下:
代码语言:javascript复制<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代码进行界面美化:
代码语言:javascript复制<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标签部分的代码,在文件中先添加如下代码:
代码语言:javascript复制<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()初始化函数的实现:
代码语言:javascript复制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创建的图片资源加载到页面上。
我们看下面这段代码片段:
代码语言:javascript复制this.bgLayer = new this.cjs.Container()
this.bgLayer.addChild(new this.assetsLib.Background())
this.stage.addChild(this.bgLayer)
代码先创建了一个图层容器bgLayer,该图层主要用来绘制游戏的背景图,而背景图片就是assets.Background()接口返回的,我们把背景图绘制到bgLayer对象里,然后把该对象加入舞台容器控件,也就是stage,这样背景图片就可以显示在页面上了, 背景图的部分显示如下:
我们接着看下面的代码片段:
代码语言:javascript复制 this.boardLayer = this.Board()
this.stage.addChild(this.boardLayer)
this.effectLayer = new this.cjs.Container()
this.stage.addChild(this.effectLayer)
这部分代码用给页面添加两个图层,一个是boardLayer,这个图层的作用是绘制玩家在页面上添加的物件,例如箱子,卫星,以及炮台。而effectLayer这个图层则用来绘制动态特效,例如飞动的E奖章,以及炮台射出的子弹。当我们把boardLayer图层添加到舞台容器后,我们就会发现页面背景图上方添加了一系列网格图案,玩家选择的所有物件都必须放置在网格里:
最后的代码片段:
代码语言:javascript复制 this.setHud()
this.cjs.Ticker.setFPS(40)
this.cjs.Ticker.addEventListener('tick', this.stage)
this.cjs.Ticker.addEventListener('tick', this.tick)
setHud()的作用是初始化在页面头部的相关信息,例如玩家有多少条命,还剩多少能量值等,同时为底层的按钮点击设置响应函数。同时代码启动了一个定时器,每秒调用组件提供的tick函数刷新页面,实现页面的更新效果。我们继续阅读和解析余下的代码:
代码语言:javascript复制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命令,代码被浏览器加载后显示如下:
本节我们搭建了游戏的基本框架,加载了游戏背景图以及一些基本控件,下节我们进进入到游戏主流程的开发中。