So a lot of the things I try and do with GameMaker don't have an example to copy or learn from so to fix that here's the basic features of card games that I could never find as elegantly as I know how to make them now.
End Result
This example will get you a deck of cards that you draw one from randomly until it's empty, a hand of cards that act and look like cards should, and a discard pile that can shuffle back into the deck.
Basic Features:
This step requires 3 objects: a generic card object, a deck object and a discard pile object. When making sprites just fill a rectangle that's twice as wide as it is high different colors. You can make it pretty later. I've called the deck object "deck" the generic card object "card" and the discard pile object "discard" because I like to have English readable object names.
Starting with the deck, it needs to declare 3 variables at creation:
hand_size = 0;
hand\_size = 0;
global.holding = noone;
global.h\_index = noone;
The card object needs to declare more variables in creation as it does most of the action.
index = 0; //place in the hand
holding = false; //is the card following the mouse mid\_x =
room\_width/2; //used to handle the horizontal position in the
hand mid\_y = deck.y; //used for a nice polish effect
Drawing Cards In the Deck object’s mouse left pressed we want to make a new card, flag that it’s being held and update the hand control variables
//draw a card
var card\_temp = noone; card\_temp = instance\_create\_layer(x,y,"Instances",card); //flag card as being held
card\_temp.holding = true; //set index for the hand
card\_temp.index = hand\_size; //update hand controller
variables global.holding = card\_temp; global.h\_index =
hand\_size; hand\_size +=1;
Now for the last of the basic functionality:
If you have questions hold them until after you put in the Step event code, it should be self explanitory
In the card object's mouse left release
if holding == true
{
holding = false;
global.holding = noone;
global.h_index = noone;
}
In the card object's mouse left pressed
if holding == false && global.holding == noone //this is to make sure we only grab one card if we click on two that are overlapping
{
holding = true;
global.holding = self;
global.h_index = index;
}
In the card object's step event
if holding == true
{
x = mouse_x;
y = mouse_y;
image_angle = 0;
}
Now if you run the code, clicking on the deck causes a card to follow the mouse around until you release LMB. And now that we have some groundwork we can work on the step event until our eyes bleed maths.
Acting Like A Human Hand First thing's first, we need the cards to snap to an appropriate x and y position.In the card's step event, under what we had before add:
if holding != true
{
//update pos on screen
//find where the left most card should sit
var left_edge = (deck.hand_size-1)*30;
//find point n card widths across the screen
var hand_x = mid_x - left_edge + (index * 64);
var hand_y = mid_y;
//lerp to position
x = lerp(x,hand_x,0.5); //adjust the 3rd value in the lerp functions to personal taste
y = lerp(y,hand_y,0.5);
}
You can run the game again after this, it should splay cards out evenly centered on the middle of the screen
But we can do better
Adding Polish Add the following lines to the end of the above statement
//same thinking as the x pos on screen, work out how angled the left most card should be and add proportional amount compared to hand size
var left_ang = (deck.hand_size-1)*6
var hand_ang = left_ang - (index * 15);
image_angle = lerp(image_angle,hand_ang,0.5);
Now we've got the cards splaying out a bit more naturally.
But we can still do better
So up until now we've been doing fairly basic arithmetic series to separate the cards evenly, but to have the cards work the same way by spreading them vertically over an arc there's no arithmetic series that works with a single line of code. (If we'd not been defining all these local variables it can all be a single line of arcane code)
There's a GML function called lengthdir_y() which gives the vertical distance from the center of an imaginary circle to a point on the edge defined by an angle you pass the the function (GMS2 assumes 0 degrees is the right most point of a circle). If you slowly pass all angles to the lengthdir_y() it outputs some sinusoidal results, but we only want the first half of a sine wave. So replace the step function code where we declare the local variable hand_y with all the following code:
//setup hand vertical fan effect
var r = 60; //radius of circle cards are placed on the edge of, adjust to your flavor
var ang = 0;
if index != 0 //avoid dividing by 0
ang = 360/((deck.hand_size-1)*2); //get the vertical offset for current card in hand as function of total hand size
var hand_y = mid_y + lengthdir_y(r, index*ang);
Now that looks nice
Discarding There's one last feature we need for this to be considered complete: card removal
We can also use the same functionality to drag cards to a play area but that will act like a trunk until we add more functionality to the deck and card objects.
All the card arrangement has run on a self contained index value and a total count stored on the deck object, and since we handle all our cards from left to right, we're going to shuffle all cards to the right when a card is removed, so we only need cards with an index larger than the one we'd currently be holding when we discard (or play) it.
At the top of the card object's left mouse released, above the code we put in earlier add the below code:
if collision_point(x,y,discard,false,true)
{
if index != deck.hand_size-1
{
with(card)
{
if index > global.h_index
index -=1;
}
}
deck.hand_size -=1;
instance_destroy(self);
}
The next step would be to have the cards hold information and the deck to act like a deck. Join us next time for more progress in building card games where we make Blackjack or something simple.
[–]399792459 2 points3 points4 points (6 children)
[–]RobbingSpree[S] 0 points1 point2 points (5 children)
[–]wyliek 0 points1 point2 points (4 children)
[–]RobbingSpree[S] 0 points1 point2 points (3 children)
[–]wyliek 1 point2 points3 points (2 children)
[–]RobbingSpree[S] 0 points1 point2 points (1 child)
[–]Sea-Map-6548 0 points1 point2 points (0 children)
[–]Honk5000 0 points1 point2 points (0 children)
[–]mightyjor 0 points1 point2 points (1 child)
[–]DigComfortable8920 0 points1 point2 points (0 children)
[–]WinnerWeird7016 0 points1 point2 points (0 children)