当前位置:   article > 正文

基于Vue.js的2048小游戏的设计与实现_vue 实现2048小游戏源码

vue 实现2048小游戏源码

游戏规则

  1. 游戏盘内有16个方格,初始状态是有两个值为2或4的数字格
  2. 指定向↑ ↓ ← →方向滑动,则所有方块都会向该方向滑动
  3. 相碰撞的两个数字格的值相等则会将其合并
  4. 每次滑动都会在随机空位上生成一个值为2或4的数字格
  5. 若游戏盘的16个方格均被填满且无法移动,则判定为游戏失败
  6. 若有一数字格的值为8192,则游戏通关

一、准备工作

1. 项目结构

在这里插入图片描述

2. 入口组件App.vue

这一块没什么好说的,就是给游戏提供入口,由于想照顾兼容更多情况引入了vue-router。

我用到了SCSS主要是为了让css写起来简洁一点,为了好看点,个人习惯用到了苹方字体,可以不必在意。

<template>
  <div id="app">
    <router-view />
  </div>
</template>

<style lang="scss">
@import "./assets/font/font.css";
body {
    
  margin: 0;
  font-family: "PingFang-RE", "Montserrat-RE", "Microsoft YaHei";
  button,
  input {
    
    font-family: "PingFang-RE";
  }
}
#app {
    
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}
</style>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

3. 路由文件router -> index.js

这一块没什么好说的,主要是把组件引入进来应用到App里面的router-view

import Vue from 'vue';
import VueRouter from 'vue-router';
import Home from '../views/Home.vue';

Vue.use(VueRouter);

const routes = [
  {
   
    path: '/',
    name: 'Home',
    component: Home,
  },
];

const router = new VueRouter({
   
  routes,
});

export default router;

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

二、界面布局

1. 游戏盘背景 Background.vue

这里我用一个v-for循环把16个格子展现出来

background就是用的Grid布局,分成四行四列

<template>
  <div class="background">
    <div v-for="i of 16" :key="i" class="grid-cell"></div>
  </div>
</template>

<script>
export default {
    };
</script>

<style lang="scss" scoped>
.background {
    
  width: 365px;
  height: 365px;
  padding: 20px;
  background-color: #bbada0;
  border-radius: 10px;
  display: grid;
  grid-row-gap: 15px;
  grid-column-gap: 15px;
  grid-template-columns: repeat(4, 80px);
  grid-template-rows: repeat(4, 80px);

  .grid-cell {
    
    width: 80px;
    height: 80px;
    border-radius: 5px;
    background-color: #ccc0b3;
  }
}
</style>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

2. 游戏界面的头部信息

在这里插入图片描述

这里也相当简单,我用一个header标签包裹

<header>
    <h1>2048</h1>
    <button @click="init" class="init-button">New Game</button>
    <p>
      Score: <span>{
  { score }}</span>
    </p>
</header>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
header {
   
  h1 {
   
    margin: 0;
    font-size: 32px;
  }
  p {
   
    margin: 0;
    margin-top: 10px;
    font-size: 16px;
    span {
   
      font-weight: bold;
    }
  }
}
.init-button {
   
  width: 110px;
  padding: 10px;
  background-color: #8f7a66;
  color: #fff;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  outline: none;
  font-size: 16px;
  font-weight: bold;
  &:hover {
   
    background-color: #9f8a77;
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

3. 游戏界面主体的编写

用一个container包裹

mask是游戏通关或失败的遮罩

Background是背景以组件形式引入了的标签

number-cells是游戏的数字格子,transition-group是为了给这些格子加过渡动画,数字格我以数组形式存起来然后v-for逐个展示出来

<div class="container">
  <div class="mask" v-if="success">
    <h1>You win!</h1>
    <button @click="init" class="init-button">Try again</button>
  </div>
  <div class="mask" v-if="gameover">
    <h1>Game over!</h1>
    <button @click="init" class="init-button">Try again</button>
  </div>
  <Background />
  <div class="number-cells">
    <transition-group name="appear">
      <div
        class="number-cell"
        v-for="cell of numberCells"
        :id="`c${cell.id}`"
        :key="cell.id"
        :style="
          `
      width: 80px;
      height: 80px;
      border-radius: 5px;
      font-size: 32px;
      font-weight: bold;
      line-height: 80px;
      color: #776e65;

      position: absolute;
      z-index: ${
      cell.num};
      backgroundColor: ${
      cell.color};
      top: ${
      getTop(cell)};
      left: ${
      getLeft(cell)};
      `
        "
      >
        {
  { cell.num }}
      </div>
    </transition-group>
  </div>
</div>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

container包裹了background,然后整个容器用margin来做水平居中

mask以container为基准做绝对定位,用top…等定位来拉伸展开

number-cell(数字格子)也是以container为基准做绝对定位,top和left的数值通过格子的数据结构计算出。

因为合并时可能会存在大数值的格子需要覆盖小数值的格子,所以以数值来作为z-index

然后就是用到了vue的transition以及CSS3的transition,前者是用来在DOM生成时的一个从无到有的动画,后者是数字格的滑动

.container {
   
  width: 405px;
  height: 405px;
  margin: 20px auto;
  position: relative;
  .mask {
   
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 9999;
    background: rgba(238, 228, 218, 0.5);
    text-align: center;
    h1 {
   
      font-size: 60px;
      font-weight: 700;
      height: 60px;
      line-height: 60px;
      margin-top: 120px;
      color: #776e65;
    }
    button {
   
      margin-top: 30px;
    }
  }
  .number-cells {
   
    .number-cell {
   
      transition: $transitionTime top, $transitionTime left;
      // animation-fill-mode: backwards;
      // animation: appear 200ms ease-in-out;
    }
  }
}
.appear-enter-active {
   
  animation: appear 100ms ease-in-out;
}
.appear-leave-active {
   
  transition: $transitionTime top, $transitionTime left;
}
@keyframes appear {
   
  0% {
   
    opacity: 0;
    transform: scale(0);
  }
  50% {
   
    opacity: 0;
    transform: scale(0.5);
  }
  100% {
   
    opacity: 1;
    transform: scale(1);
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
getTop(cell) {
   
  return `${
     20 + cell.y * 95}px`;
},
getLeft(cell) {
   
  return `${
     20 + cell.x * 95}px`;
},
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

三、游戏初始化设置以及数据结构的定义

1. 游戏所需数据

score记录游戏分数

numberCells是一个存放数字格的数组,在上面提到数字格是由这个数组生成的

color就是为了后续获得数字格对应的背景色而定义的

auxId是一个用来辅助唯一标记数字格的id的,后续会介绍

success是标记游戏是否通关

gameover标记游戏是否结束

canMove后面介绍,是一个辅助判断的变量

data() {
   
  return {
   
    score: 0,
    numberCells: [],
    color: {
   
      2: '#eee4da',
      4: '#ede0c8',
      8: '#f2b179',
      16: '#f59563',
      32: '#f67c5f',
      64: '#f65e3b',
      128: '#edcf72',
      256: '#edcc61',
      512
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/674541
推荐阅读
相关标签
  

闽ICP备14008679号