清明假期期间,闲的无聊,就做了一个小游戏玩玩,目前游戏逻辑上暂未发现bug,只不过样子稍微丑了一些-.-
项目地址:https://github.com/Jiasm/tetris
在线Demo:http://blog.jiasm.org/tetris/?width=16&height=40 (修改URL参数可以调整难度)

整体分成三块进行开发,使用面向对象式编程进行开发(其实我更喜欢用函数式编程,但苦于游戏的一些状态用对象来存储会更直观一些):

  1. Game

    1. 负责生成新的方块
    2. 负责方块移动的处理
    3. 方块触底的判断
    4. 移除满足清除条件的行
  2. Render
    1. 负责用Game的数据来渲染整个游戏界面
  3. Controller
    1. 负责接受用户输入(上下左右各种操作)并处理
    2. 向用户反馈当前游戏的状态

这样分层带来了一个好处,我们游戏的逻辑Game模块并不依赖于当前程序运行的环境,而Render可以是CanvasDOM,甚至是控制台输出。我们要移植到其他平台,只需要修改Render即可。

项目结构

忽略了一些与游戏没有直接关系的结构

.
├── model
│   ├── Brick.js
│   ├── Game.js
│   └── index.js
├── utils
│   ├── buildEnum.js
│   ├── deepCopy.js
│   ├── getShape.js
│   ├── index.js
│   ├── lineIndex.js
│   ├── matrixString.js
│   └── rotateArray.js
├── enum
│   ├── gameType.js
│   ├── index.js
│   └── pointType.js
├── data
│   └── shapes.js
├── controller
│   └── index.js
└── view├── RenderCanvas.js└── index.js

各目录下的index.js是为了方便同时引用多个文件,大致长这个样子:

export { default as model1 } from './model1'
export { default as model2 } from './model2'

然后我们就可以在用到的地方写:

import { model1, model2 } from './XXX'

model

这里是游戏的核心逻辑所在位置。

像俄罗斯方块这种的矩阵类游戏,存储数据最合适的方法就是一个二维数组了。
为了更直观一些,我们选择了游戏的高度作为第一层数组的长度:

matrix = new Array(height).fill(new Array(width))// width: 2 height: 4
[[ 1, 1],[ 1, 1],[ 1, 1],[ 1, 1]
]

而且这样选择在一些逻辑处理上也会更方便一些:

  1. 下移操作时,我们只需改变元素的第一层下标
  2. 判断是否触底时,我们只需将当前下标 + 1 判断是否有元素即可

我们对数组中的元素进行了定义:

  • 0: 空,表示当前坐标为空白
  • 1: 新的方块,表示当前活动的方块
  • 2: 老的方块,已经触底固定的方块

接下来,我们就遇到了一个问题,如何处理方块的放置。
我们知道,游戏会不停的向棋盘中加载新的方块。
如果我们每次处理下移的时候,都将当前二维数组中对应的方块元素移除,然后在塞入到新的位置,未免太过繁琐了。

所以我们在初始化数据时,初始化两个二维数组。
当我们加载一个新的方块后,将方块对应的元素塞入其中的一个二维数组。
然后等到我们有进行其他的操作时,比如左右移动,向下之类的。
我们直接使用第二个二维数组覆盖到当前的数组中去,然后再将更改下标后的方块塞入数组。
这样在数据上,我们就完成了方块的移动。

