Web版俄罗斯方块,500行代码实现

Hello,I'm Shendi;


做了一个Web版的小游戏,也是我制作的第一个H5小游戏

更多实战内容请进入我的实战专栏https://blog.csdn.net/qq_41806966/category_9656338.html

目录

简介

效果视频

解析

游戏规则

页面的制作

绘制背景

画布的初始化

方块对象

游戏逻辑执行

按键监听

交互事件(*)

思路

结果判断,加分机制

暂停游戏

游戏结束

在线试玩

源码


简介

拥有俄罗斯方块的基本功能

方块颜色随机

掉落速度会随着时间增长而加速

直接落地

实现了暂停功能

实现了虚拟按键

下一个方块的显示

效果视频

首先上制作和效果视频---基本上看完视频就能了解到制作过程了

比较简单,麻烦的是方块的变换部分都需要自己一个个去实现(使用矩阵旋转效果不好)

H5俄罗斯方块


解析

整个项目只有两个文件一个 html 文件,一个 js 文件

主要的地方就是 canvas,如果对canvas不熟可以先看下这篇文章

https://blog.csdn.net/qq_41806966/article/details/103779839

游戏规则

  • 宽: 10
  • 高: 20
  • 一横排满则消除
  • 最后一个方块到了顶端则游戏结束
  • 七个方块

为了更好地体验,方块下降速度应该随着时间增长而加速

页面的制作

整体页面只有一个 canvas 和 两个 div

两个 div 分别为暂停界面和结算界面

代码片段如下

html页面的js片段

上面代码就是提供了显示暂停界面和结束界面的方法

以及对应界面的按钮点击后做的操作,也就是 ui 方面的 js 代码

主要的是 game.init 这一行,初始化整个画布

绘制背景

我将绘制背景的方法放到了 game 对象中

上面的代码,前三行是绘制背景,黑色

然后绘制场景的四条线

接下来的代码就是绘制按键了

画布的初始化

我将关于游戏处理的一些方法都放到了 game 对象中

html 页面会执行 game对象的init方法,传递canvas对象

此方法会先判断canvas是否为空,后面重新开始游戏不需要再次传递参数了

并且将画布的context获取

当获取到画布和画笔后,会先绘制背景,然后绘制开始游戏

接下来给画布添加点击事件,用于点击虚拟按键(位置是自己通过输出试出来的)

最后会初始化一些变量,包括了分数,速度,时间,场景

当点击开始游戏的时候会执行 game对象的 run 方法

主要的操作都在 start 方法中

方块对象

我创建了两个方块对象,一个是当前方块,一个是下一个方块

提供了 spawn 方法用来生成方块信息

当前方块代码如下

当前方块的信息都是从下一个方块的信息拷贝

当生成了当前方块,会生成新的下一个方块

下一个方块多了颜色集合

在生成方块的时候,会生成一个随机的方块,生成位置是预定义的

在生成方块中有一个init方法用于初始化一些方块信息

然后就是一些固定的位置设置

游戏逻辑执行

start() 方法主要用于界面的渲染

当游戏开始后,我们需要一直刷新整个canvas

在 game 对象中有一个二维数组代表场景

我们的方块一开始是不在场景内的,当方块完成放置的时才会加入场景

也就是说,场景内只有已放置的方块信息,具体值是颜色信息

具体操作

  • 绘制背景
  • 绘制当前方块
  • 绘制下一个方块
  • 绘制现有方块
  • 绘制时间,分数
  • 自动下降
  • 根据时间调整速度
  • 递归

代码如下

/** 开启游戏 */
	start: function() {
		// 游戏执行逻辑
		game.bg();
		// 分数, 游戏事件
		game.ctx.fillStyle = "white";
		game.ctx.fillText("分数:" + game.scope, 120, 60);
		game.ctx.fillText("坚持时间:" + Math.round(game.time), 250, 60);
		
		// 渲染方块
		game.ctx.fillStyle = cur.color;
		for (let i = 0; i < cur.num; i++) {
			game.ctx.fillRect(game.minX + game.size * cur.arrX[i], game.minY + game.size * cur.arrY[i], game.size, game.size);
		};
		// 渲染下一个方块
		game.ctx.fillStyle = nextCur.color;
		for (let i = 0; i < nextCur.num; i++) {
			game.ctx.fillRect(game.minX + 180 + game.size * nextCur.arrX[i], game.minY + game.size * nextCur.arrY[i], game.size, game.size);
		};
		// 渲染现有方块
		for (let i = 0; i < game.scene.length; i++) {
			for (let j = 0; j < game.scene[i].length; j++) {
				if (game.scene[i][j] != null) {
					game.ctx.fillStyle = game.scene[i][j];
					game.ctx.fillRect(game.minX + j * game.size, game.minY + i * game.size, game.size, game.size);
				}
			}
		}
		
		// 自动下降
		if (game.num++ == game.speed) {
			game.num = 0;
			gameEnv.down();
			
			// 调整速度, 最快为0.1s
			if (game.speed != 2) {
				game.speed = 10 - Math.round(game.time / 30);
			}
		}
		
		game.time += 0.05;
		
		// 游戏暂停则不继续
		if (!game.isPause) setTimeout("game.start()", 50);
	},

按键监听

交互事件(*)

封装成了一个对象 gameEnv

里面有 变换,加速,左,右,直接到底 的方法

思路

当我们触发左事件时,方块会向左移动,因为之前的设计是将当前方块作为一个单独的对象,所以我们只需要修改对象中的坐标位置就行

  • 向左 x-1,当到边界不能往左
  • 向右 x+1,与向左同
  • 向下 y+1,当碰到其他方块或者到底部则完成放置
  • 向上,变换 顺时针旋转90度,需要自己一个一个去实现(大部分都是这么做的),除去正方形,需要实现6*4=24个if块
  • 直接到底,往下的操作需要判断,正好我们封装了向下的操作,我们只需要在向下方法中返回一个值,让我们知道是否放置完成就可以轻松实现此需求

结果判断,加分机制

当我们完成放置时,需要判断先判断游戏是否结束,然后判断是否有可以消除的方块,是则需要加分

如果游戏结束条件不成立,则开始下一个物体,并判断是否有可以消除的方块

  • 加分机制
    • 一行100
    • 两行200
    • 三行400
    • 四行800

暂停游戏

在暂停方法中,会去调用一个名 pause 的方法

游戏结束


在线试玩

https://1711680493.github.io/Tetris/index.html

源码

在我的 github 中的 Tetris 中,可以点下方链接进入,查看里面的介绍获取

https://github.com/1711680493/Application

别忘了点 Star~

猜你喜欢

转载自blog.csdn.net/qq_41806966/article/details/108610114