使用Vue.js从零开始构建2048(第1部分)

2048是一种流行的单人游戏。 该游戏有一个4 x 4的瓷砖网格。 每个磁贴可以为空,也可以有一张有值的 。 作为一名玩家,您可以左右移动。 随着卡合并,卡上的数字也会更改。 目标是使卡上的号码尽可能多。 当所有图块都已满并且没有下一步移动时,游戏结束。

您可以在此处找到演示项目,并在此处找到Github存储库。 在本博客文章(第1部分)和下一篇博客文章(第2部分)中,我们将逐步逐步构建游戏!

开始使用项目脚手架

首先,我们需要建立一个项目支架来构建Vue项目。 我们将使用普通的javascript,以便我们了解发生了什么。 让我们从克隆普通的javascript Vue支架开始。 脚手架为我们提供了几个目录:

  • 资产 。 所有第三方javascript库,样式表和图像。
  • 组件 。 所有本机Vue组件。
  • mixins 。 所有本地Vue mixins。
  • 存储 。 Vuex存储用于状态管理。
  • main.js。 启动新Vue实例的位置。
  • index.html 。 显示整个应用的地方。

步骤1.设置游戏板

在这一步中,我们将:

  1. board创建game组件
  2. 创建阴影板
  3. 创建播种功能

首先,我们有一个负责董事会game组件。 棋盘上有很多图块 ,每个图块的默认值为0。我们可以通过注册组件game

src / components / game.js

  ((()=> { 
const html =`


`
  Vue.component(“ game”,{ 
模板:html,
数据(){
返回{
董事会:[],
}
},
})
}))()

在Vue组件game ,我们有一个数据属性board ,其中包含一个瓦片数组,每个瓦片的默认值均为0。接下来,我们在html添加一个阴影板作为棋盘游戏的基础层。

src / components / game.js

  ((()=> { 
const html =`







`
...
}))()

现在,当您打开index.html ,它应如下所示:

为了开始游戏并继续玩游戏,我们需要给一些磁贴一个可玩的值(即值> 0)。 为此,我们将创建一个seedTwo()方法。 在每个游戏开始时,我们将使用它来创建开始状态,并且在有效移动之后,我们将使用它来使游戏继续前进

src / components / game.js

  Vue.component(“ game”,{ 
...
方法: {
seedTwo (){
const self =这个
 让getRandomItem = function(){ 
让行=
self.board [Math.floor(Math.random()* self.board.length)]
返回行[Math.floor(Math.random()* row.length)]
}
 让initialRandomItem = getRandomItem() 
  while(initialRandomItem.value!= 0){ 
initialRandomItem = getRandomItem()
}
  initialRandomItem.value = 2 
}
}
})
  ... 

步骤2.定义对象模型

在这一步中,我们将:

  1. 创建Tile()组件
  2. 根据图块是否为空来修改Tile() CSS类
  3. 创建GameMenu()组件

一块板可以容纳16个平铺的瓷砖,尽管我们将其作为二维阵列展示在板上。 我们在src/components/tile.js下添加了另一个组件tile瓦片组件将瓦片对象作为必需的道具,需要包含{value: X}作为对象的一部分。

src / components / tile.js

  ((()=> { 
const html =`

{{值}}

`
  Vue.component(“ tile”,{ 
模板:html,
道具: {
瓦: {
类型:对象,
必填:true
},
},
 计算:{ 
value(){
返回this.tile.value
},
}
})
}))()

请注意,在最终产品中,每个图块的样式都会随着将图块从空(值0)更改为非空(值非0)而改变。 在组件内部,我们根据该值构造计算属性,然后使用计算属性通过类绑定来切换样式:

src / components / tile.js

  ((()=> { 
const html =`

{{displayValue}}

`
  Vue.component(“ tile”,{ 
模板:html,
道具: {
瓦: {
类型:对象,
必填:true
},
},
 计算:{ 
value(){
返回this.tile.value
},
  displayValue(){ 
如果(this.value> 0){
返回this.value
}
  返回null 
},
  emptyTile(){ 
返回this.displayingValue === null
},
}
})
}))()

给定我们有一个tile组件,我们将tile重新插入游戏组件:

src / components / game.js

  ((()=> { 
const html =`








`
...
}))()

现在我们有了计算emptyTile的属性emptyTile ,我们可以插入html和toggle类:

src / components / tile.js

  ((()=> { 
const html =`
<div class =“ tile” v-bind:class =“ {'tile-empty':emptyTile} ”>
{{displayValue}}

`
  Vue.component(“ tile”,{ 
...
})
}))()

接下来,我们可以定义一个game-menu以显示当前分数,显示最佳分数并开始新游戏。 让我们从一个普通的脚手架开始:

src / components / game-menu.js

  ((()=> { 
const html =`


2048



得分

0000



最佳

0000




新游戏

`
  Vue.component(“ game-menu”,{ 
模板:html,
})
}))()

