WEBINAR: On-demand webcast
How to Boost Database Development Productivity on Linux, Docker, and Kubernetes with Microsoft SQL Server 2017 REGISTER >
In previous Animation articles we've covered many different methods and aspects of the animation. This article looks at interacting with objects on a playing field. As usual the article will use a game as a base for the code. This time we will work on a simple Solitaire game base, and as usual I will only be doing the animation and leave it up to you to complete the rest of the game.
When you think about Solitaire, you don't necessarily think of animation. However there is an aspect of animation that is very heavily used in Solitaire — Object interaction. We will be looking at interaction with several objects, with some that may be overlapping each other.
Not long ago there was a thread on CodeGuru where someone was looking for a method to detect mouse movement over different areas of an image, clickable Hotspots, so to speak. There are many ways to achieve the same results, and the method shown here might not be ideal for all applications, but is a good tool to have in your box of tricks.
The Graphics for Our Animation Example
Let's start with the graphics. Firstly let me say that, yes, I am aware that there is a DLL with all the card graphics in it that you can use, however I decided to use image files so that you can replace them with any that you choose. There are 60 odd images, including all the playing cards, two jokers and several Backings. So the first task is to load the images.
Private Cards() As Image Private Backs() As Image Private CardSize As Size Private Sub BuildCards() ReDim Cards(53) ReDim Backs(1) Dim Tmp As Integer Dim Path As String = Application.StartupPath For Tmp = 0 To 53 Cards(Tmp) = New Bitmap(Path & "\card\" & (Tmp + 1).ToString & ".png") Next For Tmp = 0 To 1 Backs(Tmp) = New Bitmap(Path & "\card\b" & (Tmp + 1).ToString & "fv.png") Next 'Lets use one of the cards to get the size.. CardSize = Cards(0).Size End Sub
But now there is additional information that is needed. We want the draw order of the cards, so that one card can be put on top of another. We also want to know if the card is not shown, or turned over, and then we want the position of the card. And all of this can be initialized with the card loading, so our above sub can be expanded a little.
Private Cards() As Image Private Backs() As Image Private CardPos() As Point Private CardShow() As ShowMode Private DrawOrder() As Integer Private CardSize As Size Private MyBrushs() As SolidBrush Private Enum ShowMode Hidden Show Back End Enum Private Sub BuildCards() ReDim Cards(53) ReDim Backs(1) ReDim CardShow(53) ReDim CardPos(53) ReDim MyBrushs(53) ReDim DrawOrder(53) Dim Tmp As Integer Dim Path As String = Application.StartupPath For Tmp = 0 To 53 Cards(Tmp) = New Bitmap(Path & "\card\" & (Tmp + 1).ToString & ".png") MyBrushs(Tmp) = New SolidBrush(Color.FromArgb(&HFF000000 Or (Tmp + 1))) DrawOrder(Tmp) = Tmp Next For Tmp = 0 To 1 Backs(Tmp) = New Bitmap(Path & "\card\b" & (Tmp + 1).ToString & "fv.png") Next 'Lets use one of the cards to get the size.. CardSize = Cards(0).Size End Sub
At the same time, we are also setting up a color brush for our hotspots. For each unique card we have a unique colored Hotspot image, I used RGB colors 1-54 for the 54 unique cards and RGB 0 for the board.
Now we have the cards in memory, you can now start building the game board. We will be using the Standard Green that most people associate with card games for the background. So we have our Form, and I normally like to place a picturebox on the form, however you can use the form's image too. So our game board is this image, and we will use it to build our Buffer. Buffers is explained in full on previous articles so I'm not going into in here
Private Buffer As Image Private BufferG As Graphics Private HotSpots As Image Private HotSpotsG As Graphics Private Boardsize As Rectangle Private Sub InitBuffer() Buffer = New Bitmap(Board.Width, Board.Height) BufferG = System.Drawing.Graphics.FromImage(Buffer) Boardsize.Size = Buffer.Size HotSpots = New Bitmap(Board.Width, Board.Height) HotSpotsG = System.Drawing.Graphics.FromImage(HotSpots) End Sub
As you may have noticed, I'm initializing two Buffers, The second one will not be drawn but used for the hotspot detection. When a card is drawn on the board's buffer, the Hotspot image will be drawn in the same position in the second buffer. Of course now we need some code to draw these images.
Private Sub DoDisplay() BuildBuffer() ShowCards() Board.Image = Buffer End Sub Private Sub BuildBuffer() BufferG.FillRectangle(Brushes.Green, Boardsize) HotSpotsG.FillRectangle(BackBrush, Boardsize) End Sub Private Sub ShowCards() Dim Tmp As Integer Dim Order As Integer For Tmp = 0 To 53 Order = DrawOrder(Tmp) Select Case CardShow(Order) Case ShowMode.Show BufferG.DrawImage(Cards(Order), CardPos(Order)) HotSpotsG.FillRectangle(MyBrushs(Order), CardPos(Order).X, _ CardPos(Order).Y, CardSize.Width, _ CardSize.Height) Case ShowMode.Back BufferG.DrawImage(Backs(0), CardPos(Order).X, CardPos(Order).Y, _ CardSize.Width, CardSize.Height) HotSpotsG.FillRectangle(MyBrushs(Order), CardPos(Order).X, _ CardPos(Order).Y, CardSize.Width, _ CardSize.Height) Case ShowMode.Hidden If MouseCard - 1 = Order Then BufferG.DrawImage(Cards(Order), CardPos(Order)) End If End Select Next End Sub
Here I've split the key codes into several different subs so that it's easier to see what's doing what.
- BuildBuffer simply paints our initial background, so we have a clean image to place cards on.
- ShowCards paints each card on the buffer in the order listed in the Cardshow Array, with bottom card first.
- Dodisplay pulls it all together, and places the buffer image on the form.