前言

最近都没有怎么写过文章,都断更很久了吧。学习前端一年多,快两年了,学习的热情相比一开始,自我感觉没有变化多少,但是
学习的动力却好像时有时无。

就好像是没了目标一样,不知道自己现在应该学些什么,从哪里提升自己,是为了工作,还是为了程序员这个职业。

自己沉迷一段时间,真的出现一种躺平的状态。今天不知道怎么了,突然想起自己好久没有写过博客了,一边想着想写什么,一边找着以前自己的github项目,
偶然间看见自己以前给别人写的一个推箱子小游戏,就写下了这篇文章。

思路

在我们平时玩的推箱子游戏中,一般是由人,箱子,终点,在终点的箱子,空地和墙这几个元素组成,由玩家控制人的上下左右移动从而推动箱子,只要所有的箱子都被推到终点,就算是游戏结束。当然,完成基本的游戏功能之后,我们可以自己在展开想象,给自己写的东西添加一些有趣的元素。

规则

确定了基本元素,接下来就要确定游戏规则,这里我只是完成了最基本的规则,也可以自己适当的添加自己想要的规则上去:
1. 人在空地上显示人,箱子在空地上显示箱子,终点在空地上显示终点;
2. 人只能推动箱子,若箱子前面有障碍物(墙,箱子之类的),则无法推动;
3. 人,箱子,终点不能离开墙内;

能想到的规则目前就是这些,不是很详细,但是也没关系,反正我们对这款游戏都是比较熟悉。

实现思路

1. 首先是素材,并不难找,百度一下就可以找到基本完成游戏的一套素材,可能需要自己裁剪一下之类的。
2. 人的移动,这一块比较容易实现,我们可以定下键盘按下什么键,对应着什么样的操作,而我们只需要监听键盘按下的动作即可。
3. 接下来就是这个游戏中唯一的难点,如何走动这个动作。就比如,人经过一个空地,那么这里就会显示为人,人离开了,又会显示为空地,对于箱子,终点也是一样的。

物体移动

在这里,我是将每一元素设置为一个数值。首先是将最基础的元素设置值,如 1 表示箱子。下面的代码就是我对不同状态下的元素进行的一个简单配置:
mapElementName: {
    0: {
      name: '墙内空地',
      icon: require('@/assets/image/墙内空地.png'),
      type: 'move' // 可移动类型,表示任何情况下可以移动
    },
    1: {
      name: '箱子',
      icon: require('@/assets/image/箱子.png'),
      type: 'MF' // move&&fix,表示不确定类型,可移动或者是不可移动
    },
    2: {
      name: '终点',
      icon: require('@/assets/image/终点.png'),
      type: 'move'
    },
    3: {
      name: '箱子&&终点',
      icon: require('@/assets/image/箱子&&终点.png'),
      type: 'MF'
    },
    10: {
      name: '人',
      icon: require('@/assets/image/人.png'),
      type: 'fix'
    },
    12: {
      name: '人',
      icon: require('@/assets/image/人.png'),
      type: 'fix'
    },
    109: {
      name: '人',
      icon: require('@/assets/image/人.png'),
      type: 'fix'
    },
    98: {
      name: '墙',
      icon: require('@/assets/image/墙.png'),
      type: 'fix'
    },
    99: {
      name: '墙外空地',
      icon: require('@/assets/image/墙外空地.png'),
      type: 'move'
    }
  },
简单解释就是:2表示终点,10表示人,人移动到终点那里,就是10+2=12,那么12表人移动到终点的时候,人一旦离开,就会是12-10=2,2表示终点

上面的思路理解了,接下来就可以写代码了。

代码实现

监听键盘上下滚动

// 循环判断当前的人所处的位置
      this.mapArray.forEach((item, index) => {
        item.forEach((array, temp) => {
          if (this.mapElementName[array].name === '人') {
            this.manPosition = [index, temp]
          }
        })
      })
      //  判断点击的键盘
      // 传递的参数:人所在的位置, 人要移动到的位置的相关元素信息以及下一个信息
      const man = this.manPosition
      const array = this.mapArray
      const name = this.mapElementName

      switch (e.keyCode) {
        case 38:
          this.toTop(name[array[man[0] - 1][man[1]]], man[0] - 2 < 0 ? false : name[array[man[0] - 2][man[1]]])
          break
        case 40:
          this.toBottom(
            name[array[man[0] + 1][man[1]]],
            man[0] + 3 > this.maxRow ? false : name[array[man[0] + 2][man[1]]]
          )
          break
        case 37:
          this.toLeft(name[array[man[0]][man[1] - 1]], man[1] - 2 < 0 ? false : name[array[man[0]][man[1] - 2]])
          break
        case 39:
          this.toRight(
            name[array[man[0]][man[1] + 1]],
            man[1] + 3 > this.maxColumn ? false : name[array[man[0]][man[1] + 2]]
          )
      }
当键盘敲击的时候,因为我们控制的是人,所以先循环判断当前人所处的位置,然后在进行移动。这段代码可以很明显看出来,每次敲击键盘的时候都要去循环找出人的位置,这一点处理得不合理,我们可以
定义一个变量将当前人的位置储存起来。(以前的代码,不想改动,只做说明:clown_face::clown_face:)

接着根据点击不同的键进行不同的操作,下面是向下操作的代码(其他的操作类型):
      //  1.人的下边是墙
      if (next.type === 'fix') {
        return
      }
      //  2.人的下边是箱子,箱子的下边是箱子或者是箱子&&终点或者是墙
      if (next.type === 'MF' && doubleNext) {
        if (doubleNext.type === 'MF' || doubleNext.type === 'fix') {
          return
        }
        //  开始移动
        //  人的位置减去10,下面的位置减去箱子加上人,下下面的位置加上箱子
        this.mapArray[this.manPosition[0]][this.manPosition[1]] -= 10
        this.mapArray[this.manPosition[0] + 1][this.manPosition[1]] -= 1
        this.mapArray[this.manPosition[0] + 1][this.manPosition[1]] += 10
        this.mapArray[this.manPosition[0] + 2][this.manPosition[1]] += 1
      }
      //  3.人的下边是空地或者是终点
      if (next.type === 'move') {
        //  人的位置减去10, 下边位置加上10
        this.mapArray[this.manPosition[0]][this.manPosition[1]] -= 10
        this.mapArray[this.manPosition[0] + 1][this.manPosition[1]] += 10
      }
      //  刷新页面
      this.$forceUpdate()
嗯,有这么几种情况,但当我看到 this.$forceUpdate(),不自禁的摇了摇头

大致上,一个基本的推箱子游戏就完成了,整体上难度不大,适合练手。最终的效果:

 拓展

因为苦于推箱子的地图搜集(太耗时间了,而且不同的人心里面可能有着自己想过的关卡),同时为了增加游戏的灵活性,我当时自己有添加了
自己diy的模式,提供玩家自己设计地图。实现的思路大致上分为下面几步:
1. 提供选项,自己选择地图的大小;
2. 根据玩家的选择的地图大小,生成对应大小的地图方格,每个方格默认为墙内的空地,玩家可以点击方格,会出现可以设置为地图某个元素的选项,自由进行设计。
每个方格可以设置的元素不一样,是为了保证玩家设计出来的地图是可以玩的。当然,可以自由改动代码,生成的地图可以自由改变。

实现效果:

 最后

从整体的实现上来看,推箱子的逻辑并不复杂,当然,因人而异,你们也可以设计得十分复杂。献上我的项目地址

原文链接

Logo

前往低代码交流专区

更多推荐