落块板和形状控制(译文)
By robot-v1.0
本文链接 https://www.kyfws.com/games/falling-blocks-board-and-shape-control-zh/
版权声明 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 16 分钟阅读 - 7652 个词 阅读量 0落块板和形状控制(译文)
原文地址:https://www.codeproject.com/Articles/10668/Falling-Blocks-Board-and-Shape-Control
原文作者:Yang Kok Wah
译文由本站 robot-v1.0 翻译
前言
Implementing the all time favourite game as .NET custom controls, complete with animation and sound for full gaming experience
将一直以来最受欢迎的游戏实现为.NET自定义控件,并附带动画和声音,以提供完整的游戏体验
- 下载源(VB)-1.2 MB(Download source (VB) - 1.2 MB)
- 下载源(C#)-1.5 MB(Download source (C#) - 1.5 MB)
- 下载演示(仅可执行文件和资源)-450 KB(Download demo (executable and resources only) - 450 KB)
介绍(Introduction)
还有另一个俄罗斯方块克隆.(Yet another Tetris clone.) 是的,它是最好的游戏之一,它为程序员提供了几乎无限的可能性,仅受程序员的想象力和能力的限制.在本文中,我将与读者分享我的游戏版本.(Yes, it is one of the best games that offers the programmer with virtually boundless possibilities limited only by the programmer’s imagination and ability. In this article, I will be sharing with the reader my version of the game.)
背景(Background)
游戏的名称是Falling Blocks.它以两个自定义控件的形式出现:(The name of the game is Falling Blocks. It comes in the form of two custom controls:)
-
FallingBlocks
板(Board) -
FallingBlocks
形状(Shape) 的(The)FallingBlocks
主板控制是主要控制.可以将其拖放到窗体中,并且在运行窗体时,可以通过单击控件来激活游戏.并且在游戏处于活动状态时,单击控件即可结束游戏.(Board control is the main control. It can be dragged and dropped into a form and when the form is run, the game can be activated by clicking on the control. And while the game is active, clicking on the control ends the game.) 的(The)FallingBlocks
形状控制通常可用于两个目的:(Shape control can be used for generally two purposes:) -
用作即将到来的预览(To serve as a preview for oncoming)
FallingBlocks
件.(pieces.) -
限制形状中的形状(To restrict the number of shapes in the)
FallingBlocks
板.(Board.) 每(Each)FallingBlocks
董事会由一系列(Board is made up of an array of)FallingBlocks
单元,共同保持董事会的地位.每(cells, which collectively hold the status of the board. Each)FallingBlocks
形状分配了一个(Shape is assigned a)FallingBlocks Shape
类型,它是各种(type, which is an enumeration of the various)FallingBlocks
形状.(shapes.)
落块形状类型(FallingBlocks Shape type)
目前,定义的形状位于(Currently, the shapes defined are in the) FallingBlocksShapeType enum
.(.)
一种(A) FallingBlocks<code>Shape
对象是基于创建的(object is created based on the) FallingBlocksShapeType
参数传递给(parameter passed to the) CreateShape()
方法:(method:)
创建一个新的(To create a new)落块(FallingBlocks) Shape
类型,简单地:(type, simply:)
- 将新的形状定义添加到(add a new shape definition to the)
FallingBlocksShapeType enum
- 新增一个(add a new)
case
块中的新形状(block for the new shape in the)CreateShape()
方法(method)
落块形状(FallingBlocks Shape)
有两个构造函数(There are two constructors for) FallingBlocksShape
:(:)
一种(A) FallingBlocksShape
对象由一组点和一个可选的私有点定义.每个点的属性是:(object is defined by a set of points and an optional privot point. The properties for each point are:)
- 颜色(color)
- 每个单元块的大小(the size of each cell block)
- 每个单元格的单元格形状(the cell shape of each cell block)
例如,(For example, the)
IShape
FallingBlocksShape
由四点定义(is defined by the four points)(0,0) (1,0) (2,0) (3,0)
.坐标基于屏幕坐标,其中x坐标向右增加,y坐标向下增加.这些数字表示块单位,而不是像素单位.(. The coordinates are based on screen coordinates with x-coordinate increasing to the right and y-coordinate increasing downwards. These numbers indicate block units, not pixel units.) 枢轴点用于旋转.这是旋转操作将保持不变的点.例如,(The pivot point is used for the purpose of rotation. It is the point that will stay invariant by the rotation operation. For example, the pivot for the)IShape
对象是(object is)(1,0)
.逆时针旋转90度后,它将保持在(. After being rotated by 90 deg anticlockwise, it will remain at)(1,0)
而其余的点会改变.下图和代码说明了旋转的执行方式:(while the rest of the points change. The diagram and the code below illustrate how the rotation is performed:)
的(The) color
,(,) blocksize
和(and) blockshape
传递到构造函数中的对象用于呈现对象.(passed into the constructor are used for the purpose of rendering the object.)
的(The) FallingBlocks<code>Shape
默认情况下,该对象不可见.它的主要目的是充当持有人使用的各种属性的持有人.(object by default is not visible. Its main purpose is to serve as a holder of various properties used by the) FallingBlocks
Board
.董事会称(. The board calls the) FallingBlocksShape
绘图功能:(drawing functions:) EraseShape()
和(and) DrawShape()
擦除和绘制(to erase and paint the) FallingBlocks
件.(pieces respectively.)
其他用途(The other use of) FallingBlocksShape
对象是作为下一个即将到来的预览(object is to serve as a preview for the next oncoming) FallingBlocks
片.为此,对象必须与(piece. For this purpose, the object must be associated with the) PreviewFallingBlocksShape
的属性(property of the) FallingBlocksBoard
.照此使用时,(. When used as such, the) FallingBlocksShape
将可见,并使用其覆盖的自我渲染(will be visible and is self rendering using its overridden) OnPaint()
方法..(method..)
的默认播放时间行为(The default play time behaviour of the) FallingBlocks
游戏是这样的:(game is such that any of the defined shapes in the) FallingBlocksShapeType enum
可以选择.但是,当(can be selected. However, this behaviour changes when the) FallingBlocksShape
对象被拖入(objects are dragged into the) FallingBlocksBoard
在设计时.当一个(during design time. When a) FallingBlocksShape
包含在(is contained in the) FallingBlocksBoard
且与(and is not associated with the) PreviewFallingBlocksShape
属性,它具有一个新的目的,即限制可以在棋盘上播放的形状的类型.在播放期间仅会出现这些包含的形状.(property, it takes on a new purpose of restricting the type of shapes that could be played on the board. Only these contained shapes will appear during play time.)
落块板(FallingBlocks Board)
它是一个网格(It is a grid of) FallingBlocks
细胞.每(cells. Each) FallingBlocks
单元格有一个(cell has a) color
和(and) avail
属性.最初是细胞(property. Initially the cell) color
设定为(is set to) Black
和(and) avail
至(to) true
.(.)
的(The) color
属性指示用于渲染单元格的颜色.每次绘制木板时,它都会重新绘制所有具有(property indicates the color to be used for rendering the cell. Each time the board is drawn, it repaints all the cells which have) avail
设置为(value set to) false
.(.)
当一个(When a) FallingBlocks
该乐曲已准备就绪,可以放在棋盘的顶部.每个点上(piece is ready for play, it is placed at the top of the board. Each of the points on the) FallingBlocksShape
将与(will overlap a cell in the) FallingBlocksBoard
.为一个(. For a) FallingBlocks
要放置在有效位置,每个重叠的单元格必须有一个(piece to be in a valid location, each overlapped cell must have an) avail
带有(property with a) true
值.如果不满足此条件,则(value. If this condition is not met, then the) FallingBlocks
件无法播放,游戏结束.(piece cannot be played and the game ends.)
板上还包含一个计时器,用于控制碎片向下移动的速度.速度可以使用(The board also contains a timer which controls the speed of the pieces downward movement. The speed can be adjusted using the) FallingBlocksDelay
属性.有效范围为100-1000毫秒.越大(property. It has a valid range of 100 - 1000 ms. The larger the) FallingBlocksDelay
值,播放速度越慢.(value, the slower the speed of play.)
面板还包含一个文本框,其宽度设置为(The board also contains a textbox whose width is set to) 0
使它几乎不可见.使用文本框的原因是因为(to make it virtually invisible. The reason for using the textbox is because the) FallingBlocks
董事会是从(Board which is derived from the) Panel
类没有键盘事件处理程序.文本框用于捕获键盘事件.播放中使用的键是箭头键:<(左)用于将乐曲向左移动,>(右)用于向右移动,^(向上)用于逆时针旋转90度,V(向下)用于快速着陆.(class has no keyboard event handler. The textbox serves the purpose of capturing the keyboard events. The keys used for the play are arrow keys:<(left) for moving the piece left, >(right) for moving right, ^(up) for 90 deg anti-clockwise rotation and V (down) for fast landing.)
当要移动一块时,木板会调用该形状的(When a piece is to be moved, the board calls the shape’s) EraseShape()
方法,将棋子移到有效位置,然后通过调用形状的(method and moves the piece to a valid position, then redraws the piece by calling the shape’s) DrawShape()
方法.(method.)
当一块降落时(大约撞击(When a piece has landed (about to hit a cell below with) avail
值(value) false
),形状的(), the shape’s) Erase()
方法被调用,并且董事会调用其(method is called, and the board calls its) PasteShape()
更新状态的方法((method to update the status () color
和(and) avail
)的单元格与形状重叠,因此,当下次重新绘制木板时,这些单元格将使用平台形状的颜色进行绘制.() of cells overlapped by the shape, so that when the board is next redrawn, these cells will be painted with the landed shape’s color.)
遏制(Containment)
的(The) `` 落块(FallingBlocks) Shape
的(’s) OnCreateControl()
当该方法被调用时(method is called when the) FallingBlocksBoard
控件以表格形式创建.加载表单并完成加载时会发生这种情况(control is being created in the form. This happens when the form is loaded and has completed loading the) FallingBlocksBoard
控制.控件的(control. The control’s) Controls
查询集合是否存在(collection is queried for any) FallingBlocksShape
对象,并将这些对象添加到(objects and these objects are added to the) arrShape
ArrayList
.(.)
在里面(In the) NewShape()
创建新方法(method which creates a new)落块形状(FallingBlocksShape)一块玩,(piece for play, the) arrShape
查询清单.如果(list is queried. If the) arrShape
列表包含任何(list contains any) FallingBlocksShape
对象,则将从该列表中选取下一个形状,否则将从列表中任何已定义的形状中选取(object, then the next shape would be taken from this list, else it would be picked from any of the defined shapes in the) FallingBlocksShapeType
enum
.(.)
预习(Preview)
的(The) FallingBlocks<code>Board
的(’s) PreviewFallingBlocksShape
属性用于指示(property is used to indicate to the) FallingBlocksBoard
关于一个存在(about the presence of a) FallingBlocksShape
用作即将来临的作品的预览.如果预览(serving as a preview for the oncoming pieces. If a preview) FallingBlocksShape
存在对象,将其各种属性设置为选定的形状及其(object is present, its various properties are set to the shape selected and its) Visible
属性设置为(property is set to) true
所以这样(so that the) FallingBlocksShape
对象可以渲染自己.(object can render itself.)
机芯(Movements)
在计时器的每个滴答事件中,(In each tick event of the timer, the) FallingBlocks
一块被向下移动(piece is moved down by the) MoveDown()
方法.(method.)
播放器还可以使用键盘上的箭头键或调用(The player can also use the keyboard arrow keys or call the) public FallingBlocksMove..()
使当前棋子移动的方法.(methods to cause the current piece to move.)
每次要移动作品时,都会创建并移动一个副本.克隆的新位置已通过验证(Each time the piece is to be moved, a clone is created and moved. The clone’s new position is validated by the) IsValidPosition()
方法.如果未通过验证,则返回原始件,否则将返回克隆件及其更新位置.(method. If not validated, the original piece is returned, else the clone piece will be returned with its updated position.)
渲染背景图像和Fallingblocks板(Rendering Background Image and Fallingblocks Board)
的(The) BackgroundImage
属性是控件属性之一,很难管理.此属性是持久的.每当出于更新目的或出于任何目的关闭设计时间窗体时,Visual Studio都会保存该图像.我注意到,Visual Studio每次在运行设计表单时都会关闭它.因此,持久(property is one of the control properties, which is quite difficult to manage. This property is persistent. Visual Studio saves the image every time it is updated or when the design time form is closed for whatever purpose. I have noted that Visual Studio closes a design time form every time before it runs it. Thus, persistent) BackgroundImage
属性将始终被保存.(property will always be saved.)
直接在(The advantage of directly painting on the) Background
image是您可以直接控制何时需要更新图形.这允许某种形式的缓冲更新.你画在(image is that you have direct control as to when you want your drawing to be updated. This allows for some form of buffered update. You paint on the) Background
图片,完成所有绘画后,您可以将(image and when all the painting is done, you can call the) Refresh()
一次显示所有更新的方法.此技术使您可以创建更平滑的动画.(method to show all the updates at once. This technique allows you to create smoother animation.)
一个问题(One problem with the) Background
image是如果图像不完全适合控件,则将其平铺.为了解决这个问题,(image is that it will be tiled if the image does not fit exactly into the control. To solve this problem, the) ResizeImage()
当任何图像要分配给(is called when any image is to be assigned to the) BackgroundImage
属性.(property.)
为了帮助更新背景图像,请使用两个图像:(To assist in the updating of the background image, two images:) _baseimage
和(and) _cleanbase
被使用.两者都是(are used. Both are copies of the) Background
设计时的图像.(image at design time.)
_baseimage
在运行时用分数更新.每当分数改变时,(is updated with the score during run time. Each time the score changes,) _cleanbase
复制到(is copied to) _baseimage
(以删除先前的分数),然后((to erase the previous score), and then the) _baseimage
用最新的分数更新.然后将其复制到(is updated with the latest score. It is then copied to) Background
通过图像(image via the) DrawPicture()
方法.(method.)
所有的细胞(All the cells in) FallingBlocks<code>Board
然后画在(are then painted on the) Background
图片.(image.)
渲染形状(Rendering Shape)
当一个(When a) FallingBlocks<code>Shape
对象是可见的,由其绘制(object is visible, it is painted by its) OnPaint()
方法.这适用于(method. This is applicable for the)落块(FallingBlocks) Shape
用于预览的对象.(object that is used for preview purposes.)
对于那些(For those) FallingBlocksShape
否则使用的对象,不会直接渲染.渲染由(objects that are used otherwise, they are not rendered directly. Rendering is done by the) FallingBlocksBoard
到它的(onto its) Background
通过调用图像(image by calling) DrawShape()
和(and) EraseShape()
方法.(methods.)
大事记(Events)
只有一个事件被定义为(There is only one event that is defined for the) FallingBlocksB<code>oard
.它是(. It is the) GameOver
游戏结束时触发的事件(顾名思义).(event which is fired (as the name suggests) when the game is over.)
控制加载注意事项(Control Loading Considerations)
没有直接的方法可以判断控件是由Visual Studio加载的,以用于设计时或运行时.我发现在设计时或运行时加载控件的顺序为:(There is no direct way to tell if your controls are loaded by Visual Studio for design-time purposes or run time. I found out that the order in which the control is loaded at design time or run time is:)
- 默认构造函数(Default constructor)
- 调整大小(Resize)
BackgroundImage
属性(property)OnControlCreated
从这里开始,我们知道(From here, we know that by the time)OnControlCreated()
方法被称为(method is called, the)BackgroundImage
属性已设置.(property has already been set.) 这很有用,因为我们要确保我们不使用(This is useful because we want to make sure that we do not make use of a)BackgroundImage
那是无效的.(that is invalid.) 预览(For the preview)FallingBlocksShape
对象,我们可能需要在下一个选定的形状时对其进行更新(object, we may want to update it with the next selected shape when the)FallingBlocksBoard
首先按原样加载(预览(is first loaded as it (the preview)FallingBlocksShape
对象)可能没有保持有效的形状.我们可以利用一个标志(object) may not be holding a valid shape. We can make use of a flag)_justloaded
默认为(which is default to)true
并且仅设置为(and is only set to)false
后(after)OnControlCreated()
方法 ((method ()_created
会在查询完(flag set) is called and after having queried the)PreviewFallingBlocksShape
属性.(property.)
增强功能(Enhancements)
自从本文的第一版以来,所做的一些增强是:(Some of the enhancements made since the first release of this article are:)
- 重新映射键盘键以允许用户使用其他键进行控制(The remapping of the keyboard keys to allow the user to use other keys to control)
FallingBlocks
动作.(pieces movements.) - 暂停游戏.(Pausing the game.)
- 为每个新游戏添加障碍的选项.(Option to add obstacles for each new game.)
- 自定义单元格的形状.(Customizing the shape of cells.)
- 平滑动画的选项.(Option for smooth animation.)
- 自定义行删除动画.(Customizing the row removal animation.)
- 选择添加网格线(Option to add in grid lines)
- 可以播放声音和背景音乐(Option to play sound and background music)
样品申请(Sample Application)
新的示例应用程序允许演示新功能.您可以输入"(The new sample application allows for the new features to be demonstrated. You can key “) s
‘开始游戏,然后”(’ to start the game, and “) `` 结束.空格键将切换,暂停游戏.(” to end it. The space bar will toggle, pausing the game. When the)障碍物(Obstacles)复选框已选中,新游戏将有一些白色的障碍物作为障碍物.您还可以单击每个复选框/单选按钮,以尝试使用平滑动画,行删除动画和单元格形状的不同选项.滑动(checkbox is checked, the new games will have some cells colored white as obstacles. You can also click each of the check boxes/radio buttons to experiment with different options of smooth animation, row removal animation and cell shapes. Sliding the) FallingBlocks
延迟跟踪栏将导致游戏速度立即改变.(delay track bar will cause the speed of the game to change immediately.)
声音(Sound)
至少有3种方法可以在Windows Forms Application中播放声音文件:(There are at least 3 ways to play sound files in Windows Forms Application:)
-
使用(Using)
System.Media.SoundPlayer
-
使用Windows Media Player控件(Using the Windows Media Player control)
-
使用(Using)**winmm.dll(winmm.dll)**称呼(calling the)
mciSendString
功能(function) 虽然最容易使用,(Although the easiest to use,)System.Media.SoundPlayer
只能播放wave文件,并且一次只能播放一个文件. Windows Media Player控件是COM组件,而不是.NET程序集,并且涉及互操作性开销.(can only play wave files and only one file can be played at any one time. Windows Media Player control is a COM component, not a .NET assembly and there are Inter-operability overheads involved.) 尽管不是.NET组件,(Although not a .NET component,)**winmm.dll(winmm.dll)**Windows OS附带了使用它的许多优点:(comes with Windows OS and there are many advantages to using it:) -
占地面积小.无需分发其他DLL(Footprint is small. No additional DLL needed to be distributed)
-
支持多种多媒体格式,包括MP3和WAV(Support many Multimedia format, including MP3 and wav)
-
支持多个媒体文件同时播放(Support multiple media files to be played simultaneously) 我创建了一个包装器类来封装所需的功能:(I have created a wrapper class to encapsulate the required functionalities:) 的(The)
FallingBlocks.MciPlayer
类的构造函数:(class constructor:)
MciPlayer(string filename, string alias)
接受媒体(mp3或wav)的完整路径文件名和分配的别名.(takes in a media (mp3 or wav) full path file name and an assigned alias.)
在上面的代码中,为每个动画创建一个播放器.对于行删除动画"(In the code above, one player is created for each animation. For the row removal animation “) Blink
“,我们寻找(”, we look for the)**Blink.mp3(Blink.mp3)**文件在当前目录中.如果找到文件,我们使用文件名和别名”(file in the current directory. If the file is found, we use the filename and the alias “) Blink
“来创建一个新的(” to create a new) MciPlayer
用于播放此动画的声音的对象.(object to be used for playing sound for this animation.)
方法(The methods of) MciPlayer
是:(are:)
-
LoadMediaFile(string filename, string alias)
-
PlayFromStart()
-
PlayLoop()
-
StopPlaying()
-
CloseMediaFile()
当一个新的(When a new)MciPlayer
被建造,(is created,)LoadMediaFile
叫做.如果成功加载,则可以调用其余方法.(is called. If successfully loaded, the rest of the methods can be called.) -
PlayFromStart()
从头到尾播放一次媒体文件.(plays the media file from the start to the end once.) -
PlayLoop()
到达结尾时,从头开始再次播放媒体文件.(plays the media file from the start once again when it reaches the end.) -
StopPlaying()
停止播放文件.如果我们想再次播放媒体,则可以调用任何一种播放方法而无需重新加载.(stops the file from playing. If we want to play the media again, we can call either of the play methods without reloading.) -
CloseMediaFile()
卸载媒体文件.如果要再次播放该文件,则必须使用(unloads the media file. If we want to play the file again, we have to load it again with the)LoadMediaFile
功能(function)
结论(Conclusion)
编写游戏不仅有益,而且还可以帮助程序员快速掌握编写游戏所需的语言.当我想学习Visual Basic,Turbo Pascal和Java时,我做了很多次.实际上,此俄罗斯方块游戏的.NET版本的初衷是选择C#.(Game writing is not only rewarding but it also helps the programmer to quickly pick up the intended language for writing the game. I had done this many times when I wanted to pick up Visual Basic, Turbo Pascal and Java. In fact, the original intention for this .NET version of the Tetris game is to pick up C#.) 我希望读者不仅喜欢玩游戏,而且希望从发现如何实现游戏的每个功能中获得满足感.(I hope that the reader would not only enjoy playing the game, but also get satisfaction from finding how each feature of the game is implemented.)
历史(History)
- 28(28)日(th)2014年5月:(May, 2014 :)
FallingBlocks
V1(V1) - 29(29)日(th)2014年5月:(May, 2014:)
FallingBlocks
V1a:修正了一些小错误,并缩短了”(V1a: Fix minor bugs and shorten the “)I
“塑造成3个格,更容易玩(” shape to 3 cells for easier play) - 30(30)日(th)2014年5月:新增了2个行删除动画:吸入和爆炸(May, 2014: Added 2 more row removal animations: Suck-In and Explode)
- 1个(1)圣(st)2014年6月:(June, 2014:)
FallingBlocks
V2:添加了声音,网格线和背景音乐(V2: Added in sound, grid lines and background music) - 3(3)rd(rd)2014年6月:(June, 2014:)
FallingBlocks
V2b.用(V2b. Use)winmm.dll(winmm.dll)mciSendString
专门播放音乐/声音(to play music/sound exclusively) - 2(2)nd(nd)2014年7月:添加VB.NET源代码(July, 2014: Add in VB.NET source codes)
参考文献(References)
- “爆炸"行移除动画使用了代码(编译为(The “Explode” row removal animation makes use of the code (complied as)粒子.dll(Particle.dll)),摘自"代码项目"一文,() from the Code Project article,) 基本粒子系统(A Basic Particle System) .(.)
- 声音文件来自(The sound files are from) 圣经(Sound Bible) .(.)
许可
本文以及所有相关的源代码和文件均已获得The Code Project Open License (CPOL)的许可。
VB VB.NET C# .NET VS2008 Win32 Visual-Studio GDI VS.NET2003 新闻 翻译