永无止境的记忆游戏(译文)
By robot-v1.0
本文链接 https://www.kyfws.com/games/the-never-ending-memory-game-zh/
版权声明 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 11 分钟阅读 - 5260 个词 阅读量 0永无止境的记忆游戏(译文)
原文地址:https://www.codeproject.com/Articles/36051/The-Never-Ending-Memory-Game
原文作者:davehamdan
译文由本站 robot-v1.0 翻译
前言
Learn how to write a small game using an XBap
了解如何使用XBap编写小型游戏
介绍(Introduction)
2009年初,Microsoft举行了一次编程比赛,以使人们对WPF/XAML技术感兴趣.挑战很简单:仅用10k的代码即可创建令人赞叹的内容.该程序必须使用以下格式之一的WPF:ClickOnce,Silverlight或XBap.我一直在阅读XBaps,认为范例听起来很酷,所以我选择尝试为比赛创建一个小型XBap应用.(Earlier in 2009, Microsoft held a programming competition to get folks interested in WPF/XAML technology. The challenge was simple: create something mind-blowing with just 10k of code. The program had to utilize WPF in one of the following formats: ClickOnce, Silverlight or XBap. I’d been reading up on XBaps and thought the paradigm sounded pretty cool, so I chose to attempt to create a small XBap app for the contest.)
背景(Background)
如果读者不知道,则XBap代表XAML浏览器应用程序.这是ClickOnce部署的下一个重大飞跃;用户只需单击网页中托管的超链接即可启动该应用程序.该链接指向一个(If the reader is unaware, XBap stands for XAML Browser Application. It’s the next great leap in ClickOnce deployment; the user simply clicks on a hyperlink hosted in a web page to launch the application. This link points to a)**.xbap(.xbap)**已部署到托管应用程序的网络服务器上.这就是ClickOnce部署的确切方式,但是使用XBaps,客户端浏览器会下载该应用程序并在浏览器空间中启动它.(that’s been deployed to the web server that hosts the application. This is exactly how ClickOnce deployments occur, but with XBaps, the client browser downloads the app and launches it within the browser space.) Silverlight应用程序也托管在浏览器中.虽然XBaps和Silverlight应用程序都在沙箱中运行,但XBaps提供了完整的WPF编程模型,而Silverlight仅可以使用.NET Framework的子集.下表是将两者进行比较的简要表:(Silverlight apps are also hosted within the browser. While both XBaps and Silverlight apps run in a sandbox, XBaps provide the full WPF programming model while Silverlight only can use a subset of the .NET Framework. Here’s a brief table to compare the two:)
XBap(XBap) | 银光(Silverlight) | |
---|---|---|
Programming | 完整的.NET/WPF框架(Full .NET/WPF Framework) | 框架和WPF的子集(Subset of framework and WPF) |
客户端机器(Client machine) | 需要完整的.NET Framework(Requires full .NET Framework) | 无需框架安装,只需要Silverlight客户端(〜5MB下载)(No framework installation required, only need the Silverlight client (~5MB download)) |
Platform | 客户端计算机必须是Windows计算机或模拟Windows(Client machine must be Windows machine or emulated Windows) | 可以在任何支持Silverlight客户端的平台上运行(Win,MacOS,即将在Linux上)(Can run on any platform that supports the Silverlight client (Win, MacOS, soon Linux)) |
Browser | 可以在Internet Explorer或Firefox中运行(Can run in Internet Explorer or Firefox) | 任何可以支持Silverlight客户端的浏览器(Any browser that can support the Silverlight client) |
需要客户端软件(Client Software required) | .NET 2.0/3.5可再发行组件包(〜30 MB)(.NET 2.0/3.5 Redistributable Package (~30 MB)) | Silverlight运行时(〜5 MB)(Silverlight Runtime (~5 MB)) |
Sandbox | 两者都在沙箱中运行;也就是说,保护客户端计算机免受本地磁盘访问,注册表访问等影响.两者都可以使用和利用服务(XBaps必须使用原始服务器)(Both run in a sandbox; i.e. protect the client machine from local disk access, registry access, etc. Both can also consume and utilize services (XBaps must use server of origin)) |
游戏(The Game)
我为比赛想到的想法是创建一个记忆匹配游戏.这是一个简单的游戏,用户在面板上"翻转"一张卡片,翻转第二张卡片后,检查用户是否选择了匹配的卡片.如果没有,则再次翻转卡片以寻找另一回合.诀窍是记住错过的比赛并在其他地方找到匹配的卡片,前提是用户可以记住原始比赛在哪里.(The idea I came up with for the contest was to create a memory matching game. It’s a simple game where the user “flips” a card on the board and after flipping a second card, checks to see if the user chose a matching card. If not, the cards are flipped again to look for another turn. The trick is to memorize the missed matches and find the matching card elsewhere if the user can remember where the original match is.) 从理论上讲,这款游戏的乐趣无穷无尽.原始板以4张卡开头,要求用户找到2个匹配项.然后,用户进入下一个级别,进行4个匹配,然后是8个,然后是12个,依此类推,以此类推,直到正方形变得太小而无法单击! ; o)(The fun twist to this game is that it’s theoretically never-ending. The original board starts with 4 cards that require the user to find 2 matches. Then the user moves on to the next level making 4 matches, then 8, then 12, so on and so forth until the squares get too small to click on! ;o))
使用代码(Using the Code)
有两个源zip文件:一个是比赛的原始提交文件,另一个是"未压缩"版本.未压缩的版本已重新格式化,并更改了其某些对象和变量名称,以使代码更加清晰.请记住,这是一个10k的挑战,因此空白的每一位都计入字节总数.如果您查看压缩版本(为比赛提交的原始版本),您会发现我非常小心地利用了我可以使用的每个字节!从现在开始,我将引用源的未压缩版本中包含的变量和方法名称.(There are two source code zip files: one is the original submission for the contest and another “uncompressed” version. The uncompressed version has been reformatted and has had some of its object and variable names changed to try to bring clarity to the code. Keep in mind this was a 10k challenge, so every bit of whitespace counted against the total number of bytes. If you look at the compressed version (the original version submitted for the contest) you’ll see that I was very careful to utilize every byte I could! From this point forward, I will make reference to variable and method names that are included in the uncompressed version of the source.)
电路板布局和卡结构(Board Layout and Card Construction)
整个游戏使用(The entire game is created using a) Grid
会根据当前级别动态生成行和列.如果一个(that has dynamically generated rows and columns based on the current level. If a) Grid
包含(contains) ColumnDefinitions
和(and) RowDefinitions
没有分配高度或宽度,WPF的布局引擎会自动将它们均匀地隔开.发现这个小窍门是促使我着手整个程序的原因.(have no height or width assigned to them, WPF’s layout engine will automatically space them out evenly. Discovering this little tid-bit was what got me started on this entire program.)
主级别变量((The main level variable () _currentLevel
)跟踪用户在整个游戏中的进度,并用作每个棋盘的主要推动力.电路板本身是使用() keeps track of the user’s progress through the game and is used as the main driving value for each board. The board itself is constructed using the) BuildGrid()
将行和列添加到(method which adds the rows and columns to the) Grid
.(.)
该过程的下一步是使用(The next step in the process is to create the “cards” using the) BuildCards()
方法.有一个自定义对象,称为(method. There is a custom object called) PlayerCard
其中包含各种信息,我们将在本文后面介绍.(that holds various pieces of information that we’ll cover a bit later in this article.)
class PlayerCard
{
public int myXCoordinate { get; set; }
public int myYCoordinate { get; set; }
public int partnerXCoordinate { get; set; }
public int partnerYCoordinate { get; set; }
public bool firstGuess { get; set; }
public bool secondGuess { get; set; }
}
的(The) BuildCards()
遵循以下基本算法:(follows this basic algorithm:)
-
计算达到当前级别所需的卡数(Calculate the number of cards needed to fill the current level)
- 每隔一个级别都有一个奇数个空格,因此这些级别得到一个空格(Every other level has an odd number of spaces, so those levels get a blank space)
-
创建卡(Create a card)
- 在网格上随机选择一个正方形(Randomly pick a square on the grid)
- 如果尚未用卡填充此正方形,请用当前卡填充(If this square is not already populated by a card, populate it with the current card)
- 如果已填充此正方形,请在网格中随机选择另一个正方形,直到找到一个空正方形(If this square is populated, randomly pick another square in the grid until an empty square is found)
- 这似乎并不是所有的优化.好吧,不是.该代码的重点是最小化.但是令我感到惊讶的是C#的处理效果如何^在开发过程中,我可以看到它构建在34\35级上. 65\66…而不会放慢太多! ; o)(This may not seem all that optimized. Well, it isn’t. The focus of this code was minimization. But I am surprised at how well C# does handle this though… during development I could watch it build level 34, 35…. 65, 66… without slowing down too much! ;o))
-
创建下一张卡(这将是第一张卡的匹配项)(Create the next card (this will be the match of the first card))
- 遵循与第一张卡片相同的算法(Follow the same algorithm as the first card)
- 在网格上找到一个正方形并填充了此卡片后,请为该卡片分配第一张卡片的网格坐标(Once a square on the grid is found and populated with this card, assign this card the first card’s grid coordinates)
- 返回创建的第一个卡,并为其分配2(Go back to the first card created and assign it the 2)nd(nd)卡的坐标(card’s coordinates)
- 这就是PlayerCard的… Coordinate变量用于(This is what the PlayerCard’s …Coordinate variables are for)
-
重复直到创建所有卡片(Repeat until all cards are created) 每个"卡"实际上只是一个(Each “card” is really just a)
Border
目的.毕竟(object. After all the)Border
创建了,每对都使用分配了随机背景(s are created, each pair gets assigned a randomized background using a)RadialGradientBrush
和(and the)BackerGradient()
方法.此方法对颜色和偏移量进行加扰(method. This method scrambles the colors and offsets of)GradientStop
对象以为每对创建不同的模式.作为"永无止境"的记忆游戏,随着用户等级的提高,这种差异化效果越来越小.但这仅仅是游戏的挑战!(objects to create a different pattern for each pair. Being a “never-ending” memory game, as the user gets higher and higher in the levels, this has less and less of a differentiating effect. But that’s just the challenge of the game!) 通过创建一个(The algorithm finishes off by then creating a)Button
每张卡的对象.每个按钮的大小与(object for each card. Each button is the exact same size as the)Border
并位于边界上方.这些按钮都被分配了(and lies on top of the Border. These buttons all get assigned the)CardClickCheckForMatch(…)
处理点击和(to handle the click and the)CardMouseMoveAnimation(…)
处理出色的鼠标悬停淡入效果.因此,通过有效地设置按钮动画效果,可以实现"卡片翻转"操作(to handle the nice mouse-over fade effect. So the “card flipping” action is achieved by effectively animating the buttons)Opacity
卡被点击后变为空.然后,如果找不到匹配项,则通过重新设置按钮的动画来"翻转"两张卡片(to nothing when a card gets clicked. Then if a match is not found, both cards get “flipped” back by re-animating the button’s)Opacity
回到100.(back to 100.)
比赛检测和计分(Match Detection and Scoring)
匹配检测是通过使用匹配状态变量((The match detection is achieved by using a matching state variable () _turnCount
)和() and the) CardClickCheckForMatch(…)
方法.如果当前点击是匹配检查中点击的第一张卡片,则该方法会在卡片集合中找到被点击的卡片((method. If the current click is the first card clicked in a matching check, the method finds the clicked card in the card collection () _PlayerCards
)基于匹配的网格坐标并标记其() based on matching grid coordinates and marks its) firstGuess
成员为true并返回.如果这是2(member as true and returns. If this is the 2)nd(nd)依次按一下,然后按2(click in the turn, then the 2)nd(nd)卡在网格中找到(card is found in the grid and its) secondGuess
设定为(is set to) true
.然后(. Then the) MatchCheck(…)
方法被调用.(method is called.)
的(The) MatchCheck(…)
方法确定是否找到匹配项,方法是首先使用LINQ to Objects拔出具有(method determines if a match is found by first using LINQ to Objects to pull out the cards that have) firstGuess
和(and) secondGuess
已标记.如果1(marked. If the 1)圣(st)牌(card’s) myXCoordinate
/(/) myYCoordinate
和2(and the 2)nd(nd)牌(card’s) partnerXCoordinate
/(/) partnerYCoordinate
相等,那么我们找到了匹配!(are equal, then we have found a match!)
然后使用(Scoring is then calculated using the) ReportScore(…)
方法.点值由许多因素决定:(method. The point value is determined by a number of factors:)
-
当前级别号(The current level number)
-
自从上一场比赛或级别开始以来已经过去了多少时间(How much time has elapsed since the previous match or the level started)
- 匹配越快,积分值越高(The quicker the match, the higher the point value)
-
组合顺序(Combo sequence)
- 每次连续比赛都不会错过,将对分数应用10的倍数(Each consecutive match without a miss results in a multiple of 10 applied to the score)
-
游戏开始时选择的难度设置(The Difficulty setting chosen at the start of the game) 如果这是未命中,则还将基于当前级别编号和难度设置以类似方式扣除分数.无论计算出的积分值如何,它都会添加到游戏总得分中,然后在屏幕上的2(If this is a miss, then points are also deducted in a similar fashion based on current level number and Difficulty setting. Regardless of the point value calculated, it’s added to the overall game score then drawn on the screen over the 2)nd(nd)点击卡时会出现不错的淡入淡出效果,(card clicked with a nice fade and)
TranslateTransform
使游戏更具吸引力.(to give the game some “pop” appeal.) 的(The)ScorePoint(…)
哪个电话(which houses the call to)ReportScore(…)
还确定这是否是游戏中的最终比赛,并向用户奖励其表现的统计数据.然后,用户将有机会继续前进到下一个级别并继续乐趣!(also determines if this is the final match in the game and rewards the user with statistics of their performance. The user then is given the opportunity to move on to the next level and continue the fun!)
难度设定(The Difficulty Setting)
首次启动游戏时,用户可以使用(Upon first starting the game, the user gets to choose a difficulty setting using a) Slider
.难度通过以下方式影响游戏^(. Difficulty affects the game in the following ways…)
-
向轻松<-(Toward Easy <–)
- 错过的比赛速度较慢,可以留出更多时间记忆错过的卡牌(Missed matches flip slower allowing for more time to memorize the missed card pattern)
- 在找到的比赛计算中获得较少的积分(Less points are awarded in found match calculation)
- 错过时扣除的积分减少(Less points are deducted during a miss)
-
走向困难->(Toward Difficult –>)
- 未命中时卡片翻转速度更快(Card flip faster during a miss)
- 比赛中获得更多积分(More points are awarded during a match)
- 错过时会扣除更多积分(More points are deducted during a miss)
兴趣点(Points of Interest)
在学习有关游戏编程和WPF/XAML的知识方面,这是一次有趣的冒险.我在WPF动画中学习了一堆新技术.(This was a fun adventure in learning a bit about game programming and WPF/XAML. I picked up a bunch of new techniques in WPF animation.)
历史(History)
- 30(30)日(th)2009年4月:初始职位(April, 2009: Initial post)
许可
本文以及所有相关的源代码和文件均已获得The Code Project Open License (CPOL)的许可。
C# C#3.0 Win2003 Vista WinXP Visual-Studio XAML VS2008 Marketing Dev 新闻 翻译