class Game {init () {// 初始化两个矩阵this.matrix = [[], []]this.oldMatrix = [[], []]}move () {// 重置当前矩阵数据this.matrix = deepCopy(this.oldMatrix) // 解除引用// 加载方块数据this.matrix[y][x1] = 1this.matrix[y][x2] = 1}
}

左右移动的处理

左右的移动不能像向下移动一样,单纯的下标+1。
我们需要判断当前的操作是否有效。
比如右侧如果遇到了障碍物或者到达边缘,我们肯定是不能够再进行移动的。

// blend 为活动砖块的形状描述 [[1, 1, 1], [0, 1, 0]] 类似这样的结构
if (x >= width - brickWidth ||blend.some((row, rowIndex) => {let _pos = oldMatrix[y + rowIndex]return row && row[brickWidth - 1] && _pos && _pos[x + brickWidth]})
)return // 右侧有障碍物,无法移动

使用类似这样的逻辑进行判断,保证当前方块向右移动后不会覆盖之前的方块。

快速向下的处理

我看有些游戏实现的,貌似下降触发只是加速下降而已(这种情况只需要改变定时下降的速度即可)-.-这里的实现是,直接触底

所以就会遇到一个问题,当前砖块最多可以下降到什么位置?

[1, 1, 1] [0, 0, 0] [0, 2, 0] [2, 2, 2] 

就像这样的一个数据,0|2这两列都可以向下移动两列,但是这样就会导致中间一列的重叠。
我们一定要取出下降幅度最小的那个值。
所以我们就要算出最后一行1的下标以及第一行2的下标,将这两个下标进行相减,最小值即为我们当前方块可下降的距离。

旋转方块的处理

旋转方块应该是游戏中比较复杂的一块逻辑了。
绝不是仅仅简单的将方块的二维数组由行改为列,在有些时候,我们还需要判断方块是否可以进行旋转。

就像这样的,中间的绿色长条是不能够进行旋转的。
所以我们要先拿到旋转后的数据,来与当前游戏中的数据进行比较,检验是否会出现重叠的情况,如果出现了,则表示不能够进行旋转。

触底检测

每完成一个移动的动作后,我们都需要进行方块的触底检测。
也就是判断当前方块下,是否已经有元素占位,如果有的话,则表示已经触底了,当前元素就会被固定进矩阵数组中。
同样的,我们在判断时,不需要将方块所有的下标都检查一遍,只需要检查最底部一层的有效元素即可。

[1, 1], [0, 1], [0, 1], 

像这样的一个方块,我们仅需要判断第一列的第二行&第二列的第四行是否有元素即可完成检查。

移除行

当某一行被填满元素后,我们就要将它进行移除。
在触底检测触发后,如果有方块被固定进数组,此时我们再进行移除行的操作。
因为如果没有新的方块进入,移除行的这步操作就不是必要的。
同时,得分的计数也应该在此处进行,我们将移除的行数进行记录,获取到的行数便是得分了。

至此,所有有关矩阵数据的操作就结束了。
Game对象只去维护这么一个二维数组,对象本身不包含任何游戏相关的操作,只会在被调用时进行对应的处理。
然后生成新的二维数组。

utils

这里放置了一些比较通用的方法,用来提高开发效率使用。
比如获取方块最底部一层的下标之类的工具函数。

enum

存放了一些状态的枚举,游戏状态以及方块所对应的状态,类似这样的数据:

{empty: 0,newBrick: 1,oldBrick: 2
}

data

存放了游戏中各种使用到的方块信息。
正方形,梯形之类的方块在二维数组中所对应的描述。

controller

就是上边我们所说的,用来与用户交互的模块,由Controller来获取游戏相关的信息,并调用Render进行渲染。
监听键盘事件,在页面中渲染一些控制按钮。
以及定时触发Game的下落方法。

view

游戏界面的渲染部分,目前选定的是使用canvas,所以只写了RenderCanvas
在渲染的这部分,稍微做了一些优化处理,将活动中的方块与固定的方块进行分开渲染。
这样在用户操作上下左右移动时,并不会重新渲染整个游戏布局,而只是渲染活动方块的canvas

小记

两天多的时间进行开发,其中有半天时间在修复FlowType的Warning提示。。。
搞完了以后,觉得实现这个的主要难点就在于方块旋转&触底的判断这里了。
能够清晰的管理游戏对应的二维数组,这个游戏开发起来就会很顺畅。

界面还有待优化。

Tips

我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=1vas1z072yivn

转载于:https://www.cnblogs.com/jiasm/p/8733356.html

使用JavaScript实现一个俄罗斯方块相关推荐

  1. php开发俄罗斯方块,用JavaScript写一个俄罗斯方块

    曾经用 Turbo C++ 3.0 写过 DOS 下的俄罗斯方块,不久之后又用 VB 写了另一个版本.这次决定用 JavaScript 再写一个并非完全心血来潮,从技术上来说,主要是想尝试使用 web ...

  2. 60行JavaScript代码写俄罗斯方块

