webgl实现3D俄罗斯方块

文章同时发布于王鹏飞的个人网站。

试玩地址:3d tetris。

源码: https://github.com/pengfeiw/3d-tetris

学习webgl/opengl已经有一段时间了,打算做个小程序练一练,所以就有了这个俄罗斯方块。采用的技术是webgl + typescript + canvas,至于界面使用的是react + material-ui框架。下面我将介绍一些实现的技术点。

一.介绍

界面左侧是可以打开或者关闭的设置面板。可以通过distance和rotate调整界面中视角和距离,以适应不同用户的视觉习惯。下面也讲解了基本操作按键,旋转、左移、右移、加速以及暂停。中间是游戏主体部分,是一个canvas,通过webgl实现游戏内容的绘制。为了使图块视觉效果明显,我加入了平行光照,让方块更真实一点。右侧是当前游戏信息和按钮控制,右侧的图块预览也是一个canvas,不过是canvas的二维绘制,通过CanvasRenderingContext2D实现预览效果。

二.俄罗斯方块实现思路

我不讲解具体的技术细节,我只讲一些功能是如何实现,比如碰撞检测算法。对细节感兴趣想要学习的朋友,可以下载我的源码参考学习。

图形方块的绘制——旋转、移动

一个俄罗斯方块游戏,有很多不同的形状,典型的有I、O、S、Z、L、J、T几种。

可以用一个4x4的区域绘制这些形状,所有的形状都在这个4x4的区域范围内。这里采用二进制01的思想,将4x4的区域内都赋予0和1,如果是0表示这一个cell不需要绘制,如果是1表示需要绘制,那么一个s形的block可以表示成如下几种形态:

这里为什么是四个,而不是两个呢?是因为从左至右表示的是每次旋转90度后的形态,恰好s形旋转180度后又和原来的形态重合了。为了方便记下这一组状态,可以使用一个16位的二进制数存储,用16进制表示。那么这7个形状的表示如下:

const shapeO: ShapeData = [0x0660, 0x0660, 0x0660, 0x0660];
const shapeI: ShapeData = [0x4444, 0x0F00, 0x4444, 0x0F00];
const shapeZ: ShapeData = [0x0c60, 0x2640, 0x0c60, 0x2640];
const shapeS: ShapeData = [0x0360, 0x4620, 0x0360, 0x4620];
const shapeL: ShapeData = [0x4460, 0x0E80, 0x6220, 0x0170];
const shapeJ: ShapeData = [0x2260, 0x08E0, 0x6440, 0x0710];
const shapeT: ShapeData = [0x04E0, 0x4640, 0x0720, 0x2620];

使用这种方式的好处就是,每次当我们进行旋转操作,只需要将当前的index + 1既可得到下一个形态。假设当前正在移动的block是ShapeI,并且此时的形态是0x0F00,位于数组中的第二个(索引值为1),此时再旋转一次,我们将索引加1,得到此时的形态为0x444。我们只需要记住上面的shadpeData数组,以及此时的数组中的形态索引shapeIndex,就可以得到此时的形状。
知道了如何绘制block的形状,我们还需要知道block此时的位置。我们可以将block的移动区域分成宽和高固定的格子,例如宽10,高20。那么只需要记住当前block所属的4x4的区域的坐标即可,可以记左上角坐标,也可以是左下角,只要能定位即可。有了block的坐标就可以轻松的实现图块的移动了。
图块的自动下落的功能,通过设置block的坐标y随时间自动变化,并且还可以设置一个下落速度,随着游戏分数的增加而增加。图块左右移动可以设置block的坐标x随键盘或者按钮的控制而变化。

block移动区域的绘制

作为block主要移动区域,也就是我游戏中的网格区域,每当一个block下落到最下方,需要重新绘制已固定的block。那么我如何知道那些网格区域已经有block存在,并且需要绘制呢?聪明的你一定想到了,同样可以用0和1做记号,如果是1表示网格区域上方已经有block了,需要绘制block。用二进制的思想是不是很方便的解决了我们的难题。接着往下看。

碰撞检测

上面讲解了,如何去记录当前激活的图块的形态及坐标,也知道了使用0和1的方式去记录网格区域的状态,通过状态我们可以绘制出当前的游戏画面。那么有一个问题,就是我们怎么确定block移动到最下方就不能继续向下移动了,怎么检测block的左右侧或者下方是否以经存在固定的block了,导致无法移动。这就需要我们做碰撞检测了。
这里使用了一种更加巧妙的方式了,同样使用二进制的方法。因为我们使用了0和1记录了当前网格中所有的状态,所以我们可以使用与(&)操作符进行碰撞检测。具体做法是,如果此时block要向下移动,我们很容易知道其往下一步的4x4的区域(也就是block将要覆盖的4x4的区域),并且获得这个区域的16个二进制0和1的值,组成一个16进制数areaValue。如果当前block的valueareaValue进行与运算,等于0表示可以移动,非0表示不能移动。

例如下图中,绿色表示一个活动方块,此时block的value,用二进制01表示为[0100-0100-0110-0000],这个block往下一步的4x4二进制区域值为[0000-0000-0110-1100]。这两个数值进行与运算后0x0060, 结果不为0,所以有碰撞无法向下继续移动。左右移动可以使用同样的方式。

