用 React+Redux+Immutable 做俄罗斯方块

2023-11-06

俄罗斯方块是一直各类程序语言热衷实现的经典游戏,JavsScript的实现版本也有很多,用React 做好俄罗斯方块则成了我一个目标。

https://chvin.github.io/react-tetris 玩一玩!

开源地址:https://github.com/chvin/react-tetris

效果预览

效果预览

正常速度的录制,体验流畅。

响应式

响应式

不仅指屏幕的自适应,而是在PC使用键盘、在手机使用手指的响应式操作

手机

数据持久化

数据持久化

玩单机游戏最怕什么?断电。通过订阅 store.subscribe,将state储存在localStorage,精确记录所有状态。网页关了刷新了、程序崩溃了、手机没电了,重新打开连接,都可以继续。

Redux 状态预览

Redux状态预览

Redux设计管理了所有应存的状态,这是上面持久化的保证。

游戏框架使用的是 React + Redux,其中再加入了 Immutable,用它的实例来做来Redux的state。(有关React和Redux的介绍可以看:React入门实例Redux中文文档

1、什么是 Immutable?

Immutable 是一旦创建,就不能再被更改的数据。对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象。

初识

让我们看下面一段代码:

function keyLog(touchFn) {
  let data = { key: 'value' };
  f(data);
  console.log(data.key); // 猜猜会打印什么?
}

不查看f,不知道它对 data 做了什么,无法确认会打印什么。但如果 data 是 Immutable,你可以确定打印的是 value

function keyLog(touchFn) {
  let data = Immutable.Map({ key: 'value' });
  f(data);
  console.log(data.get('key'));  // value
}

JavaScript 中的ObjectArray等使用的是引用赋值,新的对象简单的引用了原始对象,改变新也将影响旧的:

foo = {a: 1};  bar = foo;  bar.a = 2;
foo.a // 2

虽然这样做可以节约内存,但当应用复杂后,造成了状态不可控,是很大的隐患,节约的内存优点变得得不偿失。

Immutable则不一样,相应的:

foo = Immutable.Map({ a: 1 });  bar = foo.set('a', 2);
foo.get('a') // 1

简洁

Redux中,它的最优做法是每个reducer都返回一个新的对象(数组),所以我们常常会看到这样的代码:

// reducer
...
return [
   ...oldArr.slice(0, 3),
   newValue,
   ...oldArr.slice(4)
];

为了返回新的对象(数组),不得不有上面奇怪的样子,而在使用更深的数据结构时会变的更棘手。
让我们看看Immutable的做法:

// reducer
...
return oldArr.set(4, newValue);

是不是很简洁?

关于 “===”

我们知道对于ObjectArray===比较,是对引用地址的比较而不是“值比较”,如:

{a:1, b:2, c:3} === {a:1, b:2, c:3}; // false
[1, 2, [3, 4]] === [1, 2, [3, 4]]; // false

对于上面只能采用 deepCopydeepCompare`来遍历比较,不仅麻烦且好性能。

我们感受来一下Immutable的做法!

map1 = Immutable.Map({a:1, b:2, c:3});
map2 = Immutable.Map({a:1, b:2, c:3});
Immutable.is(map1, map2); // true

// List1 = Immutable.List([1, 2, Immutable.List[3, 4]]);
List1 = Immutable.fromJS([1, 2, [3, 4]]);
List2 = Immutable.fromJS([1, 2, [3, 4]]);
Immutable.is(List1, List2); // true

似乎有阵清风吹过。

React 做性能优化时有一个大招,就是使用 shouldComponentUpdate(),但它默认返回 true,即始终会执行 render() 方法,后面做 Virtual DOM 比较。

在使用原生属性时,为了得出shouldComponentUpdate正确的true or false,不得不用deepCopy、deepCompare来算出答案,消耗的性能很不划算。而在有了Immutable之后,使用上面的方法对深层结构的比较就变的易如反掌。

对于「俄罗斯方块」,试想棋盘是一个二维数组,可以移动的方块则是形状(也是二维数组)+坐标。棋盘与方块的叠加则组成了最后的结果Matrix。游戏中上面的属性都由Immutable构建,通过它的比较方法,可以轻松写好shouldComponentUpdate。源代码:/src/components/matrix/index.js#L35

Immutable学习资料:

2、如何在Redux中使用Immutable

将原来 Redux提供的combineReducers改由上面的库提供:

// rootReduers.js
// import { combineReducers } from 'redux'; // 旧的方法
import { combineReducers } from 'redux-immutable'; // 新的方法

import prop1 from './prop1';
import prop2 from './prop2';
import prop3 from './prop3';

const rootReducer = combineReducers({
  prop1, prop2, prop3,
});


// store.js
// 创建store的方法和常规一样
import { createStore } from 'redux';
import rootReducer from './reducers';

const store = createStore(rootReducer);
export default store;

通过新的combineReducers将把store对象转化成Immutable,在container中使用时也会略有不同(但这正是我们想要的):

const mapStateToProps = (state) => ({
  prop1: state.get('prop1'),
  prop2: state.get('prop2'),
  prop3: state.get('prop3'),
  next: state.get('next'),
});
export default connect(mapStateToProps)(App);

3、Web Audio Api

游戏里有很多不同的音效,而实际上只引用了一个音效文件:/build/music.mp3。借助Web Audio Api能够以毫秒级精确、高频率的播放音效,这是<audio>标签所做不到的。在游戏进行中按住方向键移动方块,便可以听到高频率的音效。

网页音效进阶

WAA 是一套全新的相对独立的接口系统,对音频文件拥有更高的处理权限以及更专业的内置音频效果,是W3C的推荐接口,能专业处理“音速、音量、环境、音色可视化、高频、音向”等需求,下图介绍了WAA的使用流程。

流程

其中Source代表一个音频源,Destination代表最终的输出,多个Source合成出了Destination。
源代码:/src/unit/music.js 实现了ajax加载mp3,并转为WAA,控制播放的过程。

WAA 在各个浏览器的最新2个版本下的支持情况(CanIUse

浏览器支持

可以看到IE阵营与大部分安卓机不能使用,其他ok。

Web Audio Api 学习资料:

4、游戏在体验上的优化

技术:

  • 按下方向键水平移动和竖直移动的触发频率是不同的,游戏可以定义触发频率,代替原生的事件频率,源代码:/src/unit/event.js

  • 左右移动可以 delay 掉落的速度,但在撞墙移动的时候 delay 的稍小;在速度为6级时 通过delay 会保证在一行内水平完整移动一次;

  • 对按钮同时注册touchstartmousedown事件,以供响应式游戏。当touchstart发生时,不会触发mousedown,而当mousedown发生时,由于鼠标移开事件元素可以不触发mouseup,将同时监听mouseout 模拟 mouseup`。源代码:/src/components/keyboard/index.js

  • 监听了 visibilitychange 事件,当页面被隐藏切换的时候,游戏将不会进行,切换回来将继续,这个focus状态也被写进了Redux中。所以当用手机玩来电话时,游戏进度将保存;PC开着游戏干别的也不会听到gameover,这有点像 ios 应用的切换。

  • 任意时刻刷新网页,(比如消除方块时、游戏结束时)也能还原当前状态;

  • 游戏中唯一用到的图片,其他都是CSS;
    image

  • 游戏兼容 Chrome、Firefox、IE9+、Edge等;

玩法:

  • 可以在游戏未开始时制定初始的棋盘(十个级别)和速度(六个级别);

  • 一次消除1行得100分、2行得300分、3行得700分、4行得1500分;

  • 方块掉落速度会随着消除的行数增加(每20行增加一个级别);

5、开发中的经验梳理

  • 为所有的component都编写了shouldComponentUpdate,在手机上的性能相对有显著的提升。中大型应用在遇到性能上的问题的时候,写好shouldComponentUpdate 一定会帮你一把。

  • 无状态组件`(Stateless Functional Components)是没有生命周期的。而因为上条因素,所有组件都需要生命周期 shouldComponentUpdate,所以未使用无状态组件。

  • 在 webpack.config.js 中的 devServer属性写入host: '0.0.0.0'`,可以在开发时用ip访问,不局限在localhost;

  • redux中的store并非只能通过connect将方法传递给container,可以跳出组件,在别的文件拿出来做流程控制(dispatch),源代码:/src/control/states.js

  • 用 react+redux 做持久化非常的方便,只要将redux状态储存,在每一个reduers做初始化的时候读取就好。

  • 通过配置 .eslintrc.js 与 webpack.config.js ,项目中集成了 ESLint` 检验。使用 ESLint 可以使编码按规范编写,有效地控制代码质量。不符规范的代码在开发时(或build时)都能通过IDE与控制台发现错误。 参考:Airbnb: React使用规范

6、总结

  • 作为一个 React 的练手应用,在实现的过程中发现小小的“方块”还是有很多的细节可以优化和打磨,这时就是考验一名前端工程师的细心和功力的时候。

  • 优化的方向既有 React 的本身,比如哪些状态由 Redux存,哪些状态给组件的state就好;而跳出框架又有产品的很多特点可以玩,为了达到你的需求,这些都将自然的推进技术的发展。

  • 一个项目从零开始,功能一点一滴慢慢累积,就会盖成高楼,不要畏难,有想法就敲起来吧。 ^_^

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

用 React+Redux+Immutable 做俄罗斯方块 的相关文章

随机推荐

  • tomcat版本与jdk对应关系

    见tomcat官网说明 http tomcat apache org whichversion html Apache Tomcat Versions Apache Tomcat is an open source software imp
  • Mongodb系列- spring-data-mongodb使用MongoTemplate实现分页查询

    转载于 http www cnblogs com jycboy p 8969035 html 在用spring data mongodb框架开发的过程中 需要实现分页查询 就百度了下 没找到满意的又google了下 找到了思路 在sprin
  • IO进程线程day8(2023.8.6)

    一 Xmind整理 管道的原理 有名管道的特点 信号的原理 二 课上练习 练习1 pipe 功能 创建一个无名管道 同时打开无名管道的读写端 原型 include
  • 如何让Myeclipse已经关闭掉的项目不显示出来

    一 打开Package Explorer视图 在它的右上角有一个向下的三角图标 2 点击后选择Filters 在弹出的Filter配置窗口中选中 Closed Projects 转载于 https www cnblogs com rensh
  • rabbitmq消息消费失败如何处理

    在介绍消息中间件 MQ 之前 我们先来简单的了解一下 为何要引用消息中间件 例如 在电商平台中 常见的用户下单 会经历以下几个流程 当用户下单时 创建完订单之后 会调用第三方支付平台 对用户的账户金额进行扣款 如果平台支付扣款成功 会将结果
  • electron 自定义标题栏(windows 系统)

    其实没有网上那么麻烦 如果你仅仅只是想去掉标题 还有下面的工具栏 只需要如下配置 const win new BrowserWindow width 800 height 600 titleBarStyle hidden titleBarO
  • 大数据系列——概论

    1 何为大数据 在万物互联的时代 数据每时每刻都在产生 你的手机 笔记本 智能穿戴设备 智能家电等等所有联网的终端设备都在不间断的向云端输送着数据 云端数据呈指数级增长 在过去 受限于人类的计算能力 只能通过随机采样来进行数据分析 分析的结
  • 深度学习实战(十):使用 PyTorch 进行 3D 医学图像分割

    深度学习实战 十 使用 PyTorch 进行 3D 医学图像分割 1 项目简介 2 3D医学图像分割的需求 3 医学图像和MRI 4 三维医学图像表示 5 3D Unet模型 5 1损失函数 Dice Loss 5 2医学成像数据 5 2
  • 常用快捷键整理(centos7、Notepad++、Idea、Excel)

    目录 一 常用快捷键 1 Notepad 常用快捷键 2 centos7快捷键 3 idea快捷键 4 excel 一 常用快捷键 1 Notepad 常用快捷键 1 Ctrl D 复制当前行 2 Ctrl L 删除当前行 3 Ctrl T
  • Python面试经验总结,面试一时爽,一直面试一直爽!

    有伙伴问过我Python面试相关的问题 这里也跟大家总结了一下 跟大家说说我之前的面试经历和心得体会 1 简历制作 我做了两份简历 用两个手机账号 两个简历名字 分别在各个招聘网站投了双份简历 一个是数据分析的简历 一个是web全栈开发的简
  • 软件测试第一阶段:web前端技术基础-9- 测试用例设计方法

    一丶测试用例 测试用例 将要进行的测试工作具体化 并且记录到一个文件中 一般情况下是一个excel 表格 在测试用例中 明确的指定了每一步要做什么操作 期望得到什么结果 测试工作 等程序员完成代码 此时可以进行前期准备工作 也就是编写测试用
  • JS小数点前面的0显示

    使用Java从 oracle数据库中取出0 225的数字时 在前台js页面中显示为 225 有两种方法 1 Java中的类使用string来存储0 225时 会保存为 225 需要在前台使用 js方法parseFloat 转为float类型
  • LF AI & Data基金会执行董事Ibrahim Haddad:加速中的开源人工智能创新与合作

    以人工智能为代表的新一代信息技术正在深刻改变着世界 改变着人类生活 人工智能技术不但能够带来便利 同时也为其带来了不确定 不稳定等诸多挑战 2022年7月21日 由中国开源软件推进联盟主办 赛迪传媒 软件和集成电路 杂志社联合承办 CSDN
  • Windows装机必备——WinRAR2023最新版下载&安装教程

    软件下载 软件 WinRAR 版本 自动更新 语言 简体中文 大小 3 38M 系统要求 Win7及以上 32 64为操作系统 硬件要求 CPU 2 0GHz 内存 2G 或更高 下载通道 百度网盘丨下载链接 链接 https pan ba
  • 使用Prometheus+Grafana监控MySQL

    你还不会监控服务器资源吗 你还不会监控mysql性能吗 但是你看了这篇文章之后我想你应该会了 哈哈哈 就算不会 至少你也知道是个怎么回事 这篇文章就来介绍怎么给自己的服务器系统搭建一个监控平台 要是还不会 请私聊我 哈哈 一 介绍Prome
  • 单片机_第2章 MCS-51单片机的结构及原理

    目录 2 1 MCS 51单片机的结构 2 1 1 MCS 51单片机的内部结构 80C51单片机的内部资源主要包括 CPU Central Processing Unit 2 1 2 MCS 51引脚及功能 封装 80C51单片机的40只
  • base model初始化large model,造成的参数矩阵对不上权重不匹配问题

    先报错没有指定文件 OSError Error no file named pytorch model bin tf model h5 model ckpt index or flax model msgpack found in dire
  • Python环境搭建—Anaconda的安装与使用

    Python安装 Anaconda的安装与使用 Anaconda是一个集成的Python数据科学环境 除了有Python外 还安装了很多用于 数据分析的第三方库 相对于新手来说 相当友好 可以避免安装第三方库的麻烦 1 Anaconda下载
  • Hadoop分布式部署,部署一个namenode和三个datanode

    本文以一主三从部署hadoop为介绍 1 首先克隆原有centos系统 2 nn y 为主 dn1 dn2 dn3为从 都是通过克隆快速完成的 右键管理有克隆 选择完全克隆 3 配置网络集群 设置静态ip 192 168 64 132 19
  • 用 React+Redux+Immutable 做俄罗斯方块

    俄罗斯方块是一直各类程序语言热衷实现的经典游戏 JavsScript的实现版本也有很多 用React 做好俄罗斯方块则成了我一个目标 戳 https chvin github io react tetris 玩一玩 开源地址 https g