蜘蛛纸牌游戏的VB.NET版本(译文)
By robot-v1.0
本文链接 https://www.kyfws.com/games/a-vb-net-version-of-the-spider-solitaire-game-zh/
版权声明 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 12 分钟阅读 - 5780 个词 阅读量 0蜘蛛纸牌游戏的VB.NET版本(译文)
原文地址:https://www.codeproject.com/Articles/1071808/A-VB-NET-Version-of-the-Spider-Solitaire-Game
原文作者:Pat Dooley
译文由本站 robot-v1.0 翻译
前言
In this article I will describe the approach I took in creating a VB.NET implementation of the Spider Solitaire Game.
在本文中,我将介绍创建蜘蛛纸牌游戏的VB.NET实现时所采用的方法.
介绍(Introduction)
该程序实现了标准的2层版Spider.这个版本叫做(The program implements the standard two-deck version of Spider. This version is called)红背(Redback).代表纸牌,纸牌堆栈和移动历史的类可用于其他基于纸牌的纸牌游戏,因为特定的游戏逻辑采用表格形式.(. The classes that represent cards, stacks of cards, and move history could be reused for other card based solitaire games since the specific game logic is on the tableau form.)
背景(Background)
RedBack模仿Windows 3.1共享软件游戏(RedBack is modelled on the Windows 3.1 shareware game)**蛛形纲(Arachnid)**我妻子喜欢玩.该游戏在Windows 95下无法正常运行-可以加载,但不能显示卡-因此我在基于Visual Basic对象的编程中将我的版本写为学习练习.(that my wife loved to play. This game didn’t work under Windows 95 - it would load but the cards wouldn’t show - so I wrote my version as a learning exercise in Visual Basic object based programming.) 2004年,我使用1.1 .NET框架将游戏从VB6转换为VB.NET.我添加了更多的蜘蛛图形以及用于隐藏蜘蛛恐惧症的图片的选项.(In 2004, I converted the game from VB6 to VB.NET, using the 1.1 .NET framework. I added more spider graphics plus an option to hide the pictures for the arachnophobic.) 为了与原始名称保持一致,我以臭名昭著的澳大利亚蜘蛛命名为RedBack.它是黑寡妇的近亲.(In keeping with the original name, I called my version RedBack after an infamous Australian spider. It is a close relative of the Black Widow.) 我最近重新整理了图形并整理了本文的代码.(I recently redid the graphics and tidied up the code for this article.) Redback具有一些应我妻子要求添加的不规则功能:(Redback has some irregular features that I added at my wife’s request:)
- 您可以撤消动作并直接回到游戏开始时进行交易.(You can undo moves and deals right back to the beginning of the game.)
- 您只需单击要移动的卡即可移动.(You can make moves simply by clicking the card you want to move.)
创建图形(Creating the graphics)
第一个版本使用了早期Windows 3.1纸牌游戏随附的显卡图形.按照今天的标准,它们太小了,按比例放大会降低图像质量.我是通过送纸扫描仪运行旧纸牌来创建纸面的.然后,我使用Paint.Net处理了图像.我添加了一个带有圆角的两个像素宽的黑色边框.最初,边界外的拐角区域为白色,在屏幕上看起来不佳.我不想返回并手动将这几个像素重置为透明像素.那将需要在像素级别进行52 x 4个单独的编辑.相反,我添加了代码以使有问题的像素透明.(The first version used the card graphics that came with early Windows 3.1 solitaire games. By today’s standards, they are too small, and scaling them up reduced the image quality. I created the card faces by running an old deck of cards through a sheet-feed scanner. I then processed the images using Paint.Net. I added a two pixel wide black border with rounded corners. Initially, the corner areas outside the border were white and they looked bad on the screen. I didn’t feel like going back and manually resetting those few pixels to transparent. That would have required 52 x 4 separate edits working at the pixel level. Instead, I added code to make the offending pixels transparent.)
Public Sub CleanUpCorners(ByRef bmp As System.Drawing.Bitmap, color As Drawing.Color)
Dim limits() As Integer = {7, 5, 4, 3, 2, 1, 1}
For y As Integer = 0 To 6
For x As Integer = 0 To limits(y) - 1
bmp.SetPixel(x, y, color)
bmp.SetPixel(bmp.Width - 1 - x, y, color)
bmp.SetPixel(x, bmp.Height - 1 - y, color)
bmp.SetPixel(bmp.Width - 1 - x, bmp.Height - 1 - y, color)
Next
Next
End Sub
'
' How to invoke
bmp = New Bitmap(clsResources.GetImage("c" + .GetImageIndex(.Suit, .Rank).ToString()))
CleanUpCorners(bmp, System.Drawing.Color.Transparent)
这是不完美的,因为在缩放图像之前会更改像素.(It is not perfect, because the pixels are changed before the image gets scaled.) 我想要一个以蜘蛛为主题的背景,所以我当然在他的允许下从澳大利亚蜘蛛专家那里获得了一些澳大利亚蜘蛛的照片.这些用于创建平铺的背景图像.有一个标签为"我讨厌蜘蛛"的菜单选项可以关闭此美丽背景功能.我不想实现此菜单选项,但我妻子坚决主张.(I wanted a spider themed background so I obtained some Australian spider photographs from an Australian expert on spiders, with his permission, of course. These are used to create a tiled background image. There is a menu option labelled “I hate spiders” to turn off this beautiful background feature. I didn’t want to implement this menu option, but my wife was adamant.)
建筑模块(Building Blocks)
画面(Tableau)
该表格是标准的VB.NET表单.它具有一个用于启动屏幕的面板控件,八个用于完成西装的面板控件以及十个用于发牌的面板.它还具有104个可保存卡片图像的图片框.它们可以在运行时更轻松地创建,但这是原始VB6版本的遗留物.(The tableau is a standard VB.NET form. It has a panel control for the splash screen, eight panel controls for completed suits, and ten panels for the cards that are dealt. It also has 104 picture boxes to hold card images. They could more easily be created at run-time but this was a legacy from the original VB6 version.)
纸牌对象(Playing Card object)
这是一个纸牌游戏,因此需要代表单个纸牌的类.班级(This is a card game, so it needs a class that represents a single card. The class) Card
代表卡的实例.它宣布公开(represents an instance of a card. It declares public)*枚举(Enums)*定义可能的套牌,西装和等级.它还具有这些属性(to define the possible decks, suits and ranks. It also has these properties)
Rank As RankSet
Size as CardSize
FaceUp As Boolean
Playable As Boolean
Clickable As Boolean
Dragable As Boolean
Left As Single
Top As Single
Stack As Stack
StackPosition As Short
Image As System.Windows.Forms.PictureBox
班级(The class) clsCard
还需要响应图像上的事件(also needs to respond to events on the Image, a standard) PictureBox
.必要事件是(. The requisite events are) Mouse_Down, Mouse_Up and Mouse_Move.
在(In)蜘蛛(Spider),您可以成组移动相同花色的纸牌.最初,通过拖放操作移动多张卡会导致可怕的闪烁.解决方案非常简单.只需设置表单属性(, you can move stacks of cards of the same suit as a group. Initially, moving multiple cards with a drag and drop operation resulted in horrible flickering. The solution was very simple. Just set the form property) DoubleBuffered
至(to) True
.(.)
堆叠物件(Stack object)
纸牌纸牌游戏涉及将纸牌从一堆纸牌移动到另一堆纸牌.班级(Solitaire card games involve moving cards from one stack of cards to another stack of cards. The class) clsStack
代表一叠纸牌.它继承自(represents a stack of cards. It inherits from) CollectionBase
,并且我还没有将其更新为使用通用列表对象.(, and I haven’t updated it to use a generic list object.)
在内部,它现在使用(Internally, it now uses a) Dictionary
反对存储卡.它公开了以下属性:(object to store cards. It exposes the following properties:)
Left
Top
SmallHorizontalSpace
LargeHorizontalSpace
SmallVerticalSpace
LargeVerticalSpace
VerticalSpacing
HorizontalSpacing
StackKey ' Enum denoting which stack is represented. Values are STOCK, PILE1 thru PILE10, and HOME1 thru HOME10
TOS ' Top of Stack index
Open ' Stack can be played to
该类为每个实现方法(The class implements methods for each) EnumMoveType
并进行一举一反的交易.无需更改即可迎合其他游戏.(and for going back one move and back one deal. It would not need changing to cater to a different game.)
堆栈对象(Stacks object)
这是一个收集对象,其中包含堆栈对象集.它提供了一种遍历堆栈集合的方法.(This is a collection object that holds sets of stack objects. It provides a way to iterate through a collection of stacks.)
经销商对象(Dealer object)
该对象以伪随机顺序发行卡片.它实现了(This object deals cards in pseudo random order. It implements a) DealCard
方法和一个(method and a) CardsLeft
属性.它最重要的方法是(property. Its most important method is) DealCard
.看起来像这样:(. It looks like this:)
Public Function DealCard(ByVal faceUp As Boolean) As Card
Dim deck As Card.DeckSet
Dim suit As Card.SuitSet
Dim rank As Card.RankSet
Dim card As Card = Nothing
If Me.CardsLeft = 0 Then
Return Nothing
Exit Function
End If
Select Case _DealMode
Case EnumDealMode.RANDOM_DEAL
Do
deck = GetRandom(Card.DeckSet.DECK_ONE, _Decks - 1)
rank = GetRandom(Card.RankSet.LOWEST_RANK + 1, Card.RankSet.HIGHEST_RANK - 1)
suit = GetRandom(Card.SuitSet.LOWEST_SUIT + 1, Card.SuitSet.HIGHEST_SUIT - 1)
Loop Until _CardRecords(deck, rank, suit).Dealt = False
_Seq = _Seq + 1
_CardRecords(deck, rank, suit).Dealt = True
_CardRecords(deck, rank, suit).Seq = _Seq
card = _Cards.Item(CStr(deck) & "." & CStr(rank) & "." & CStr(suit))
card.FaceUp = faceUp
End Select
DealCard = card
End Function
该功能根据甲板,西服和等级选择随机卡.如果该卡已经发出,它将再次尝试.它与游戏无关.(The function selects a random card based on deck, suit and rank. If that card has already been dealt, it tries again. It is game independent.)
移动记录对象(Move Logging object)
自从(Since the)**红背(RedBack)**版本的Spider实现了无限的撤消和交易撤消,它需要跟踪每一个动作并提供撤消它们的方法.此类处理这种复杂性.(version of Spider implements unlimited undos and deal undos, it needs to track every move and provide methods to undo them. This class handles that complexity.)
该类还公开了这两个(The class also exposes these two) Enums
:(:)
Public Enum MoveTypeSet
START_DEAL
END_DEAL
START_MOVE
END_MOVE
MOVE_CARD_FROM_PILE_TO_LIST
MOVE_CARD_FROM_LIST_TO_PILE
TURN_CARD_FACE_UP
End Enum
Public Enum ReplayTypeSet
UNDO_ONE_MOVE
DEAL_1
DEAL_2
DEAL_3
DEAL_4
DEAL_5
End Enum
每个移动都被记录下来,因此可以通过撤消来撤消.这是记录有效卡点击的逻辑.它记录从堆栈中取出卡,将其放置在新堆栈中以及是否需要将新暴露的卡面朝上记录下来.(Each move is logged so it can be reversed by an undo. Here is the logic that logs a valid card click. It logs the removal of a card from a stack, its placement on a new stack, and whether the newly exposed card needs to be turned face up.)
_Log.StartMove()
Stop_Redraw()
For cardIndex = oldPile.Count To startIndex Step -1
moved = moved + 1
cardsToMove(moved) = oldPile.RemovedCard
_Log.MoveCardFromPileToList((oldPile.StackKey))
Next cardIndex
For cardIndex = moved To 1 Step -1
pile.AddCard(cardsToMove(cardIndex))
_Log.MoveCardFromListToPile((pile.StackKey))
Next cardIndex
If Not oldPile.TopCard Is Nothing Then
If oldPile.TopCard.FaceUp = False Then
oldPile.TurnUpTopCard()
_Log.TurnCardFaceUp((oldPile.StackKey))
End If
End If
pile.Refresh()
oldPile.Refresh()
_Log.EndMove()
Start_Redraw()
游戏逻辑(Game Logic)
实际的游戏逻辑在表单上实现.这可能不是放置它的最佳位置.一个可配置的游戏引擎会更好.但是,这就是原始VB6版本中的位置,因此现在已经存在.第一步是创建初始画面或起始位置.(The actual game logic is implemented on the form. This may not be the best place to put it; a configurable game engine would be better. However, that was where it was in the original VB6 version so that is where it lives now. The first step is creating the initial tableau or starting position.)**红背(RedBack)**调用一个称为(calls a method called) StartGame
使事情进展顺利.它显示一个初始屏幕,它只是一个面板控件.必须先将其消除,然后再进行任何操作.(to get things underway. It shows a splash screen, which is just a Panel control. It needs to be dismissed before anything can happen.) StartGame
然后实例化并填充一个用于STOCK的堆栈,十个用于可玩列的堆栈,八个用于接收已完成的套装的堆栈.为了减少屏幕闪烁,此操作在两次调用之间都包含了此操作(then instantiates and populates a stack for the STOCK, ten stacks for the playable columns and eight stacks to receive completed suits. To reduce screen flicker, it encloses this operation between calls to) Stop_Redraw
和(and) Start_Redraw
.(.)
Public Declare Function LockWindowUpdate Lib "user32" (ByVal hwndLock As Integer) As Integer
'
' API calls to stop Windows refreshing during complex operations. Not so necessary when form.DoubleBuffered is set to true.
Public Sub Stop_Redraw()
LockWindowUpdate(Me.Handle)
End Sub
Public Sub Start_Redraw()
LockWindowUpdate(0)
End Sub
初始化游戏后,它将等待玩家单击或拖动卡.这些事件在(Once the game is initialized, it waits for the player to click or drag cards. These events are handled in the) Card
类.的(class. The) MouseMove
事件必须处理这样一个事实,即在被拖动的牌上可能有相同衣服的牌.(event has to deal with the fact that there may be cards of the same suit on top of the card being dragged.)
它在(It invokes a method in the) Card
类称为(class called) MoveCardsOnTop
.此方法在顶部查找卡片,然后将其与所选卡片一起移动.它用(. This methods looks for the cards on top and moves them along with the selected card. It uses) SetBounds
移动元素,而不是设置(to move elements, rather than setting) left
和(and) top
在两个单独的语句中.(in two separate statements.)
这是方法.(Here is the method.)
Private Sub MoveCardsOnTop()
Dim pile As Stack
Dim cardIndex As Short
Dim verticalSpace As Single
pile = Me.Stack
verticalSpace = pile.VerticalSpacing
For cardIndex = Me.StackPosition + 1 To pile.Count
With pile.Card(cardIndex).Image
.SetBounds(Me.Image.Left + pile.HorizontalSpacing, Me.Image.Top + verticalSpace, 0, 0, Windows.Forms.BoundsSpecified.x Or Windows.Forms.BoundsSpecified.y)
End With
verticalSpace += pile.VerticalSpacing
Next cardIndex
End Sub
的(The) Card
MouseUp事件必须确定它是响应拖放事件还是单击事件.它是通过在原始MouseDown事件和MouseUp事件之间的持续时间来实现的.如果持续时间少于0.2秒,则假定它正在响应单击事件.无论哪种情况,它都会调用表单上的方法来处理事件.这是事件代码.(MouseUp event has to decide whether it is responding to a drag and drop event, or a click event. It does it by timimg the duration betweem the original MouseDown event and the MouseUp event. If the duration is less than 0.2 seconds, it assumes it is responding to a click event. In either case, it invokes methods on the form to process the event. Here is the event code.)
Private Sub _imgCard_MouseUp(ByVal eventSender As System.Object, ByVal eventArgs As System.Windows.Forms.MouseEventArgs) Handles _CardImage.MouseUp
Dim newTime As Double
If Not _Playable Then
Exit Sub
End If
newTime = Microsoft.VisualBasic.DateAndTime.Timer
If (newTime - _MouseDownTime) < 0.2 Then
_Moving = False
End If
If _Moving Then
_Moving = False
frmTableau.ProcessCardDrop(Me)
Else
frmTableau.ProcessCardClick(Me)
End If
_MouseDown = False
End Sub
的(The) frmTableau.ProcessCardClick
方法处理卡片单击.它经历了响应卡点击可采取的各种操作.如果是Stock桩,则它将另一行交易到列堆栈中.如果选择了一套完整的西服,则会将西服移到"本垒打"中.否则,它将寻找匹配的西装和等级.如果找不到,它将寻找匹配的西装.如果找不到,它将查找一个空列.如果搜索失败,则返回.否则,它会移动并完成.使用以下方法可以完成移动逻辑(method processes a card click. It goes through the various actions it can take to respond to a card click. If it is the Stock pile, then it deals another row onto the column stacks. If a complete suit has been selected, then it moves the suit to a Home pile. Otherwise, it looks for a matching suit and rank. If it doesn’t find that, it looks for a matching suit. If it doesn’t find that, it looks for an empty column. If the search it unsuccessful, it returns. Otherwise, it makes the move and finishes up. The move logic is done using methods of the) Stack
目的.这是逻辑:(object. Here is the logic:)
moved = 0
For cardIndex = oldPile.Count To startIndex Step -1
moved = moved + 1
cardsToMove(moved) = oldPile.RemovedCard
_Log.MoveCardFromPileToList((oldPile.StackKey))
Next cardIndex
For cardIndex = moved To 1 Step -1
pile.AddCard(cardsToMove(cardIndex))
_Log.MoveCardFromListToPile((pile.StackKey))
Next cardIndex
If Not oldPile.TopCard Is Nothing Then
If oldPile.TopCard.FaceUp = False Then
oldPile.TurnUpTopCard()
_Log.TurnCardFaceUp((oldPile.StackKey))
End If
End If
调整大小(Resizing)
当将太多的卡添加到列堆栈中时,某些版本的Spider会出现问题,它们会从板的底部消失并变得无法触及.为了克服这一点,Redback允许玩家在大,中和小卡大小之间进行选择.(Some versions of Spider have a problem when too many cards are added to a column stack and they disappear off the bottom of the board and become unreachable. To overcome that, Redback lets the player choose between large, medium and small card sizes.)
动画(Animation)
Beta测试员又叫我妻子,我想查看点击股票后所发出的卡片.我添加了一种移动带有动画的卡片的方法.要获得流畅的动画而不出现闪烁,这是一个挑战.在此代码的情况下,我怀疑潜在的问题是表单上有太多控件.尽管所有这些控件都可能迭代每个屏幕绘制,以查看是否需要重新绘制它们.解决方案是在动画过程中禁用表单.(The beta tester, a.k.a my wife, wanted to see the cards being dealt when the stock was clicked. I added a method for moving a card wth animation. Getting smooth animation without flickering proved to be a bit of a challenge. In the case of this code, I suspect the underlying problem is that there are so many controls on the form. Every screen paint probably iterates though all these controls to see if they need to be repainted. The solution was to disable the form during the animation.) 我还为卡片点击添加了动画,因此您可以看到卡片移至其选定的目的地.这是动画方法.(I also added animation to the card click, so you can see the card move to its selected destination. This is the animation method.)
Private Sub AnimateMove(oldPile As Stack, startIndex As Integer, pile As Stack, cumVertOffSet As Integer, throttle As Integer)
Dim left As Integer
Dim top As Integer
'
' Work out how many steps there are from source pile to target pile. Throttle increases the number of steps.
Dim steps As Integer = Math.Max(Math.Abs(oldPile.LeftPos - pile.LeftPos), Math.Abs(oldPile.Top - pile.Top)) / throttle
Select Case _Speed
Case SpeedSet.FAST
steps = Math.Min(_FastTHrottle, steps)
Case SpeedSet.MEDIUM
steps = Math.Min(_MediumThrottle, steps)
Case SpeedSet.SLOW
steps = Math.Min(_SlowThrottle, steps)
End Select
'
' Locate the left and top co-ordinates in the target pile
Dim leftTarget As Integer = If(pile.Count = 0, pile.LeftPos, pile.TopCard.Image.Left)
Dim topTarget As Integer = If(pile.Count = 0, pile.Top, pile.TopCard.Image.Top) + cumVertOffSet
'
' Get the card to move and its co-ordibates
Dim card As Card = oldPile.ThisCard(startIndex)
Dim leftSource As Integer = card.Image.Left
Dim topSource As Integer = card.Image.Top
'
' Calculate how far to move on each iteraction
Dim leftInc As Integer = (leftTarget - leftSource) / steps
Dim topInc As Integer = (topTarget - topSource) / steps
With card.Image
left = .Left
top = .Top
'
' Critical step. If this isn't done, the animation flickers badly
Me.Enabled = False
'
' Iterate through the steps
For i As Integer = 1 To steps
'
' On last step, force card to target location
If i = steps Then
left = leftTarget
top = topTarget
Else
left += leftInc
top += topInc
End If
'
' Slow things down
Thread.Sleep(1)
'
' Move the card
.SetBounds(left, top, .Width, .Height, Windows.Forms.BoundsSpecified.X Or Windows.Forms.BoundsSpecified.Y)
'
' Ensure it is visible
.BringToFront()
.Refresh()
'
' Let Windows do its thing
Application.DoEvents()
Next
'
' Reset the card position
card.Top = topTarget
card.LeftPos = leftTarget
'
' Re-enable the tableau.
Me.Enabled = True
End With
End Sub
帮帮我(Help)
我使用Robohelp为VB6版本创建了一个帮助文件.当我尝试更新它时,发现不再安装RoboHelp.因此,我创建了一个简单的HTML帮助文件.它看起来比旧的.CHM文件更好.(I created a help file for the VB6 version using Robohelp. When I tried to update it, I found I didn’t have RoboHelp installed anymore. So, I created a simple HTML help file. It looks better than the old .CHM file.)
使用代码(Using the code)
此代码使用中间的VB.NET编码技术.因此,对于中级开发人员而言,将代码改编为其他或其他单人游戏应该相对容易.的(This code uses intermediate VB.NET coding techniques. As such, it should be relatively easy for an intermediate developer to adapt the code for other or additional solitaire games. The) Card
,(,) Stack
,(,) Dealer
和(, and) LogMove
类不是特定于蜘蛛纸牌的,可以在不更改其他游戏的情况下使用.(classes are not specific to Spider solitaire and could be used without change in another game.)
某些邮件服务器拒绝包含扩展名为.vb的文件的zip文件.从zip文件中提取文件后,将.vbsrc文件的扩展名更改为.vb.(Some mail servers reject zip files containing files with with the extension .vb. After you extract the files from the zip file, change the extension of .vbsrc files to .vb.)
兴趣点(Points of Interest)
单击卡片并将其移动到最明显的位置的能力可以加快游戏速度.无限撤消消除了玩更具挑战性的纸牌游戏之一的挫败感.蜘蛛背景是大多数玩家都会喜欢的新颖事物.(The ability to click a card and have it move to the most obvious location speeds up the game. Unlimited undos remove the frustration of playing one of the more challenging Solitaire games. The spider backgrounds are a novelty that most players will turn off.)
历史(History)
代码项目的第一个版本(First Version for Code-Project)
许可
本文以及所有相关的源代码和文件均已获得The Code Project Open License (CPOL)的许可。
VB.NET HTML .NET4.5 Windows VS2013 windows-forms 新闻 翻译