如何克隆俄罗斯方块?
我正在使用 XNA C# 编写俄罗斯方块克隆代码,但不确定从高层次上处理游戏数据结构方面的最佳方法。
我对碰撞检测、旋转、动画等完全没问题。我需要知道存储“掉落块”的最佳方法——即不再受玩家控制的块。
我认为每个 Tetromino 块都应该存储在自己的由 4x4 数组组成的类中,以便可以轻松旋转该块。 那么问题是如何将四格骨牌的最终位置存储到游戏网格中,然后将四格骨牌切割成单独的块(对于每个单元格),然后设置主游戏网格的相应位置以容纳这些相同的块,然后使四格骨牌消失一次它已到达最终位置。 也许我的方法有一些缺点。
我应该为主游戏网格创建一个 10x20 矩阵来存储吗? 或者我应该使用堆栈或队列以某种方式存储丢弃的块。 或者也许有一些更好的方法/数据结构来存储东西?
我确信我的方法会起作用,但我想看看是否有人知道更好的方法,或者我的方法是否足够好?
PS 不是家庭作业,这将是我的作品集的一个项目。 谢谢。
I am working on coding a Tetris clone in XNA C# and am unsure of the best way to approach the data structure side of the game on a high level.
I am totally fine with the collision detection, rotations, animation etc. What I need to know the best way to do the storing of "dropped blocks" - ie blocks that are no longer under tha player's control.
I think that each Tetromino block should be stored in its own class that consists of a 4x4 array so that the block can easily be rotated. The problem is then how to I store the tetromino's final position into the game grid by then cutting the tetromino up into individual blocks(for each cell) and then set the main game grid's corresponding positions to hold these same blocks, then disappearing the tetromino once it has reached its final position. Maybe there is some drawback to my method.
Should I create a 10x20 matrix for the main game grid which can then store? or should I use stacks or queues to somehow store the dropped blocks. Or maybe there is some better method/data structure to store things?
I am sure my way would work, but I am reaching out to see if anybody knows a better way or if my way is good enough?
P.S. Not homework, this will be a project for my portfolio. Thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
一旦一个方块无法移动,它就无法与任何其他现在无法移动的方块区分开来。 在这方面,我认为将整个网格存储为矩阵是最有意义的,其中每个方块要么被填充,要么未被填充(如果是,则连同块的颜色)。
我觉得矩阵有很多优点。 它将使碰撞检测变得简单(不必与多个对象进行比较,只需比较矩阵上的位置)。 将其存储为矩阵还可以更轻松地确定何时创建整行。 最重要的是,当线条消失时,您不必担心拼接固定的 Tetromino。 当发生这种情况时,你可以一举将整个矩阵向下移动。
Once a block is immobile, there's nothing that distinguishes it from any other block that is now immobile. In that regard, I think it makes the most sense to store the entire grid as a matrix, where each square is either filled or not (along with the color of the block if it is).
I feel like the matrix has many advantages. It'll make collision detection simple (no having to compare with multiple objects, just locations on a matrix). Storing it as a matrix will also make it easier to determine when a full line has been created. On top of that, you don't have to worry about splicing an immobile Tetromino when a line disappears. And when one does, you can just shift the entire matrix down in one fell swoop.
这听起来像家庭作业,但我对俄罗斯方块的面向对象方法的看法是让每个单独的方块都是一个对象,并且“块”(tetrominos)和网格本身都将是相同方块对象的集合。
块对象管理下落方块的旋转和位置,网格处理显示它们并销毁已完成的行。 每个块都会有一个与之相关的颜色或纹理,这些颜色或纹理将由它所来自的原始块对象提供,但否则网格底部的方块将没有其他指示表明它们曾经是同一原始块的一部分。
详细来说,当您创建一个新的块对象时,它会在网格上创建一组具有相同颜色/纹理的 4 个正方形。 网格管理它们的显示。 因此,当块到达底部时,您只需忘记该块,并且方块仍由网格引用。
旋转和放置是仅一个块需要处理的操作,并且仅是其四个方块中的一个(尽管它需要能够查询网格以确保旋转适合)。
This smells like homework, but my take on an object-oriented approach to Tetris would be to have each individual square be an object, and both "blocks" (tetrominos) and the grid itself would be collections of the same square objects.
Block objects manage the rotation and position of the falling squares, and the grid handles displaying them and detroying completed rows. Each block would have a colour or texture associated with it that would be provided by the original block object it came from, but otherwise squares at the base of the grid would have no other indication that they were ever part of the same original block.
To elaborate, when you create a new block object, it creates a set of 4 squares with the same colour/texture on the grid. The grid manages their display. So when the block hits the bottom, you just forget about the block, and the squares remain referenced by the grid.
Rotations and dropping are operations only a block need deal with, and only one its four squares (though it will need to be able to query the grid to make sure the rotation can fit).
在我看来,不让区块实际上看起来像自治区块是许多俄罗斯方块克隆的一大失败。 我特别努力确保我的克隆始终看起来正确,无论该块是否仍在“进行中”或已丢弃。 这意味着稍微超出简单的矩阵数据结构,并提出支持块部分之间“连接”概念的东西。
我有一个名为
BlockGrid
的类,它用作Block
和Board
的基类。BlockGrid
有一个名为AreBlockPartsSameBlock
的抽象方法(C++ 中的纯虚拟),子类必须重写该方法以确定两个不同的块部分是否属于同一块。 对于Block
中的实现,如果两个位置都存在块部分,则仅返回true
。 对于Board
中的实现,如果两个位置包含相同的Block
,则返回true
。BlockGrid
类使用此信息“填充”渲染块中的详细信息,以便它们实际上看起来像块。Not making blocks actually look like autonomous blocks is - in my opinion - a big failing of many a Tetris clone. I put special effort into ensuring my clone always looked right, whether the block is still "in play" or dropped. This meant going slightly beyond the simple matrix data structure and coming up with something that supported the concept of "connection" between block parts.
I had a class called
BlockGrid
that is used as a base class for both aBlock
and theBoard
.BlockGrid
has an abstract (pure virtual in C++) method calledAreBlockPartsSameBlock
that subclasses must override to determine whether two different block parts belong to the same block. For the implementation inBlock
, it simply returnstrue
if there are block parts at both locations. For the implementation inBoard
, it returnstrue
if both locations contain the sameBlock
.The
BlockGrid
class uses this information to "fill in" the details in the rendered blocks, so that they actually look like blocks.使用数组是处理俄罗斯方块最简单的方法。 您在屏幕上看到的内容与内存中使用的结构之间存在直接关联。 使用堆栈/队列将是一种矫枉过正并且不必要的复杂。
您可以拥有 2 个掉落方块的副本。 一种用于显示(Alpha),另一种用于移动(Beta)。
您将需要一个像 _beta 数组这样的结构,该
数组将被移动或旋转,并对照棋盘检查是否发生碰撞。 如果发生冲突,则将其恢复为 _alpha,如果没有,则将 _beta 复制到 _alpha 上。
如果 moveDown() 发生碰撞,则块的生命周期结束,并且 _alpha 网格必须复制到游戏板上,并删除 FallingBlock 对象。
当然,棋盘必须是另一种结构,例如:
我使用 int 来表示一个块,每个值(如 1、2、3)表示不同的纹理或颜色(0 表示一个空点)。
一旦块成为游戏板的一部分,它只需要显示纹理/颜色标识符。
Using arrays would be the easiest way to handle tetris. There is a direct correlation between what you see on screen and the structures used in memory. Using stack/queues would be an overkill and unnecessarily complicated.
You can have 2 copies of a falling block. One will be for display (Alpha) and the other one will be movement (Beta).
You will need a structure like
The _beta array would be move or rotated and checked against the board for collisions. If there is a collision, revert it to _alpha, if not, copy _beta onto _alpha.
And if there is a collision on movedDown(), the block's life is over and the _alpha grid would have to copied onto the game board and the FallingBlock object deleted.
The board would of course have to be another structure like:
I used int to represent a block, each value (like 1,2,3) representing a different texture or color (0 would mean an empty spot).
Once the block is part of the gameboard, it would only need a texture/color identifier to be displayed.
实际上我几天前才这样做过,只是在 WPF 而不是 XNA 中。 这就是我所做的:
编辑:
看来我对“阻止”的定义与其他人不同。 我定义的“块”是构成 Tetromino 的 4 个单元之一,而实际的 Tetromino 本身就是一个“Piece”。
将块作为具有 X、Y 坐标和颜色的结构。 (我后来添加了一个 bool IsSet 来指示它是在浮动块中还是在实际板上,但这只是因为我想在视觉上区分它们)
作为 Block 上的方法,我有 Left、Right、Down 和 Rotate(块中心)返回一个新的移动块。 这使我能够在不知道零件的形状或方向的情况下旋转或移动任何零件。
我有一个通用的 Piece 对象,它包含它包含的所有块的列表以及作为中心的块的索引,该索引用作旋转中心。
然后,我制作了一个可以生产所有不同部件的 PieceFactory,并且对于不需要知道它是什么类型的部件,我可以(并且确实)轻松添加由多于或少于 4 个块组成的部件的变体,而无需创建任何新类
该板由一个字典组成,其中包含当前板上的所有块以及可配置的板的尺寸。 您也可以使用矩阵,但使用字典时,我只需要迭代没有空格的块。
I actually just did this a few days ago except in WPF rather than XNA. Here's what I did:
Edit:
Seems like I define "Block" differently than other people. What I define as a Block is one of 4 cells that make up a Tetromino, and an actual Tetromino itself as a Piece.
Have a Block as a struct that had X, Y coordinates and Color. (I later added a bool IsSet to indicate whether it was in a floating piece or on the actual board, but that was just because I wanted to distinguish them visually)
As methods on Block, I had Left, Right, Down, and Rotate(Block center) which returned a new shifted Block. This allowed me to rotate or move any piece without knowing the shape or orientation of the piece.
I had a generic Piece object that had a List of all the blocks it contained and the index of the Block that was the center, which is used as the center of rotation.
I then made a PieceFactory that could produce all the different pieces, and with a Piece not needing to know what kind of piece it was, I could (and did) easily add variation of Pieces consisting of more or less than 4 Blocks without needing to create any new classes
The Board consisted of a Dictionary which was all the blocks that were currently on the board, as well as the dimensions of the board that was configurable. You can use a Matrix just as well probably, but with a Dictionary I only needed to iterate through Blocks without white spaces.
我的解决方案(设计),使用 Python 中的示例作为伪代码的良好替代品。
使用 20 x 10 的网格,使四格骨牌落下。
四格骨牌由块组成,块具有坐标(x,y)和颜色属性。
例如,T 形四联骨牌看起来像这样...
因此,T 形是坐标为 (5,19)、(6,19)、(7,19)、(6 ,20)。
移动形状只需对组中的所有坐标应用简单的变换即可。 例如,要向下移动形状,请将 (0,1)、向左 (-1,0) 或向右 (1,0) 添加到构成形状的集合中的所有坐标。
这还允许您使用一些简单的三角函数将形状旋转 90 度。 规则是,当相对原点旋转 90 度时,(x,y) 等于 (-y,x)。
这是一个例子来解释它。 从上方看成T字形,以(6,19)为中心块绕其旋转。 为简单起见,将其设为集合中的第一个坐标,因此...
然后,这是一个将该坐标集合旋转 90 度的简单函数
现在,如果将此函数应用于我们的 T 形坐标集合...
在坐标系中绘制出来,它看起来像这样...
这对我来说是最难的一点,希望这对某人有帮助。
My Solution (design), with examples in Python as a good substitute for pseudo code.
Use a grid 20 x 10, that the tetrominoes fall down.
Tetrominoes are made up of blocks, which have attributes of coordinate (x,y) and colour.
So, for example, the T-shape tetrominoe looks like this...
Thus, the T-shape is a collection of blocks with the coords (5,19), (6,19), (7,19), (6,20).
Moving the shape is a matter of applying a simple transformation to all the coords in the group. e.g. to move the shape down add (0,1), left (-1,0) or right (1,0) to all coords in the collection that make the shape.
This also lets you use some simple trig to rotate the shape by 90-degrees. The rule is that when rotating 90-degrees relative to an origin, then (x,y) becomes equal to (-y,x).
Here is an example to explain it. Taking the T-shape from above, use the (6,19) as the centre block to rotate around. For simplicity, make this the first coordinate in the collection, so...
Then, here is a simple function to rotate that collection of coordinates by 90-degrees
Now, if you apply this function to our collection of coordinate for the T-shape...
Plot this out in the coordinate system and it looks like this...
That was the hardest bit for me, hope this helps someone.
请记住,混淆 C 代码竞赛的前一位获胜者用不到 512 字节的混淆 C 代码实现了一个相当不错的俄罗斯方块游戏(适用于 BSD unix 上的 VT100 终端):
http://www.ioccc.org/1989/tromp.hint
Keep in mind that a previous winner of the Obfuscated C Code Contest implemented a pretty good tetris game (for VT100 terminals on BSD unix) in fewer than 512 bytes of obfuscated C:
http://www.ioccc.org/1989/tromp.hint
我绝不是俄罗斯方块专家,但正如您所描述的,10x20 矩阵对我来说似乎是一个自然的选择。
当需要检查您是否完成一行并处理它时,这将变得非常容易。 只需迭代 2d 数组,查看每个位置的布尔值,看看它们是否加起来最多 10 个块位置。
但是,如果有一条完整的生产线,您将需要进行一些手动清理。 不得不把一切都往下移。 尽管归根结底这并不是什么大不了的事。
I'm by no means a Tetris expert, but as you described a 10x20 matrix seems like a natural choice to me.
It will make it very easy when the time comes to check if you have completed a line or not, and dealing with it. Simply iterating over the 2d-array looking at boolean values of each position to see if they add up to 10 block positions.
However, you'll have some manual clean up to do if there is a completed line. Having to shift everything down. All though it isn't that big of a deal when it comes down to it.
使用 Simon Peverett 逻辑,这就是我在 C# 中得到的结果
注意:使用 XNA,
Point
结构可以交换为Vector2D
Using Simon Peverett logic, here is what I ended up with in c#
Note: Using XNA,
Point
structure could be swapped forVector2D
在我的示例(Java)中 - 所有图形都有块列表 - 可以在需要时删除。 另外,在我的 Board 类中,我有一个图形列表和一个字段变量图形 - 由用户控制。 当人物“登陆”时,它会进入其他人物的列表,并且新的人物将由用户控制。
这里有更好的解释: http://bordiani .wordpress.com/2014/10/20/tetris-in-java-part-i-overview/
in my example (Java) - all figures have lists of blocks - which can be removed whenever needed. Also in my Board class I have a list of figures and a field variable figure - which is controlled by the user. When the figure is "landed" - it goes into the list of other figures, and a new figure is made controllable by the user.
A better explanation here: http://bordiani.wordpress.com/2014/10/20/tetris-in-java-part-i-overview/