画布上的掉落块(译文)
By robot-v1.0
本文链接 https://www.kyfws.com/games/falling-blocks-on-canvas-zh/
版权声明 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 20 分钟阅读 - 9966 个词 阅读量 0画布上的掉落块(译文)
原文地址:https://www.codeproject.com/Articles/876475/Falling-Blocks-on-Canvas
原文作者:Sergey Alexandrovich Kryukov
译文由本站 robot-v1.0 翻译
前言
V.7.5.1: Derived work: customizable Falling Blocks with pure HTML + JavaScript + Canvas, using strict mode, complete with help and all classic Falling Blocks operations
V.7.5.1:衍生的工作:使用严格的模式,纯正的HTML + JavaScript + Canvas,可自定义的Falling Blocks,帮助和所有经典的Falling Blocks操作
目录(Table of Contents)
- 介绍(Introduction)
- 衍生工作(Derived Work)
- 新的功能(New Features)
- 客制化(Customization)
- 通用代码设计(General Code Design)
- 低水平四丁胺(Low-Level Tetromino)
- Tetromino构造函数和原型(Tetromino Constructor and Prototype)
- 内部功能和事件处理:传递" this"(Inner Functions and Event Handling: Passing “this”)
- 未来发展?(Future Development?)
- 版本号(Versions)
- 现场表演(Live Play)
- 结论(Conclusions)
1.简介(1. Introduction)
这是我关于JavaScript的第一篇完整的著作,还不算完全琐碎的事.我不能说我以前从未在纯娱乐领域尝试过任何东西,但是,再次,没有什么值得一提的.(This is my very first complete work on JavaScript, not counting something totally trivial. I cannot say I never tried anything in the field of pure entertainment before, but, again, nothing worth mentioning.) 下降块(Falling Blocks) 是一款传奇游戏,结合了极端简单性和吸引力.(is a legendary game with unique combination of extreme simplicity and attractiveness.) 不幸的是,我已经很长时间没有见过真正可玩的游戏了.不,我不是游戏玩家,没有看到太多东西.首先,如果没有开放源代码,我不会冒险,但是由于缺少最重要的功能和适当的外观,因此我看到的内容并不是真正可玩的,与DOS的旧版本实现方式不符.(Unfortunately, I haven’t seen a really playable implementation of the game for a long time. No, I’m not a gamer and did not see much. First of all, I would not risk anything without open source, but what I saw wasn’t really playable, due to the lack of most important features and proper look and feel, making no match with the old-time implementations for DOS.) 同时,带有JavaScript的HTML使用新的HTML(HTML5)画布元素是最简单的游戏平台.除了浏览器外,它不需要任何东西,应该可以在所有平台上使用,并且始终随附源代码.因此,当我有机会碰到这样的实现时,我感到非常高兴.显然,我发现的未完成作品是由相当合格的作者撰写的.同时,尽管它清晰明了且具有正确性,但它仍无法播放,并且代码的质量在总体设计水平上并不令我满意.这主要是由于初始设计中已经缺乏灵活性以及缺少重要功能.但是,如果您有一个简洁的源代码,那怎么可能是个问题呢?所以我决定从头开始重写它.(At the same time, HTML with JavaScript using new HTML (HTML5) canvas element is the most attractive platform for simple games. It does not require anything except a browser, should work on all platforms and always comes with source code. So, when I, just be some chance, came across such implementation, I was much pleased. It was apparent that the incomplete work I found was written by quite a qualified author. At the same time, it wasn’t yet playable and the quality of code did not satisfy me at the level of its general design, despite of its clarity and general correctness. This was mostly due to the lack of flexibility already put in the initial design and lack of important features. But how can it be a problem if you have a neat source code? So I decided to rewrite it from scratch.)
2.派生工作(2. Derived Work)
这是杰克`戈登(Jake Gordon)的原创作品,引起了我的注意:(This is the original work by Jake Gordon which caught my attention:)
- http://codeincomplete.com(http://codeincomplete.com) ,(,)
- http://codeincomplete.com/posts/2011/10/10/javascript_tetris(http://codeincomplete.com/posts/2011/10/10/javascript_tetris) ,(,)
- https://github.com/jakesgordon/javascript-tetris(https://github.com/jakesgordon/javascript-tetris) 我从头开始重新编写了将近100%的代码,但是我使用了Jake开发的所有低级算法,并遵循了他的大多数算法思想以及应用程序的一般设计,分解为主要块:基本帮助器方法,带有事件队列的游戏,带有失效机制的渲染以及主要应用程序.杰克(Jake)的原始作品尚不完整,但某些初始设计功能已经将开发推向了错误的方向,并且至少缺少一项功能使得该游戏无法真正发挥作用.但是我喜欢该解决方案的基础知识,并且真的很想对其进行修复,以创建一个真正可操作且维护良好的产品.(I re-wrote nearly 100% of the code from scratch, but I used all the low-level algorithms developed by Jake and followed most of his algorithmic ideas, as well as the general design of the application, decomposition into major blocks: basic helper methods, game with its event queue, rendering with invalidation mechanism and main application. The original Jake’s work was incomplete, but some initial design feature already led the development in wrong direction, and the lack of at least one feature made the game not really playable. But I liked the basics of the solution and really wanted to fix it all, to create a really operational and well maintained product.) 我没有遵循最初的代码设计,而是将游戏进一步分解为"常量",“变量”,“逻辑"等部分.相反,我设计了将设置对象放置在单独文件” settings.js"中的分离方式,并引入了带有原型方法的单独构造函数,这些原型方法代表(I did not follow the original code design with the further decomposition of the game into such parts as “constants”, “variable”, “logics” and so on. Instead, I designed the separation of the settings objects places in a separate file “settings.js”, introduced a separate constructor with prototype methods representing) 特罗米诺(Tetromino) 元素和其他结构元素正式表示为一组单独的JavaScript对象,例如简单的FSM和布局.(elements and other structural elements formally expressed as a set of separate JavaScript objects, such as simple FSM and layout.) 因此,首先,它允许轻松定制在原始作品中完全僵化的内容,首先,可以在合理的范围内修改游戏的大小.甚至可以在大小约为100 x 100的木板上玩游戏(但是,顺便说一句,确实很烦人:-).(So, first of all, it allows to easily customize things which were totally rigid in the original work, first of all, the size of the game in blocks can be modified in reasonable limits. Even playing on the board of the size of some 100 x 100 blocks became quite possible (but, by the way, really irritating :-)).)
3.新功能(3. New Features)
我提到缺少该功能,导致该游戏的实现无法玩.不幸的是,我见到的大多数实现都缺少此功能.它是什么?应该有一个按键(最初是空格键),如果有放置空间,可以将当前的Tetromino放到底部,仍然可以移动到底部.因此,我添加了此重要功能.(I mentioned the lack of the feature which rendered the implementation of the game not playable. Unfortunately, the lack of this feature is typical for most of the implementations I saw. What is it? There should be a key press (space bar, originally) which should drop a current tetromino to the bottom, where it still can be moved, if there is a room for that. So, I added this important feature.)
我完全改变了页面的布局.杰克(Jake)的原始设计基于针对不同页面尺寸的固定预定义布局集.他可能认为这会更简单,但事实并非如此.它不仅添加了多余的CSS代码,而且看起来很丑陋.现在,该游戏在任何大小的网页上看起来都是对称的.用户甚至可以在游戏过程中随时调整页面大小.根据该布局重新计算(I completely changed the layout of the page. Original Jake’s design was based on the fixed set of predefined layouts for different page sizes. Probably he thought it would be simpler, but it wasn’t. Not only it added superfluous CSS code, but looked ugly. Now, the game looks symmetrical on the Web page of any size. A user can adjust the page size at any time, even during the game play. The layout is recalculated according to the) window.innerHeight
和游戏板的大小(以块为单位).进行重新计算是为了将块的大小保持为整数(而不是小数值),因此与页面的内部高度相比,棋盘的相对大小会发生变化,以使所有纵横比值保持不变,而牺牲了可变的游戏区域利润.换句话说,它是按照大小合理的桌面应用程序的样式设计的.(and the size of the game board in blocks. The recalculation is made to keep the size of the block to an integer (not fractional value), so the relative size of the board compared to the inner height of the page vary to keep all the aspects ratio values at the expense of variable game area margins. In other words, it is designed in the style of a well-resized desktop application.)
更重要的是,游戏可以定制.首先,由于上面提到的布局和长宽比问题,无法更改整体游戏板的大小.现在,正如我之前提到的,可以在一个单独的文件中更改它,以及块颜色甚至形状.我会用(More importantly, the game can be customized. First of all, the size of the game board in block could not be changed, due to the layout and aspect ratio problems I mentioned above. Now, as I mentioned before, it can be changed in a separate file, as well as the block colors and even shapes. I’ll describe it in) 下一节(next section) 的文章.我实际上改变了颜色和原始方向,以使游戏更具可玩性并更接近其原始设计.(of the article. I actually changed the colors and original orientation, to make the game more playable and closer to its original design.)
我还添加了随时显示在同一页面上的帮助.(I also added help showing on the same page at any moment of time.)
在内部,我使用JavaScript创建了另一个完全结构化的代码设计(Internally, I created a different thoroughly structured code design, used JavaScript)严格模式(strict mode)和异常处理,并提高了性能.我将在下面简要描述此设计(and exception handling, and improved performance. I’ll briefly describe this design in) 第5节(section 5) ,但首先将描述可以定制的内容.(, but first will describe what can be customized.)
4.定制(4. Customization)
游戏的可定制部分放置在单独的文件" settings.js"中.(The customizable part of the game is placed in a separate file “settings.js”.)
-
由于上述布局的变化,可以更改游戏大小(以块为单位).可以更改此声明:(Game size in blocks can be changed, due to the changes in the layout described above. This declaration can be changed:)
-
可以在对象中更改键分配(Key assignment can be changed in the object)
key
.该对象的属性是通过函数而不是键名来命名的.默认情况下,[Enter]用于开始/暂停/继续游戏,[Esc]停止当前玩的游戏,箭头键移动当前的tetromino元素(“向上"键将其旋转),空格将其放下,[F1]显示和隐藏帮助.(. The properties of this object are named by function, not by key name. By default, [Enter] is used to start/pause/continue the game, [Esc] stops currently played game, arrow keys move the current tetromino element (“up” key rotates it), blank space drops it down, [F1] shows and hides help.) -
游戏的时间可以在对象中更改(Timing of the game can be changed in the object)
delays
.该对象定义了将tetromino元素移动一行之前的延迟(以秒为单位):随着用户的进步,用于加速游戏的延迟的初始值,最小值和减量.根据游戏规则,随着总行数的增加,延迟将增加一个恒定值.(. This object defines the delays in seconds before moving a tetromino element by a line: initial, minimal and decrement of the delay applied for acceleration of the game as the user progress. The delay is incremented by a constant value as total number of lines, according to the game rule, grows.) -
得分规则可以在对象中更改(Score rules can be changed in the object)
scoreRules
.规则定义了在每个丁醇胺滴下和移除某些行时增加的分数.规则可以是任何用户定义的函数,这些函数根据删除的行的当前计数,得分和要立即删除的行数来计算添加的分数.默认情况下,为每条放置的线添加固定数量的点,为删除的线添加的点数量随一次删除的线数的幂函数增长.这是根据原始游戏设计完成的,在该设计中,玩家有动机收集不完整的行数,然后一次完成多达4个行.(. The rules define the added score on the drop of each tetromino and when some rows are removed. The rules can be any user-defined functions calculated the added score depending on current count of removed lines, score and the number of lines to be removed at once. By default, a fixed amount of points is added for each dropped line, and the amount of points added for removed lines grows as a power function of the number of lines removed at once. This is done according the original game design, where the player is given the incentive to collect numbers of incomplete rows and then complete up to 4 of them at once.) -
最后,tetromino的颜色和形状可以更改.我会在里面解释(Finally, tetromino colors and shapes can be changed. I’ll explain it in) 第6节(section 6) .(.)
5.通用代码设计(5. General Code Design)
游戏的核心单元是(*The central unit of the game is the*) Tetromino
构造函数及其原型对象的两种方法,在(*constructor and two methods of its prototype object described in*) 第7节(section 7) .(*.*)
该代码以包含HTML的文件” settings.js"开头,而主要代码为" application.js".(*The code starts with the file “settings.js” including in HTML first, and the main code is in “application.js”.*)
物体(*The object*) layout
获取主要的DOM元素,并实现原始布局以及更改窗口大小时的布局行为.下一个对象(*gets main DOM elements and implements original layout and the layout behavior on the change of the window size. Next object,*) game
,定义了从图形呈现中抽象出来的游戏逻辑,并将其委托给对象(*, defines the game logic abstracted from the graphical rendering, which is delegated to the object*) rendering
,它使用(*, which uses*) HTML5 Canvas功能(HTML5 Canvas feature) ,(*,*)
所有这些都放在了一些简单的基本实用功能之后,接着是游戏的主要功能(*A set of few simple basic utility functions is put below all that, followed by the game’s main*)*匿名(*anonymous*)*函数,该函数以IIFE形式实现,有助于防止从主函数外部访问所有本地函数.在整个代码中都使用此模式. (请参阅IIFE JavaScript上的这篇文章(*function, which is implemented in the IIFE form, which helps to keep all local functions inaccessible from outside the main function. This pattern is used throughout the code. (Please see this article on the IIFE JavaScript*)*设计模式(*design pattern*)*,(,) “Immediately-invoked function expression”)“立即调用的函数表达式”( )(.))
此设计模式巧妙解决的另一个问题是JavaScript需求的解析(Another problem elegantly solved by this design pattern is the resolution of the requirements of JavaScript) 严格模式(strict mode) .它有助于使用内部函数,同时将主要代码夹在try-catch块中,这很重要,特别是对于开发而言.(. It helps to use inner functions and, at the same time, sandwich the main code in the try-catch block, which is important, especially for development.)
主函数初始化游戏,安装事件处理程序并开始第一帧;其他框架通过(Main function initialize the game, installs event handlers and starts the first frame; other frames are requested through) window.requestAnimationFrame
.异常捕获的使用仅限于最高级别:根据结构异常处理原理,在每个事件处理程序和主函数上.(. The use of exception catching is limited to the very top level: on each event handler and main function, according to the structural exception handling philosophy.)
下一步,我将描述代码中最有趣的部分:算法代码及其实现.(As a next step, I’ll describe the most interesting part of the code: the code of the algorithms and its implementation.)
6.低级四丁胺(6. Low-Level Tetromino)
如果按四面体形状按位定义,这是一个片段:(This is a fragment if bitwise definition of tetromino shapes:)
function TetrominoShape(size, blocks, color) {
this.size = size; this.blocks = blocks; this.color = color;
}
const tetrominoSet = [
new TetrominoShape(4, [0x0F00, 0x2222, 0x00F0, 0x4444], tetrominoColor.I),
//...
];
这是每个形状对象的二进制表示形式的描述:(This is the description of the binary representation of each shape object:)
// blocks: each element represents a rotation of the piece (0, 90, 180, 270)
// each element is a 16 bit integer where the 16 bits represent
// a 4x4 set of blocks, e.g. "J"-shaped tetrominoSet[1].blocks[1] = 0x44C0
//
// 0100 = 0x4 << 3 = 0x4000
// 0100 = 0x4 << 2 = 0x0400
// 1100 = 0xC << 1 = 0x00C0
// 0000 = 0x0 << 0 = 0x0000
// ------
// 0x44C0
7. Tetromino构造函数和原型(7. Tetromino Constructor and Prototype)
我介绍了(I introduced the) Tetromino
构造函数对象有一些很好的理由:同时提高代码性能和可维护性.相关的JavaScript功能通常被称为" OOP"和"类",但是这些术语极具误导性或至少引起争议.基于JavaScript原型的对象机制与"带有类的OOP"主要不同.(constructor object for some very good reasons: to improve performance of the code and maintainability at the same time. Related JavaScript features are often referred to as “OOP” and “class”, but these are very misleading or at least controversial terms; JavaScript prototype-based object machinery is principally different from “OOP with classes”.)
这是构造函数:(Here is the constructor:)
function Tetromino(shape, x, y, orientation) {
this.shape = shape; //TetrominoShape
this.x = x;
this.y = y;
this.orientation = orientation;
} //Tetromino
并且在其原型中添加了两种方法:(And two method are added to its prototype:)
Tetromino.prototype = {
// fn(x, y), accepts coordinates of each block, returns true to break
first: function(x0, y0, orientation, fn, doBreak) {
let row = 0, col = 0, result = false,
blocks = this.shape.blocks[orientation];
for(let bit = 0x8000; bit > 0; bit = bit >> 1) {
if (blocks & bit) {
result = fn(x0 + col, y0 + row);
if (doBreak && result)
return result;
} //if
if (++col === 4) {
col = 0;
++row;
} //if
} //loop
return result;
}, //Tetromino.prototype.first
all: function(fn) { // fn(x, y), accepts coordinates of each block
this.first(this.x, this.y, this.orientation, fn, false); // no break
} //Tetromino.prototype.all
} //Tetromino.prototype
这两种方法是低级算法的核心:它们实现了众所周知的"第一"和"所有"模式.它们遍历所有呈tetromino形状的块,并且当function参数提供的某些条件变为true时,第一个中断搜索.(These two methods is the heart of the low-level algorithms: they implement well-known “first of” and “all” patterns. They traverse all the blocks in the tetromino shape and the first one breaks the search when some condition supplied by the function argument becomes true.)
这打破了主要性能改进之一,因为原始代码在所有情况下都遍历给定形状的所有块. (还请注意,(This break one of the major performance improvements as original code traversed all blocks of a given shape in all cases. (Also note, that the) first
和(and) all
仅创建一次;这就是为什么要在构造函数之外完成此原型分配的原因.)(are created only once; that’s why this prototype assignment is done outside of the constructor.))
这就是功能(This is how the function) first
用于游戏逻辑:(is used in the game logic:)
willHitObstacle: function(tetromino, x0, y0, orientation) {
// tentative move is blocked with some obstacle
return tetromino.first(x0, y0, orientation, function(x, y) {
if ((x < 0)
|| (x >= gameSizeInBlocks.x)
|| (y < 0)
|| (y >= gameSizeInBlocks.y)
|| game.getBlock(x,y))
return true;
}, true);
}, //willHitObstacle
匿名函数传递给(As soon as the anonymous function passed to) tetromino.first
真实,功能(returs true, the function) first
也会立即返回true,从而打破遍历tetromino块的循环.这表明遇到了第一个障碍物,它可能是墙壁中的一个或另一个障碍物.检测到第一个障碍物将进一步考虑障碍物的冗余性,因此该功能(also returns true immediately, breaking from the loop traversing the tetromino blocks. This indicates that the first obstacle has been encountered, which could be one of the walls or another block. Detecting the very first obstacle makes further consideration of obstacle redundant, so the function) willHitObstacle
此时返回true.(returns true at this point.)
功能的使用(The use of the function) Tetromino.all
更简单:遍历所有形状的块.这尤其用于在HTML画布上绘制tetromino元素.(is simpler: all blocks of the shape are traversed. This is used, in particular, for drawing the tetromino elements on the HTML canvas.)
8.内部功能和事件处理:传递" this"(8. Inner Functions and Event Handling: Passing “this”)
让我们看一个更有趣的细节:现在添加了一个事件处理程序.仅考虑一个就足够了.这是可以做到的:(Let’s look at one more interesting detail: now an event handlers are added. It’s enough to consider just one. This is how it can be done:)
function someFunction(event) { /* ... */ }
document.onkeydown = someFunction;
能行吗一个小问题是处理程序函数是作为游戏对象的成员实现的.那么下面的代码也可以工作吗?(Will it work? One little problem is that the handler function is implemented as a member of the game object. So will the below code also work?)
document.onkeydown = game.keydown;
不完全的.问题是(Not quite. The problem is that the) keydown
函数不仅使用事件参数,还使用隐式参数(function uses not only the event argument, but also the implicit argument) this
,用于访问其他成员(, which is used to access other members of the) game
目的.如果以上面显示的方式添加了事件处理程序,则仍将像往常一样传递此参数,但并不奇怪,它将引用…(object. If the event handler is added the way showed above, this this argument will still be passed, as always, but it will, not too surprisingly, reference…) document
目的.我不是为自己创造了一些人为的问题吗?一点也不.此问题很容易通过以下方式解决:(object. Didn’t I created some artificial problem for myself? Not at all. This problem is easily solved this way:)
document.onkeydown = function(event) { game.keydown(event); };
请注意(Note that the) event
参数应显式传递.(argument should be explicitly passed.)
内部功能也有类似的故事.查看对象的短片段(Similar story goes with inner functions. Look at the short fragment of the object) rendering
:(:)
const rendering = {
// ...
promptText: element("prompt"),
rowsText: element("rows"),
pausedText: element("paused"),
invalid: { board: true, upcoming: true, score: true, rows: true, state: true },
// ...
// ...
draw: function() {
const drawRows = function() {
if (!this.invalid.rows) return;
setText(this.rowsText, game.rows);
this.invalid.rows = false;
}; //drawRows
const drawState = function() {
if (!this.invalid.state) return;
setText(statusVerb, game.states.current === game.states.paused ? "continue" : "start");
setVisibility(this.pausedText, game.states.current === game.states.paused);
setVisibility(this.promptText, game.states.current != game.states.playing);
this.invalid.state = false;
}; //drawState
// ...
drawRows.call(this);
drawState.call(this);
// compare:
// drawState(); //won't work
// drawState(this); //won't work
// ...
} //draw
} //rendering
在我的设计之初,几种绘制方法如(In the beginning of my design, several drawing methods like) drawRows
要么(or) drawState
被定义为渲染属性,直到我发现除了(were defined as rendering properties, until I figured out that they won’t be used anywhere but in) draw
,因此最好通过使它们成为内部函数将它们从外部上下文中隐藏出来.从上面显示的代码片段中,可以看到他们使用隐式(, so it’s better to hide them from outside context by making them inner functions. From the code fragment shown above, one can see that they use implicit) this
访问对象成员的参数(argument to access members of the object) rendering
.为什么直接调用(在代码示例中注释掉)不起作用?在JavaScript中,(. Why direct calls (commented out in the code sample) won’t work? In JavaScript,) this
传递给内部函数的参数将是外部函数对象,绘制(argument passed to an inner function would be the outer function object, draw) instead
的(of) rendering
.解决方法是使用函数的(. The work-around is to use the function’s) call
功能,只需通过(function, which simply passes) this
明确地:(explicitly:) https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call)
当然,可以添加一个(Of course, it would be possible to add an)显性的(explicit)论证与通过(argument and pass) rendering
,但使用已经总是传递的隐式(, but using already-always-passing implicit) this
论据是一种自然而经济的解决方案.毕竟,我想解释一种处理内部函数的非常通用的技术.(argument is a natural and economic solution. After all, I wanted to explain a very general technique for dealing with inner functions.)
9.未来发展?(9. Future Development?)
我没有计划在游戏中添加鼠标/触摸支持.我实际上使用鼠标控件实现了实验版本,并决定将其删除:它基本上可以在不同的版本中使用,但是使用鼠标玩起来确实很尴尬,不便.(I have no plan on adding mouse/touch support to the game. I actually implemented experimental version with mouse control and decided to remove it: it basically worked in different variants, but playing with mouse is really awkward, inconvenient.) 为具有此设备的客户端计算机添加对加速度计和陀螺仪的支持,实际上是更实际的.但这将是本机Windows,Linux或Android的工作.对于任何Web技术的使用,我觉得^等待很重要.我坚信,只有在W3联盟将它们标准化后,才可以将任何设备的使用并入公共应用程序中,即使是像Web摄像机或指纹读取器这样广泛使用的设备.我认为,当适当的W3草案或只是提案演变成标准并在主要浏览器中实现时,所有这些设备都可以使用.请参见,例如:(It would be really more practical to add the support for the accelerometer and gyroscope for client computers having this equipment. But this rather would be the job for native Windows, Linux or Android. As to the use of any Web technologies, I find it important to… wait. I strongly believe that the use of any devices should be incorporated in the public applications only when they become standardized with W3 Consortium, even so widespread devices as Web cameras or fingerprint readers. In my opinion, all such devices can be used when appropriate W3 drafts or just proposals evolve into standards and get implemented in the major browsers. Please see, for example:)
- http://www.w3.org/TR/orientation-event(http://www.w3.org/TR/orientation-event)
- http://www.w3.org/TR/html-media-capture(http://www.w3.org/TR/html-media-capture) .(.) 为了存储设置,我不想破坏JavaScript隔离并与文件系统进行交互.由于某些非常好的原因,这被认为是不安全的.当我将应用程序设计为"家庭使用"而不是用于在线播放时,很可能并且非常容易从UI即时生成设置文件并使其可下载,因此用户可以手动替换它.(I don’t want to break JavaScript isolation and interact with file system for the sake of storing the setting. By some very good reasons, this is considered unsafe. As I designed the application rather for “home use”, not for playing online, it would quite possible and pretty easy to generate a setting file from UI on the fly and make it downloadable, so the user could replace it manually.) 我认为唯一合法的方法是使用(I think the only legitimate way would be using) Web本地存储(Web local storage) 游戏设置.显然,这将是要实现的另一个功能.另一个选择是在游戏开始时用块填充游戏,并选择平均密度并达到一定高度.这是我想实现的原始游戏的流行功能,因为我认为这是最有趣的游戏方式.由于多种原因,它没有其他的那么琐碎,所以我只是在考虑它.(for the game setting. Apparently, this is would be one additional feature to implement. Another one would be the option to populate the game with blocks when it starts, with chosen average density and up to certain height. This is the popular feature of the original game which I would love to implement, as I think this is the most interesting way to play it. By a number of reasons, it is less trivial than the rest, so I’m only thinking about it.)
更新
游戏设置和使用的交互式在线编辑器(Interactive on-line editor of game settings and using) Web本地存储(Web local storage) 在7.0版中实现了用于永久数据存储的功能.(for permanent data storage is implemented in v. 7.0.) 我邀请任何人发送任何建议或剥离任何衍生的作品.(I invite anyone to send any suggestions or spin-off any kind of derived work.)
10.版本(10. Versions)
****1.0:(1.0:)****2015年2月15日:如文章所述,第一个全功能版本.(February 15, 2015: First fully-functional version, as described in the article.)
****1.1:(1.1:)****2015年2月19日:功能相同的版本(包含版本信息),指向游戏,许可证,贡献者和原始出版物的信息的链接已添加到帮助框中.这样做是为了有可能将产品发布在独立的网页上,除了本文之外,仍然显示此法律敏感信息.(February 19, 2015: Functionally the same version, with version information, links to the information on the game, license, contributors and original publication, added to the help box. This is done for the possibility to publish the product on a stand-along Web page, apart from this article, still showing this legally sensitive information.)
****2.0:(2.0:)****2015年2月19日:修复了已知的浏览器兼容性问题.(February 19, 2015: Known browser compatibility issues fixed.)
****3.0:(3.0:)****2015年9月20日:现代化的JavaScript代码,基于文本的帮助显示/关闭按钮替换为SVG图片,为不兼容的浏览器添加了注释.(September 20, 2015: Modernized JavaScript code, text-based help show/close button replaced with SVG image, added a note for incompatible browsers.)
****4.0:(4.0:)****2019年1月20日:修复了将tetromino放下后的行为(使用空格键):现在其位置冻结了,因此无法再移动了;移动键会影响下一个tetromino元素.(January 20, 2019: Fixed behavior after a tetromino is dropped down (with blank space key): now its location freezes, so it cannot be moved anymore; move keys affect next tetromino element.)
****4.1:(4.1:)****2019年1月23日:实施了更高级的空间角色处理.(January 23, 2019: Implemented more advanced handling of Space character.)
现在,仅当键不是自动重复空格或按下Ctrl + Space时,它才会下拉当前的tetromino.(Now it drops down current tetromino only if the key is not auto-repeat space or if Ctrl+Space is pressed.)
KeyboardEvent.repeat
可能并非在所有浏览器中都实现,因此可以使用(may be not implemented in all browsers, so this property is simulated using) game.repeatedKeyDropDown
属性.帮助会相应更新.(property. Help is updated accordingly.)
****7.0:(7.0:)****2019年2月1日:许多新功能.(February 1, 2019: Many new features.)
- 添加了"下载源代码"和"设置"命令.(Added “Download source code” and “Settings” commands.)
- 新的"设置"页面提供了一种交互式便捷的方式,可以自定义块中的游戏大小,时间(速度和速度增长),tetromino颜色和按键分配以及"混乱".自定义数据保存在浏览器的本地存储中,可以随时删除.(New “Settings” page provides interactive and convenient way to customize game size in blocks, timing (speed and speed growth), tetromino colors and key assignments, as well as “clutter”. Custom data is saved in a browser’s local storage and can be removed at any time.)
- “杂波"是经典俄罗斯方块的最佳旧实现的典型功能,它增加了游戏的趣味性.游戏区域上到处都是随机的四聚体,到一定高度为止(由用户通过设置或紧接在游戏之前以百分比指定).然后,用户可以尝试清理杂物.(“Clutter” is the feature typical for best old implementations of classical Tetris which adds interest to the game. The game field is cluttered with random tetrominoes up to certain height (specified by the user in percents via settings or immediately before the game). Then the user can try to clean up the clutter.)
- 许多便利功能和更好的帮助.特别是,自定义键分配反映在帮助中.(Many convenience feature and better help. In particular, custom key assignments are reflected in help.)
****7.1:(7.1:)**2019年2月5日:修复了浏览器不允许的问题(February 5, 2019: Fixed the problem with browsers not allowing)
localStorage
(DOM存储)-实现后备功能:播放作品,但不将自定义数据存储在本地存储中.((DOM Storage) — implemented the fallback: playing works, but not storing of custom data in local storage.) 通过在以下方面进行测试发现了此问题:Microsoft Edge 42.17134.1.0,EdgeHTML 17.17134\2018年.(The problem was revealed by testing on: Microsoft Edge 42.17134.1.0, EdgeHTML 17.17134, 2018.)
11.现场表演(11. Live Play)
可以现场玩游戏(The game can be played live) 这里(here) .(.)
12.结论(12. Conclusions)
由于某些良好的原因,JavaScript有时被称为世界上最容易被误解的语言:(For some good reasons, JavaScript is sometimes claimed to be the world’s most misunderstood language:) http://javascript.crockford.com/javascript.html(http://javascript.crockford.com/javascript.html) https://yow.eventer.com/yow-2013-1080/the-world-s-most-misunderstood-programming-language-by-douglas-crockford-1377(https://yow.eventer.com/yow-2013-1080/the-world-s-most-misunderstood-programming-language-by-douglas-crockford-1377) .(.) 我从该练习中学到的一个重要教训是:通过研究非常基本的特征并避免容易克服无法正确清除的思维的幻觉来获得正确的实践,这一点非常重要.重要的是不要落入由炒作以及明显无能但相当有说服力的人引起的干扰中.什么样的干扰?这里描述的一些:(One important lesson I learned from this exercise is: It’s very important to derive the right practices by looking into the very fundamental features and stay away from the illusions which are too easy to overcome the mind which is not properly cleared. It is very important not to fall into the distractions created by hypes and plainly incompetent but pretty convincing people. What kind of distractions? Some of those described here:) http://davidwalsh.name/javascript-objects-distractions(http://davidwalsh.name/javascript-objects-distractions) .(.) 我认为,所有神话破灭都非常有用,但这是^一个完全不同的故事.(I think all that myth busting if very useful, but this is… a whole different story.)
许可
本文以及所有相关的源代码和文件均已获得The Code Project Open License (CPOL)的许可。
HTML HTML5 Javascript Canvas text 新闻 翻译