40. Working with playing cards – part 2
In this post, we’ll create (ie draw) an entire deck of cards from scratch.
There are several stages to doing this
- drawing a rounded card shape
- finding pictures for the suits and royalty cards
- drawing the card faces
- drawing the results to the screen face up, face down, at an angle etc
..
Drawing a basic card
Playing cards have rounded corners, so they are rounded rectangles. There has been some discussion on the Codea forums about drawing these (for dialog boxes, but the idea is the same).
There are two obvious methods –
1. draw four small circles at the corners plus a couple of rectangles
2. use a couple of thick lines with rounded ends, plus a couple of rectangles
I’ve used the first method below. There is code for the other one in this thread.
function Card:createOutline(face) --use standard 25/35 ratio self.width=math.floor(self.height*25/35+.5) local img=image(self.width,self.height) --create rounded corner on top right local corner=0.05 --distance from end of card as percent of height local c=math.floor(corner*self.height+0.5) setContext(img) pushStyle() strokeWidth(1) stroke(self.cornerColor) --set background colour if face then fill(self.faceColor) else fill(self.backColor) end --draw small circles at corners ellipse(self.width-c+1,self.height-c+1,c*2) ellipse(self.width-c+1,c-1,c*2) ellipse(c-1,self.height-c+1,c*2) ellipse(c-1,c-1,c*2) if face then stroke(self.faceColor) else stroke(self.backColor) end --now rectangles to fill in thre centre of the card rect(0,c,self.width,self.height-c*2) rect(c,0,self.width-c*2,self.height) --now a border round the card stroke(self.borderColor) line(0,c,0,self.height-c) line(c,0,self.width-c,0) line(self.width,c,self.width,self.height-c) line(c,self.height,self.width-c,self.height) --do picture on back if face~=true then sprite(self.backImg,img.width/2,img.height/2,img.width*.9) end popStyle() setContext() return img end
Note that I only have to create one card like this, put it in an image, and after that, I can just use that image as a template for any particular card that I need.
Finding pictures for the suits and royalty cards
Note – by royalty, I mean of course the King, Queen and Jack pictures usually shown on cards.
I could have borrowed pictures from the internet, but they would have made it more difficult to share the code with you, they would not have scaled to larger or smaller sizes, and besides, I always like to write code that is self contained, with no dependencies.
So I had a look in the emoji character set that I wrote about in post 25, and I found some good pictures of the four suits in there, and some people pictures that were close enough to do for the royalty pictures. This means my code can generate all the cards completely by itself.
Drawing the card faces
This is the hardest part, because every one of the 13 cards is different, using little suit images that are all in different places, and half of them have to be drawn upside down!
I’m not going to put all of the code in here because it’s quite long (I will provide it below, of course), but essentially it does the following, given a particular card to draw
- 1. figure out the suit and value
- 2. set the text colour to red or black depending on the suit
- 3. put the number in the top left corner and the suit image under it
- 4. draw the suit images in the middle of the card that are the right way up
- 5. rotate the card 180 degrees and repeat from step 3
.
Most of the numbers and images on the cards are mirrored, that is, if you flip the card 180 degrees they look exactly the same. However, some odd numbered cards have an extra image in the middle that doesn’t have an upside down partner, so I need “if” statements to prevent that happening.
My main problem in all of this was guessing exactly where to put all the bits on the cards, and it was definitely by trial and error.
Drawing to the screen
This is the easy part, even if you want to draw the card at an angle
--draws a card at x,y with value of card (1-52), face=true if face up, a=angle in degrees (default 0) function Card:draw(x,y,card,face,a) pushMatrix() --reset 0,0 to be the bottom left of the card --for drawing purposes, to make coding easier translate(x+self.width/2,y-self.height/2) if a==nil then a=0 end --angle rotate(a) if face then --face up --drawDetails is the function that draws all the details if card>0 then self:drawDetails(card) else sprite(self.cardFace,0,0) end else sprite(self.cardBack,0,0) end popMatrix() end
Which gives the ability (if we include the shuffling and dealing from the last post), to deal cards to a hand and display them on the screen. Here is the code.
This is very exciting, but you’ll find if you try to program a real game of cards, it can get quite tricky. For example, just basic patience requires dealing overlapping cards, managing a pack of unused cards, a waste pile of cards, four ace piles, and allowing dragging of cards from one place to other, following various rules. So I’m only about halfway through all that…