让我们连接“新游戏”按钮! 因为game-menu组件嵌套在game组件内部。 我们可以将game-menu视为父组件game的子组件。 Vue有一个很好的范式,用于子代与父代之间的通信,子代组件可以向父代组件发出事件,并让父代处理事件。 这种范例称为“数据丢失,事件增加”。 您可以在此处阅读有关Vue事件处理的更多信息。

因此,让我们在game-menu.js上注册事件

src / components / game-menu.js

  ((()=> { 
const html =`

...
<a @click="newGame()">新游戏

`
  Vue.component(“ game-menu”,{ 
模板:html,
 方法: { 
新游戏() {
this。$ emit(“ new-game”)
}
}
})
}))()

this。$ emit(“ new-game”)将事件发送到其父game ,在game.js内部,我们需要处理该事件。 收到事件new-gamegame组件将调用newGame()方法,该方法通过两次调用seedTwo()来重置棋盘并为两个图块设置种子。

src / components / game.js

  ((()=> { 
const html =`

<game-menu @ new-game =“ newGame()” >
...

`
  Vue.component(“ game”,{ 
模板:html,
...
方法: {
seedTwo(){
...
},
  新游戏() { 
this.resetBoard()
this.seedTwo()
this.seedTwo()
},
  resetBoard(){ 
this.board = Array.apply(null,{length:16})
.map(function(_,index){
返回{id:索引,值:0}
})
}
}
})
}))()

第3步:合并和滑动图块

在这一步中,我们将:

  1. moveRight()为例,了解如何通过算法合并和滑动图块
  2. moveRight()为例,实现合并和滑动
  3. 使用Vue mixin在game组件中注册控件

本质上,我们有四种不同的方法来实现moveRightmoveLeftmoveUpmoveDown 。 一旦我们实现了一种方法,其他方法都将遵循相同的模式,因此更易于实现。 让我们以moveRight()为例。

moveRight()本质上包括两个步骤: mergeslide 。 在合并步骤中,我们找出需要合并为一个的两个图块; 在步骤sldie中,我们找出每个磁贴的未来位置。 因为我们在水平移动moveRight() ,所以该算法扫描每行并触发mergeRightslideRight 。 下面的动画演示了moveRight()算法如何在一行上工作:

以下是实现moveRight()算法的代码:

src / mixins / control.js

  mergeRight(board,a) { 
让我= board.length-2
令j = board.length-1

而(i> = 0){
if(board [a] [i] .value === 0 && board [a] [j] .value === 0){
//如果两个元素都为零
j-
一世 -
}否则,如果(board [a] [i] .value === board [a] [j] .value){
//如果两个元素的值相同
 板[a] [j] .value =板[a] [i] .value +板[a] [j] .value 
板[a] [i] .value = 0

j--
一世 -
}否则,如果(board [a] [j] .value === 0){
//如果最右边的值为0
j--
一世 -
}否则if(board [a] [i] .value!= 0 && board [a] [j] .value!= 0 &&(i + 1 == j)){//如果两者都不为零且从彼此
j--
一世 -
} else if(board [a] [i] .value!= 0 && board [a] [j] .value!= 0){
//如果两者都不为零且彼此不相邻
j--
}否则,如果(board [a] [i] .value === 0){
//如果最左边的元素为零
一世 -
}
}
},
  slideRight(board,a) { 
令k = board.length-2
令l = board.length-1
而(k> = 0){
if(board [a] [l] .value!== 0){
//如果最右边的元素为0
l-
k-
}否则if(board [a] [l] .value!== 0 && board [a] [k] .value!== 0){
//如果最右边和最左边的元素不为0
l-
k-
}否则,如果(board [a] [l] .value === 0 && board [a] [k] .value === 0){
//如果最右边和最左边的元素为0
k-
}否则,如果(board [a] [l] .value === 0 && board [a] [k] .value!== 0){
//如果最右边的元素为0,最左边的元素不为0
板[a] [l] .value =板[a] [k] .value +板[a] [l] .value
板[a] [k] .value = 0
l-
k-
}
}
}

在实现moveRightmoveLeftmoveUpmoveDown 。 我们可以在src/mixins/control.js隔离所有这些方法,然后将控件作为game组件的mixin包含在内。 现在,在安装game时,我们注册控件:

src / components / game.js

  ((()=> { 
const html =`
...
`
  Vue.component(“ game”,{ 
模板:html,
mixins:[window.app.mixins.control],
...
方法() {
setupBoard(){
this.newGame()
this.registerControl()
}
  } 
  }) 
}))()

我们在这篇博客文章中已经走的很远了! 在本系列博客的第2部分中,我们将逐步介绍如何:

  1. 使用Vue.js将FLIP动画添加到图块运动中
  2. 使用Vuex跟踪分数和游戏状态。