Coding a Euchre Game, Part 2: Show me the cards! (Matt Gertz)

Coding a Euchre Game, Part 2: Show me the cards! (Matt Gertz)

  • Comments 2

Coding a Euchre Game, Part 2: Show me the cards!

In my previous post, I discussed using control-drag to speed up the design of a form which uses lots of similar controls.  In this post, I’m going to discuss displaying images on those controls.

Now, the face of each card is going to need an image, and you’ll also need one image for the back of the card (which you can use for any of the cards).  This is where I’m afraid you’re going to have to leverage your own artistic talents, unless you can find some "free" cards.  For VBEuchre, I used Paint.exe to design each card by hand, because I couldn’t find a set of card images which weren’t copyrighted -- I've attached a few examples to this post.  (Before you get too impressed, I should mention that there was a lot of copy/paste involved in making those images!) At any rate, what I ended up with were a set of vertical card images for each of the 24 cards in a Euchre deck, plus the card back – 25 images in total.  (All will be made available to you when I post the final code.)

The cards need to go into the resource manager.  So, bring up the resource manager (hereafter referred to as the RM) by right-clicking on the project and choosing “Properties,” then clicking the Resources tab.  In the upper-left corner of the resulting RM, there will be a dropdown control currently set to “Strings.”  Change that to “Images,” so that you can see any images in the project.  Adding images into the RM is really easy; just click the dropdown arrow next to “Add Resource,” choose “Add Existing Resource,” and multiselect the image files.  Once you press OK, they’ll be added to the resources.

You’ll want to make sure that the images are embedded in the assembly itself, so that when you deploy the app, you don’t have to deal with a bunch of separate files.  You can multiselect the images and verify that their “Persistence” property is set to “Linked at compile time” to accomplish this.

Now, you’re going to want to have a consistent naming scheme for the cards so that it’s easy to find them in the RM.  I chose CARDFACErankOfsuit (e.g., CARDFACEJackOfClubs) for the face-up cards and CARDBACK for the image which represents the back of the cards.  This makes it really easy to load the image for a given face-up card by using this helper function in my EuchreCard class:

    Public Function GetImageResourceName() As String

        Dim FileName As New StringBuilder()

        FileName.AppendFormat("CARDFACE{0}Of{1}", Rank, Suit)

        Return FileName.ToString

    End Function

 

which I later leverage from my EuchreDeck’s constructor (using the enums Suits and Ranks defined on the EuchreCard class, and where Cards is an member array of type EuchreCard):

            Dim NewSuit As EuchreCard.Suits

            Dim NewRank As EuchreCard.Ranks

            Dim n As Integer = 0

            For NewSuit = EuchreCard.Suits.Spades To EuchreCard.Suits.Hearts 

                For NewRank = EuchreCard.Ranks.Nine To EuchreCard.Ranks.Ace

                        Cards(n) = New EuchreCard(NewRank, NewSuit, Table)

                        Cards(n).im = _ My.Resources.ResourceManager.GetObject(Cards(n).GetImageResourceName())

                        n = n + 1

                Next NewRank

            Next NewSuit

 

You’ll note that I’m using the GetObject() call on the RM to retrieve the card – this is because I’m constructing the card’s image name in code.  For a static name, such as CARDBACK, it’s even simpler than that to initialize the card back member image, thanks to the automatic friend properties that we generated for resources:

            EuchreCard.CardBackImage = My.Resources.CARDBACK

Cool, no? I also know that I’ll be showing tooltips for my cards, so I create a bunch of string resources in the RM named CARDNAMErankOfSuit (e.g., CARDNAMEJackOfClubs having the value “Jack of Clubs”) to prepare for that.  (The RM supports cutting and pasting text, which makes this go fairly quickly.)

