翻牌子游戏的实现思路

我正在参加掘金社区游戏创意投稿大赛个人赛,详情请看:游戏创意投稿大赛

前言

  这个游戏想必大家都没有见过吧,我这里给它取个名字叫翻牌子, 也比较贴切,就是点击一张牌子,然后将牌子周围的其它牌子翻转过来,包括上下左右的牌子,当然特殊的四个角落或者边缘的牌子,点击的时候呢,周围的翻转牌子数量就不一定是四个了。

image.png

思路

  我们来说以下具体的实现思路,假设这些牌子的周围有一个大盒子,这里我给的是540 * 540px,然后默认size5 * 5的,这里的size5,你也可以自己修改。

  最外层的大盒子开启相对定位,然后里面需要渲染size * size个元素,也就是5 * 5 = 25个元素,这些个元素需要在这个盒子中定位放置。

<div
  v-for="(item, index) in list"
  :key="index"
  class="item"
  :style="setPostion(index)"
></div>

init() {
  this.list = new Array(size * size).fill(0).map(e => ({ v: false }))
},
复制代码

  因此我们有一个专门的定位函数setPostion,用来根据具体的每个元素的索引号,来定位它们的位置。其中~~双非运算符表示取整,以下可能看不太懂。我直接举个栗子就好明白了,假设元素的索引值为6,那么7 / 5取整为17 % 5取余为2,因此就是(1, 2)

setPostion(index) {
  const top = ~~(index / size)
  const left = index % size

  return {
    left: `${left * 110}px`,
    top: `${top * 110}px`,
  }
},
复制代码

  所以6的元素的定位就是,110 * 2 = 220px110 * 1 = 110px,故left220top110px

image.png

  定位好了之后就是翻牌子的问题了,刚才也说了,理想情况是点击的元素和周围的四个元素都翻牌子。

image.png

  但是注意特殊的地方,四个角,四个边。其中result为需要翻转的索引,然后无非就是另外四个索引,分别为index - 1index + 1index + 5index - 5。以下四个if判断详细讲一下,index % size === size - 1是右边的一个边,右边的边没有index + 1的索引值,因此只有当index % size !== size - 1的情况时,result中才能push这个索引值,也就是index + 1,然后另外三个边也是同理的。

handleClick(index) {
  const result = [index]

  if (index % size !== size - 1) {
    result.push(index + 1)
  }

  if (index % size !== 0) {
    result.push(index - 1)
  }

  if (~~(index / size) !== 0) {
    result.push(index - 5)
  }

  if (~~(index / size) !== size - 1) {
    result.push(index + 5)
  }

  this.list.forEach((el, index) => {
    if (result.includes(index)) {
      el.v = !el.v
    }
  })
},
复制代码

  拿到result后,我们就去遍历25个元素,当其中有一个元素的索引值在result中,我们就将它的v值去翻,实际上就是翻转。

  为了翻转好看一点,我们给v值为true的元素bg类名,且角度为Y轴的180度,默认元素是0度,然后加上transition: all 0.6s;的过渡效果。由于是all,因此背景色也会存在过渡效果。

image.png

  最后,怎么判断完全翻转呢,很简单,当list列表中的每个元素的v值都是true的时候,表示已经翻转完全了,游戏就通过了,这里我们用every是非常合理的。注意为什么需要setTimeout延时呢,主要是想等到过渡动画完全消失时再提示游戏通过,当然你也可以利用transitionend事件来判断成功与否。

if (this.list.every(el => el.v)) {
  setTimeout(() => {
    alert('成功')
    this.init()
  }, 600)
}
复制代码

  最后贴一下完整代码。

<template>
  <div id="app">
    <div
      v-for="(item, index) in list"
      :key="index"
      class="item"
      :style="setPostion(index)"
      :class="{ bg: item.v }"
      @click="handleClick(index)"
    ></div>
  </div>
</template>

<script>
const size = 5

export default {
  name: 'App',
  data() {
    return {
      list: [],
    }
  },
  mounted() {
    this.init()
  },
  methods: {
    init() {
      this.list = new Array(size * size).fill(0).map(e => ({ v: false }))
    },

    handleClick(index) {
      const result = [index]

      if (index % size !== size - 1) {
        result.push(index + 1)
      }

      if (index % size !== 0) {
        result.push(index - 1)
      }

      if (~~(index / size) !== 0) {
        result.push(index - 5)
      }

      if (~~(index / size) !== size - 1) {
        result.push(index + 5)
      }

      this.list.forEach((el, index) => {
        if (result.includes(index)) {
          el.v = !el.v
        }
      })

      if (this.list.every(el => el.v)) {
        setTimeout(() => {
          alert('成功')
          this.init()
        }, 600)
      }
    },

    setPostion(index) {
      const top = ~~(index / size)
      const left = index % size

      return {
        left: `${left * 110}px`,
        top: `${top * 110}px`,
      }
    },
  },
}
</script>

<style lang="scss" scoped>
#app {
  width: 540px;
  height: 540px;
  margin: 0 auto;
  border-radius: 5px;
  margin-top: 50px;
  position: relative;
}

.item {
  width: 100px;
  height: 100px;
  border-radius: 5px;
  background-color: #00ccff;
  box-sizing: border-box;
  position: absolute;
  transform: rotateY(0deg);
  transition: all 0.6s;

  &.bg {
    background-color: pink;
    transform: rotateY(180deg);
  }
}
</style>
复制代码

  再放上一张完整通关图,实际这个游戏是有难度的。

image.png

猜你喜欢

转载自juejin.im/post/7088230286664269838