    教你看懂网上流传的60行JavaScript代码俄罗斯方块游戏 早就听说网上有人仅仅用60行JavaScript代码写出了一个俄罗斯方块游戏,最近看了看,今天在这篇文章里面我把我做的分析整理一下(主要 ...

  3. 500行代码写一个俄罗斯方块游戏

    导读:本文我们要制作一个俄罗斯方块游戏. 01 俄罗斯方块 Tetris 俄罗斯方块游戏是世界上最流行的游戏之一.是由一名叫Alexey Pajitnov的俄罗斯程序员在1985年制作的,从那时起,这 ...

  4. python俄罗斯方块算法详解_用 Python 写一个俄罗斯方块游戏 (

    @@ -2,34 +2,34 @@ > * 原文作者:[Dr Pommes](https://medium.com/@pommes) > * 译文出自:[掘金翻译计划](https://g ...

  5. 使用JavaScript实现一个简单的编译器

    本文同步在个人博客shymean.com上,欢迎关注 在前端开发中也会或多或少接触到一些与编译相关的内容,常见的有 将ES6.7代码编译成ES5的代码 将SCSS.LESS代码转换成浏览器支持的CSS ...

  6. 500 行代码写一个俄罗斯方块游戏

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 导读:本文我们要制作一个俄罗斯方块游戏. 作者 | 派森 ...

  7. HTML CSS JavaScript 从一个表格到一个灰阶颜色表(目录)

    HTML & CSS & JavaScript 从一个表格到一个灰阶颜色表 01 HTML & CSS & JavaScript 从一个表格到一个灰阶颜色表 02 HT ...

  8. 谷歌技术专家关于JavaScript的一个引人注目的观点

    谷歌的一位技术专家曾经和我分享过关于 JavaScript 的一个引人注目的观点:它不是真正的内聚编程语言--至少不是正式意义上的内聚编程语言. ECMA-262规范定义了JavaScript,但没有 ...

  9. JavaScript产生一个n到m之间的随机数

    JavaScript产生一个n到m之间的随机数 算法源码: // 实现方法function getRandomInt(n, m) {return Math.floor(Math.random() * ...

  10. 请用JavaScript实现一个函数,接受一-个IP白名单列表whitelist以及列表ipList

    请用JavaScript实现一个函数,接受一-个IP白名单列表whitelist以及 列表ipList,判断输入的ipList中是否有任何ip包含在whitelist中,如果存在返回true,如果都不 ...

最新文章

  1. 「DB」数据库事务的隔离级别
  2. 在夕阳下第一次写文章的菜鸟
  3. margin-top的百分比是相对父元素的哪个值
  4. 腾讯云入选云原生产业联盟首批理事单位 助力生态发展与产业落地
  5. 形态学操作之提取水平与垂直直线
  6. C# 如何处理抛出的异常,或者已知的错误
  7. SGU[222] Little Rooks
  8. Android和iPhone浏览器大战,第1部分,WebKit抢救
  9. H3C交换机堆叠配置
  10. 在网页中实现透明flash的代码
  11. Java实现浏览器下载文件
  12. mysql数据库原理设计与应用在线pdf_《数据库原理与应用》[51MB]PDF完整版下载-码农之家...
  13. Openwrt PPTP ALG功能
  14. matlab练习程序(图像放大/缩小,双线性插值)
  15. 两个pdf合并成一个pdf,操作方法
  16. Java基础3-循环
  17. flutter 人脸识别_使用flutter和tensorflow lite进行人脸识别认证
  18. MyEclipse解决SVN同步冲突问题conflict in the working copy obstructs the current operation
  19. 人体冷冻技术科学家称四十年内实现冷冻后复活,实现起死回生和长生不死
  20. Easy2D 轻量级游戏开发框架(1)

热门文章

  1. python音译爬虫_Python爬虫入门案例:获取百词斩已学单词列表
  2. 港股分时交易数据 API 接口
  3. 【腾讯云技术沙龙预告】云端数据库的设计之美
  4. html anki编辑器,打造你的专属单词本——Anki 初探(无痛入门)
  5. 银行卡四要素检测 API接口数据
  6. 真正的落雷(打印图形练习题)C语言
  7. GitHub上README.md文件的图片大小尺寸
  8. 马尔可夫不等式 Markov's inequality
  9. 数据分析师的薪资大约有多少?
  10. 读懂职场则规潜 潜伏者的上位秘诀