Now, you’ll note that I only specified one image per card, despite the fact that the orientation of the card will change over the course of the game.  I don’t want to have a separate image for both horizontal and vertical – that would waste memory, and actually I'd need four images anyway because I want the cards to have the correct perspective relative to the owner of the card.  Instead, I’ll create a property on EuchreCard to return the current orientation of the card, as well as set it based on the different from it’s *last* orientation.  In the following code, EuchrePlayer.Seats is an enum which specifies the owning player (and hence a desired orientation for the card), im is a EuchreCard’s member image of the card face, and the code just determines the difference in 90-degree increments from the current orientation and rotates the image accordingly using RotateFlip:

    Public Enum Seats

        LeftOpponent = 0

        Partner = 1

        RightOpponent = 2

        Player = 3

    End Enum

 

    Public ori As EuchrePlayer.Seats

    Public Property Perspective() As EuchrePlayer.Seats

        Get

            Return ori

        End Get

        Set(ByVal value As EuchrePlayer.Seats)

            If ori <> value Then

                Dim diff As Integer

                diff = ori - value

                Dim rft As RotateFlipType

                If diff = -1 OrElse diff = 3 Then

                    rft = RotateFlipType.Rotate90FlipNone

                ElseIf diff = 2 OrElse diff = -2 Then

                    rft = RotateFlipType.Rotate180FlipNone

                ElseIf diff = 1 OrElse diff = -3 Then

                    rft = RotateFlipType.Rotate270FlipNone

                End If

                im.RotateFlip(rft)

                ori = value

            End If

        End Set

    End Property

Now the cards will appear in the correct perspective to the player who owns them, just as would happen at a real card table!  (Those who look at my final code (which I'll post at the end of this blog series) will note that, for the card back orientations, I just clone three extra images and rotate them to different orientations during the game initialization, and use them as necessary – a memory “splurge,” if you will.  There’s such a thing as being *too* clever…)

Now, if you’ll recall from Part 1, we created a bunch of labels on the form where the cards should show up when used.  To make life easier, I created an array in my EuchreTable form class which maps the labels to a 2D array indexed by player (left, opposite, right, player) and card position in the hand (1-5 plus the played card):

    Public TableTopCards(4, 6) As System.Windows.Forms.Label

    ' (Player index, card index -- final value is played card)

 

And then initialize it in the constructor with actual references to the labels. I was tedious about this in code – ideally, I should construct the label name to reference in the array and then look through the controls of the form to find the matching control, but that seemed rather non-performant so, in the absence of control arrays, I skipped it and just did a line for each:

        ' Yuck.

        Me.TableTopCards(EuchrePlayer.Seats.LeftOpponent, 0) = Me.LeftOpponentCard1

        Me.TableTopCards(EuchrePlayer.Seats.LeftOpponent, 1) = Me.LeftOpponentCard2

        ' Etc…

 

That's grotesque, but I don't actually win a lot in this case by trying to be more elegant by constructing the label's name in a loop and then using Me.Controls.Find() to get the reference to assign to the array -- in fact, I'd actually lose performance.

Now we have everything we need to display cards as required by class EuchreTable.  For example, let’s set the card images for the user’s hand.  First, we’ll set the cards’ orientations back to that of the user, then we’ll update the label on the form representing that card location to be the image of the appropriate card in the player’s hand, and then we’ll issue an Update command to make sure that the image gets refreshed immediately.  (In this code, Players is the array of EuchrePlayer objects, and TableTopCards is a 2D array of form labels indexed by the position of the card and the player holding them.)

  Dim i As Integer

  For i = 0 To 4

Players(EuchrePlayer.Seats.Player).CardsHeldThisHand(i).Perspective _

  = EuchrePlayer.Seats.Player

 

TableTopCards(EuchrePlayer.Seats.Player, i).Image = Players( _

  EuchrePlayer.Seats.Player).CardsHeldThisHand(i).GetImage( _

  EuchrePlayer.Seats.Player)

           

TableTopCards(EuchrePlayer.Seats.Player, i).Update()

  Next

 

and that’s pretty much it.

Next time we’ll talk about timer controls, which are a very important controls to use when writing games.  See you then!

--Matt--*

Attachment: Euchre card images.zip
Leave a Comment
  • Please add 3 and 5 and type the answer here:
  • Post
Page 1 of 1 (2 items)