网格区域的左侧和右侧以及下侧,我们都可以设置为1值,防止block移动到网格区域外。但是因为图块是从上方往下移动的,所以最上面的网格区域外设置成0值。

二进制的优美,在这个游戏中,我是切切实实的感受到了。

关于minicode,我会一直保持更新的,感兴趣的朋友不妨关注一下。你有任何问题,也可以给我留言,也可以留下联系方式加个好友。

(完)

webgl实现3D俄罗斯方块相关推荐

  1. 基于 HTML5 的 WebGL 自定义 3D 摄像头监控模型

    2019独角兽企业重金招聘Python工程师标准>>> 前言 随着视频监控联网系统的不断普及和发展, 网络摄像机更多的应用于监控系统中,尤其是高清时代的来临,更加快了网络摄像机的发展 ...

  2. 分享一个WebGL开发的网站-用JavaScript + WebGL开发3D模型

    这张图每位程序员应该都深有感触. 人民心目中的程序员是这样的:坐在电脑面前噼里啪啦敲着键盘,运键如飞. 现实中程序员是这样的:编码5分钟,调试两小时. 今天我要给大家分享一个用WebGL开发的网站,感 ...

  3. three.js加载3d模型_基于WebGL的3D技术在网页中的运用 ThingJS 前端开发

    Three.js.ThingJS这些引擎库可以加载3D制作软件的模型,大幅度提高了制作效率,改变WebGL开发困难的局面,让Web开发者享受便捷的3D开发服务.三者的难度对比如下: ThingJS(框 ...

  4. c#web页面显示弹窗_基于 HTML5 WebGL 的 3D 风机 Web 组态工业互联网应用

    前言 在目前大数据时代背景之下,数据可视化的需求也变得越来越庞大,在数据可视化的背景之下,通过智能机器间的链接并最终将人机链接,结合软件和大数据分析的工业互联网也将变得越来越容易实现! 国家也敏锐意识 ...

  5. 使用WebGL 自定义 3D 摄像头监控模型

    前言 随着视频监控联网系统的不断普及和发展, 网络摄像机更多的应用于监控系统中,尤其是高清时代的来临,更加快了网络摄像机的发展和应用. 在监控摄像机数量的不断庞大的同时,在监控系统中面临着严峻的现状问 ...

  6. 视频教程-WebGL 可视化3D绘图框架:Three.js 零基础上手实战-其他

    WebGL 可视化3D绘图框架:Three.js 零基础上手实战 网名风舞烟,中国科技大学计算机专业.微软认证讲师(MCE).微软数据分析讲师.10多年软件行业从业经验,参与过数百万的企业级ERP系统 ...

  7. java 电子围栏_基于H5与webGL的 3d 电子围栏展示

    前言 现代工业化的推进在极大加速现代化进程的同时也带来的相应的安全隐患,在传统的可视化监控领域,一般都是基于 Web SCADA 的前端技术来实现 2D 可视化监控,本系统采用 Hightopo 的 ...

  8. Unity 3D俄罗斯方块

    学习unity也有一段时间了,从一开始的懵懵懂懂到现在的学有所小成,心里挺是高兴的,不过目前还是处于初学者阶段,很多东西还等着自己去发掘去学习. 最近做了一个3D俄罗斯方块小游戏作为练手,花了有两个多 ...

  9. 软件工程第二次二人协作项目 3D俄罗斯方块

    开发背景: 完成软件工程任务,提高自己解决问题的实际能力,适应二人协作开发的模式,互助进取. 本次作业将完成一个3D版本的俄罗斯方块的小游戏开发. 开发组员: 张羿  2012211841  宋浩达 ...

最新文章

  1. 微软宣布正式开源 Azure IoT Edge 边缘计算服务
  2. oracle rman 跨版本恢复 11.2.0.3- 11.2.0.4
  3. vue - blog开发学习2
  4. C#新手该如何规划学习【学习路线指南】
  5. [项目经验]玩转开源项目
  6. 【文末有赠书】从历史角度讲现代数学
  7. Flutter ListView 下拉刷新与上拉加载更多
  8. 系统技巧之如何巧妙的整理磁盘碎片
  9. java常见数据算法_冒泡排序
  10. Linux内核dev_set_drvdata()和dev_get_drvdata()存储自定义结构体用法
  11. 计算机软件文档编制规范百度云,计算机软件文档编制规范
  12. Android TextView 右上角文字角标(TM, ©,® )
  13. 俄罗斯方块游戏51单片机实现
  14. install pecl php_pecl安装以前的php版本
  15. RHY融获基金数字资产投资,海外扩建加速
  16. java word 加水印_java如何给office加水印
  17. mysql登录框万能密码_Sqli-LABS通关笔录-11[sql注入之万能密码以及登录框报错注入]...
  18. android遥控器适配
  19. 微信小程序时间加法_微信小程序获取系统时间、时间戳、时间时间戳加减
  20. Qt音视频开发04-保存音频文件(pcm/wav/aac)

热门文章

  1. 用Excel来将一行分隔成多行
  2. 边缘计算场景下云边端一体化的挑战与实践
  3. FBX格式转换为GLTF/GLB格式
  4. 声呐学习笔记之波束成形
  5. DB2 数据库错误码释义
  6. JSONObject转对象、集合、数组
  7. 白盒测试的用例设计方法
  8. CentOS MySQL数据库备份
  9. C语言typedef的用法
  10. Jmeter测试上传身份证图片base64编码接口