Results 1 to 7 of 7

Thread: Cards Tutorial

  1. #1
    Legendary Member cairnswm's Avatar
    Join Date
    Nov 2002
    Location
    Randburg, South Africa
    Posts
    1,537

    Cards Tutorial

    Cards Tutorial



    0. Why Cards?
    1. Using TomCards
    1.1 Drawing a Card
    1.2 Drawing the Deck
    2. Creating a Deck
    2.1 Shuffling a deck
    2.2 Creating a Deck Class
    3. Lets make Blackjack
    3.1 Storing the Displayed Cards
    3.2 Dealing Cards
    4. Deal Animation
    4.1 Creating the Timer
    4.2 Drawing the Card



    0. Why Cards?

    If you read on the internet about successful Independant Game Developers a lot
    are creating card and puzzle games. My goal is to make it possible for us to
    easily create card games that in the future will allow us to become successful
    Indy developers.



    1. Using TomCards

    A long time ago I downloaded a Card component called TomCards. I have not been
    able to find any updates on this component. As its an easy place to start I
    thought I'd use this component as a basis for creating card games.

    Download TomCards from http\\www.cairnsgames.co.za\cardtut\tomcards.zip

    1.1 Drawing a Card

    The easiest way to draw a card is to place a Card component on your form. Just
    set the various properties to display the card you want.

    The problem with placing the card as a component on the form is that you need
    to place all the cards you need up front. Often this is rather impracticle. So
    lets look at how to dynamically create a card.

    [pascal]
    // For this example you need a Form with a single button on it. In the example
    // the form is Form1 and the button btnMakeCard.
    // Make sure TomCard is in your forms uses clause

    procedure TForm1.btnMakeCardClick(Sender: TObject);
    Var
    Card : TCards;
    begin
    Card := TCards.Create(Self);
    Card.Parent := Self;
    Card.Left := 100;
    Card.Top := 100;
    Card.Suit := csHearts;
    Card.Value := 9;
    end;

    [/pascal]



    If you run this program and press the Make Card button a 9 of hearts will be
    drawn on the screen.

    1.2 Drawing the deck

    To draw a deck of card we typically only need to display a single card that is
    placed face down. TomCards component has a property called State that defines
    how a card should be drawn. By default a card is drawn Face up (ctFront) by
    setting this property to ctBack the card back is drawn. The default card back
    is a plain dreary hatch pattern. To spice it up the CardBackStyle property
    should be changed. The valid values are from 1 to 13.

    [pascal]
    // For this example add an additional button to the form. Call the button
    // MakeDeck.
    procedure TForm1.btnMakeDeckClick(Sender: TObject);
    Var
    Card : TCards;
    begin
    Card := TCards.Create(Self);
    Card.Parent := Self;
    Card.Left := 100;
    Card.Top := 200;
    Card.State := ctBack;
    Card.CardBackStyle := 7;
    end;
    [/pascal]



    My favorite CardBackStyle is definitly number 7.

    TomCards does not declare constants for the various CardBackStyles so it is
    mostly trial and error to find what you like.

    Of course in any card game we create we would want to allow the person playing
    the game to change the card back to whatever pattern they like.

    The tutorial to this point is dowloadable as Cards1 from http://www.cairnsgames.co.za/cardtut/cards1.zip

    2. Creating a Deck

    Nearly all card games have a completely shuffled deck of cards to start with.
    For any game we create we are going to have to store the order of the cards and
    show them to the player in the order.

    There are many ways of creating a data-structure to store the deck. For me the
    easiest way is to create an array.

    [pascal]
    // First we declare a record to store the card relevant data in
    Type
    TCardDetail = Record
    CardSuit : TCardSuit;
    CardValue : Integer;
    End;
    // Now declare the deck to store the cards
    TDeck = Array of TCardDetail;
    [/pascal]

    Notice that I have not created an upper and lower limit for the Deck. This is to
    allow for different size decks, e.g. Decks that include or exclude Jokers.

    For now I'm just going to populate the deck with a standard set of cards. For
    this example I have extended the previous example. I have created a new public
    variable for the form:

    [pascal]
    public
    { Public declarations }
    Deck : TDeck;
    Card : TCards;
    CardNo : Integer; // Store the current card in the deck
    [/pascal]

    Then in the forms OnCreate event handler:

    [pascal]
    procedure TForm1.FormCreate(Sender: TObject);
    Var
    I : Integer;
    begin
    // Dynamic arrays need their length set before they can be accessed
    SetLength(Deck, 52);
    For I := 0 to 51 do
    Begin
    Case I div 13 of // div by 13 gives a 0-3 result
    0: Deck[I].CardSuit := csClubs;
    1: Deck[I].CardSuit := csDiamonds;
    2: Deck[I].CardSuit := csHearts;
    3: Deck[I].CardSuit := csSpades;
    End;
    Deck[I].CardValue := (I mod 13) + 1; // populates the card value
    End;
    end;
    [/pascal]

    Just to display the various cards I changed the Make Card button to just
    display the next card each time.

    [pascal]
    procedure TForm1.btnMakeCardClick(Sender: TObject);
    begin
    // First check if the Card display has been created or not
    If Not Assigned(Card) then
    Begin
    Card := TCards.Create(Self);
    Card.Parent := Self;
    Card.Left := 100;
    Card.Top := 100;
    End;
    Card.Suit := Deck[CardNo].CardSuit;
    Card.Value := Deck[CardNo].CardValue;
    CardNo := CardNo + 1;
    end;
    [/pascal]

    When running this program you will see that as you press the Make Card button
    the card steps through the deck in card order.

    2.1 Suffling a deck

    Most card games use a randomly sorted deck of cards. So at some point we should
    randomize the order of the cards in the deck. If you search the internet you
    will see that there are multiple shuffling algorithms. As we dont have that many
    cards in our deck we can use a simple card swapping routine to shuffle the
    deck.

    For each card in the deck randomly select another card and switch them. This
    garantees that every card is moved at least once in a shuffle. By repeating the
    shuffle multiple times we can ensure that the deck is properly randomized.

    [pascal]
    procedure TForm1.ShuffleDeck(var Deck: TDeck; Times: Integer);
    Var
    J,I : Integer;
    R : Integer;
    begin
    For J := 1 to Times do
    For I := Low(Deck) to High(Deck) do
    Begin
    R := -1;
    Repeat
    R := Trunc(Random(High(Deck)+1)) + Low(Deck);
    Until R <> I;
    SwapCard(Deck[I],Deck[R]);
    End;
    end;
    [/pascal]

    I've created a little function called SwapCard that exchanges all the fields
    within two TCardDetail variables:

    [pascal]
    // Swapcard is created so that additional fields can be added to the TCardDetail
    // record type without having to modify this function.
    procedure TForm1.SwapCard(var Card1, Card2: TCardDetail);
    Var
    TempCard : TCardDetail;
    begin
    Move(Card1,TempCard,SizeOf(TCardDetail));
    Move(Card2,Card1,SizeOf(TCardDetail));
    Move(TempCard,Card2,SizeOf(TCardDetail));
    end;
    [/pascal]

    2.2 Creating a Deck Class

    Wouldn't it be nice if we could create a class that manages all the function for
    a deck.

    The class would need to manage the Deck including populating the cards,
    shuffling and returning cards as you need them.

    [pascal]
    // An example deck class might look something like this
    Type
    TDeck = Class
    private
    Cards : Array of TCardDetail;
    CardNo : Integer;
    Procedure SwapCards(var Card1,Card2 : TCardDetail);
    published
    Property NumberOfCards : Integer;
    Procedure Shuffle(Times : Integer);
    Function NextCard : TCardDetail;
    End;
    [/pascal]

    I'm not going to go into the implementation of the class as the TDeck type we
    have already declared will be sufficient for most of what we need. I'll possibly
    do an implementation of it later.

    The tutorial to this point is dowloadable as Cards2. http://www.cairnsgames.co.za/cardtut/cards2.zip

    3. Lets make Blackjack

    Ok so now we know how to show and manipulate cards. Lets actually try and make a
    simple game using what we have learnt.

    Blackjack also known as 21 is a relativly simple game. The goal is to try and
    get a score as close to 21 as possible but going over 21 means that you are bust
    and out of the game, if betting is involved being bust immediatly loses your
    stake. An Ace is worth 1 or 11 points and all face cards (Jack, Queen, King) are
    worth 10 point.

    For this example I am going to use the structure that we built before. I'm going
    to build a small part of the game showing how easy it is to build the
    functionality of a game.

    3.1 Storing the Displayed Cards

    As discussed before it would be possible to place a number of card components
    on the form. But as it would not be possible to know how many cards the player
    has got this solution isn't really viable. My choice would be to use a TList to
    store a link to each card dealt.

    For this example I have deleted everything from the example program except for
    the OnCreate, ShuffleDeck and SwapCard functions. I also deleted the two buttons
    we had had. I added a TCards component to the form to represent the deck of
    cards. and Two labels with their Font size set to 42.



    3.2 Dealing Cards

    When the DeckOfCards is clicked we want to create the next dealt card for the
    player. These cards are displayed next to one another at the bottom of the form.
    Rememeber also that we need to increment the current CardNo being dealt so that
    the same card does not get dealt time and again.

    [pascal]
    procedure TForm1.DeckOfCardsClick(Sender: TObject);
    Begin
    Card := TCards.Create(Self);
    Card.Parent := Self;
    Card.Left := 150+(100*PlayerCards.Count);
    Card.Top := 300;
    Card.Suit := Deck[CardNo].CardSuit;
    Card.Value := Deck[CardNo].CardValue;
    CardNo := CardNo + 1;
    PlayerCards.Add(Card);
    Score := Score + CardScore[Card.Value];
    end;
    [/pascal]

    As can be seen in the example I'm incrementing a Score property with the values
    in CardScore. I've created a constant that represents the various values of
    each card.

    [pascal]
    Const
    CardScore : Array[1..13] of Integer = (11,2,3,4,5,6,7,8,9,10,10,10,10);
    [/pascal]

    The Array index represents the various values stored in the Value property of a
    TCards component. Currently I've forced all Aces to be worth 11 points.

    Properties are something that I don't see used nearly enough. Properties allow
    all sorts of 'automated' functionality. In the following example I'm using a
    property to basically control the whole state of the game.

    [pascal]
    private
    FScore: Integer;
    procedure SetScore(const Value: Integer);
    public
    Property Score : Integer read FScore write SetScore;

    ....

    procedure TForm1.SetScore(const Value: Integer);
    begin
    FScore := Value;
    lScore.Caption := IntToStr(Score);
    If FScore > 21 then
    lBust.Visible := True;
    If FScore = 0 then
    lBust.Visible := False;
    end;
    [/pascal]

    On the lBust label I have created an event that restarts the game. As you can
    only click on the label when it is visible this means that you can only click
    the label after you are bust - ie the game is over.

    The tutorial to this point is dowloadable as Cards3. http://www.cairnsgames.co.za/cardtut/cards3.zip

    4. Deal Animation

    Lots of card games show the card flying off the deck and ending at the location
    the card needs to be. This sort of animation is also used when moving cards from
    one stack to another.

    4.1 Creating the Timer

    The easiest way to animate a card is to implement a timer and a timing control
    class. The Timing control class stores information about the position of the
    card while it is being flown to the new location.

    [pascal]
    // TFlyCard stores the information for a moving card. This can be used whenever
    // you want to automate a cards motion.
    // I'd also suggest an event for when the card reaches its destination - this
    // would allow you to fly a face down card and turn it face up on reaching its
    // destination.
    TFlyCard = Class
    DestLeft, DestTop : Integer;
    Speed : Integer;
    Card : TCards;
    End;
    [/pascal]

    Basic trigonometry is needed to calculate how the card needs to move in each
    timer event. By using the theorem of pythagorus we can word out how far a card
    needs to move in each direction to move straight to its destination.

    [pascal]
    procedure TForm1.Timer1Timer(Sender: TObject);
    Var
    Fly : TFlyCard;
    DX,DY,DirX,DirY : Integer;
    DH : Single;
    I : Integer;
    begin
    If FlyingCards.Count = 0 then
    Exit;
    For I := FlyingCards.Count - 1 downto 0 do
    Begin
    Fly := TFlyCard(FlyingCards[I]);
    DX := ABS(Fly.Card.Left-Fly.DestLeft);
    DY := ABS(Fly.Card.Top-Fly.DestTop);
    // Calculate the distance of the hypotenuse
    DH := SQRT(DX*DX+DY*DY);
    If DH < 1 then DH := 1;
    // Move the card
    If Fly.Card.Top-Fly.DestTop > 0 then
    DirY := Trunc((DY/DH) * -Fly.Speed)
    else
    DirY := Trunc((DY/DH) * Fly.Speed);
    If Fly.Card.Left-Fly.DestLeft > 0 then
    DirX := Trunc((DX/DH) * -Fly.Speed)
    Else
    DirX := Trunc((DX/DH) * Fly.Speed);
    If (ABS(DirX) >= ABS(DX)) and
    (ABS(DirY) >= ABS(DY)) then
    Begin
    // If reached destination then ensure that Timing Control Class is
    // removed.
    Fly.Card.Top := Fly.DestTop;
    Fly.Card.Left := Fly.DestLeft;
    FlyingCards.Delete(I);
    End
    Else
    Begin
    Fly.Card.Top := Fly.Card.Top + DirY;
    Fly.Card.Left := Fly.Card.Left + DirX;
    End;
    End;
    end;
    [/pascal]

    Obviously a FlyCard can be manually insert into the FlyingCards list but I
    prefer doing it in a generic function.

    [pascal]
    procedure TForm1.MoveCardTo(Card: TCards; Speed, DLeft, DTop: Integer);
    Var
    Fly : TFlyCard;
    begin
    Fly := TFlyCard.Create;
    Fly.Card := Card;
    Fly.DestLeft := DLeft;
    Fly.DestTop := DTop;
    Fly.Speed := Speed;
    FlyingCards.Add(Fly);
    end;
    [/pascal]

    My best results come when setting a timer interval of 1 and a speed of 15.

    By using animations like this moving the cards to the various areas gives the
    game a bit of class.

    4.2 Drawing the Card

    The real beauty of the Delphi VCL is often you don't have to worry about doing
    certain actions. As the Parent for the Card has been set as the cards position
    changes it is automatically draw in the new location.



    The tutorial to this point is dowloadable as Cards4. http://www.cairnsgames.co.za/cardtut/cards4.zip
    William Cairns
    My Games: http://www.cairnsgames.co.za (Currently very inactive)
    MyOnline Games: http://TheGameDeveloper.co.za (Currently very inactive)

  2. #2
    iLLUNis
    Guest

    Cards Tutorial

    nice.......your images though and the ZIP file will not work since i think there is a mistake with your Backslashes and Forward Slashes... :? ....

  3. #3
    Legendary Member cairnswm's Avatar
    Join Date
    Nov 2002
    Location
    Randburg, South Africa
    Posts
    1,537

    Cards Tutorial

    I've tried everything I can to get the images and URLs to work - back, forward and everything else :x

    :scratch:
    William Cairns
    My Games: http://www.cairnsgames.co.za (Currently very inactive)
    MyOnline Games: http://TheGameDeveloper.co.za (Currently very inactive)

  4. #4
    iLLUNis
    Guest

    Cards Tutorial

    hmmm...you are right.....i wonder if it has anything to do with the images being .GIF format.... :scratch: ....because i never has problems with the .JPEG images.....MYSTERY :batman:

  5. #5

    Cards Tutorial

    mabey because he has spaces after .gif and before [/ ?

  6. #6

    Re: Cards Tutorial

    Fixed the links in the quote:
    Quote Originally Posted by cairnswm
    Cards Tutorial





    Code:
    0. Why Cards?
    1. Using TomCards
       1.1 Drawing a Card
       1.2 Drawing the Deck
    2. Creating a Deck
       2.1 Shuffling a deck
       2.2 Creating a Deck Class
    3. Lets make Blackjack
       3.1 Storing the Displayed Cards
       3.2 Dealing Cards
    4. Deal Animation
       4.1 Creating the Timer
       4.2 Drawing the Card


    0. Why Cards?

    If you read on the internet about successful Independant Game Developers a lot
    are creating card and puzzle games. My goal is to make it possible for us to
    easily create card games that in the future will allow us to become successful
    Indy developers.



    1. Using TomCards

    A long time ago I downloaded a Card component called TomCards. I have not been
    able to find any updates on this component. As its an easy place to start I
    thought I'd use this component as a basis for creating card games.

    Download TomCards from http://www.cairnsgames.co.za/cardtut/tomcards.zip

    1.1 Drawing a Card

    The easiest way to draw a card is to place a Card component on your form. Just
    set the various properties to display the card you want.

    The problem with placing the card as a component on the form is that you need
    to place all the cards you need up front. Often this is rather impracticle. So
    lets look at how to dynamically create a card.

    [pascal]
    // For this example you need a Form with a single button on it. In the example
    // the form is Form1 and the button btnMakeCard.
    // Make sure TomCard is in your forms uses clause

    procedure TForm1.btnMakeCardClick(Sender: TObject);
    Var
    Card : TCards;
    begin
    Card := TCards.Create(Self);
    Card.Parent := Self;
    Card.Left := 100;
    Card.Top := 100;
    Card.Suit := csHearts;
    Card.Value := 9;
    end;

    [/pascal]



    If you run this program and press the Make Card button a 9 of hearts will be
    drawn on the screen.

    1.2 Drawing the deck

    To draw a deck of card we typically only need to display a single card that is
    placed face down. TomCards component has a property called State that defines
    how a card should be drawn. By default a card is drawn Face up (ctFront) by
    setting this property to ctBack the card back is drawn. The default card back
    is a plain dreary hatch pattern. To spice it up the CardBackStyle property
    should be changed. The valid values are from 1 to 13.

    [pascal]
    // For this example add an additional button to the form. Call the button
    // MakeDeck.
    procedure TForm1.btnMakeDeckClick(Sender: TObject);
    Var
    Card : TCards;
    begin
    Card := TCards.Create(Self);
    Card.Parent := Self;
    Card.Left := 100;
    Card.Top := 200;
    Card.State := ctBack;
    Card.CardBackStyle := 7;
    end;
    [/pascal]



    My favorite CardBackStyle is definitly number 7.

    TomCards does not declare constants for the various CardBackStyles so it is
    mostly trial and error to find what you like.

    Of course in any card game we create we would want to allow the person playing
    the game to change the card back to whatever pattern they like.

    The tutorial to this point is dowloadable as Cards1 from http://www.cairnsgames.co.za/cardtut/cards1.zip

    2. Creating a Deck

    Nearly all card games have a completely shuffled deck of cards to start with.
    For any game we create we are going to have to store the order of the cards and
    show them to the player in the order.

    There are many ways of creating a data-structure to store the deck. For me the
    easiest way is to create an array.

    [pascal]
    // First we declare a record to store the card relevant data in
    Type
    TCardDetail = Record
    CardSuit : TCardSuit;
    CardValue : Integer;
    End;
    // Now declare the deck to store the cards
    TDeck = Array of TCardDetail;
    [/pascal]

    Notice that I have not created an upper and lower limit for the Deck. This is to
    allow for different size decks, e.g. Decks that include or exclude Jokers.

    For now I'm just going to populate the deck with a standard set of cards. For
    this example I have extended the previous example. I have created a new public
    variable for the form:

    [pascal]
    public
    { Public declarations }
    Deck : TDeck;
    Card : TCards;
    CardNo : Integer; // Store the current card in the deck
    [/pascal]

    Then in the forms OnCreate event handler:

    [pascal]
    procedure TForm1.FormCreate(Sender: TObject);
    Var
    I : Integer;
    begin
    // Dynamic arrays need their length set before they can be accessed
    SetLength(Deck, 52);
    For I := 0 to 51 do
    Begin
    Case I div 13 of // div by 13 gives a 0-3 result
    0: Deck[I].CardSuit := csClubs;
    1: Deck[I].CardSuit := csDiamonds;
    2: Deck[I].CardSuit := csHearts;
    3: Deck[I].CardSuit := csSpades;
    End;
    Deck[I].CardValue := (I mod 13) + 1; // populates the card value
    End;
    end;
    [/pascal]

    Just to display the various cards I changed the Make Card button to just
    display the next card each time.

    [pascal]
    procedure TForm1.btnMakeCardClick(Sender: TObject);
    begin
    // First check if the Card display has been created or not
    If Not Assigned(Card) then
    Begin
    Card := TCards.Create(Self);
    Card.Parent := Self;
    Card.Left := 100;
    Card.Top := 100;
    End;
    Card.Suit := Deck[CardNo].CardSuit;
    Card.Value := Deck[CardNo].CardValue;
    CardNo := CardNo + 1;
    end;
    [/pascal]

    When running this program you will see that as you press the Make Card button
    the card steps through the deck in card order.

    2.1 Suffling a deck

    Most card games use a randomly sorted deck of cards. So at some point we should
    randomize the order of the cards in the deck. If you search the internet you
    will see that there are multiple shuffling algorithms. As we dont have that many
    cards in our deck we can use a simple card swapping routine to shuffle the
    deck.

    For each card in the deck randomly select another card and switch them. This
    garantees that every card is moved at least once in a shuffle. By repeating the
    shuffle multiple times we can ensure that the deck is properly randomized.

    [pascal]
    procedure TForm1.ShuffleDeck(var Deck: TDeck; Times: Integer);
    Var
    J,I : Integer;
    R : Integer;
    begin
    For J := 1 to Times do
    For I := Low(Deck) to High(Deck) do
    Begin
    R := -1;
    Repeat
    R := Trunc(Random(High(Deck)+1)) + Low(Deck);
    Until R <> I;
    SwapCard(Deck[I],Deck[R]);
    End;
    end;
    [/pascal]

    I've created a little function called SwapCard that exchanges all the fields
    within two TCardDetail variables:

    [pascal]
    // Swapcard is created so that additional fields can be added to the TCardDetail
    // record type without having to modify this function.
    procedure TForm1.SwapCard(var Card1, Card2: TCardDetail);
    Var
    TempCard : TCardDetail;
    begin
    Move(Card1,TempCard,SizeOf(TCardDetail));
    Move(Card2,Card1,SizeOf(TCardDetail));
    Move(TempCard,Card2,SizeOf(TCardDetail));
    end;
    [/pascal]

    2.2 Creating a Deck Class

    Wouldn't it be nice if we could create a class that manages all the function for
    a deck.

    The class would need to manage the Deck including populating the cards,
    shuffling and returning cards as you need them.

    [pascal]
    // An example deck class might look something like this
    Type
    TDeck = Class
    private
    Cards : Array of TCardDetail;
    CardNo : Integer;
    Procedure SwapCards(var Card1,Card2 : TCardDetail);
    published
    Property NumberOfCards : Integer;
    Procedure Shuffle(Times : Integer);
    Function NextCard : TCardDetail;
    End;
    [/pascal]

    I'm not going to go into the implementation of the class as the TDeck type we
    have already declared will be sufficient for most of what we need. I'll possibly
    do an implementation of it later.

    The tutorial to this point is dowloadable as Cards2. http://www.cairnsgames.co.za/cardtut/cards2.zip

    3. Lets make Blackjack

    Ok so now we know how to show and manipulate cards. Lets actually try and make a
    simple game using what we have learnt.

    Blackjack also known as 21 is a relativly simple game. The goal is to try and
    get a score as close to 21 as possible but going over 21 means that you are bust
    and out of the game, if betting is involved being bust immediatly loses your
    stake. An Ace is worth 1 or 11 points and all face cards (Jack, Queen, King) are
    worth 10 point.

    For this example I am going to use the structure that we built before. I'm going
    to build a small part of the game showing how easy it is to build the
    functionality of a game.

    3.1 Storing the Displayed Cards

    As discussed before it would be possible to place a number of card components
    on the form. But as it would not be possible to know how many cards the player
    has got this solution isn't really viable. My choice would be to use a TList to
    store a ]http://www.cairnsgames.co.za/cardtut/cards3form.gif[/img]

    3.2 Dealing Cards

    When the DeckOfCards is clicked we want to create the next dealt card for the
    player. These cards are displayed next to one another at the bottom of the form.
    Rememeber also that we need to increment the current CardNo being dealt so that
    the same card does not get dealt time and again.

    [pascal]
    procedure TForm1.DeckOfCardsClick(Sender: TObject);
    Begin
    Card := TCards.Create(Self);
    Card.Parent := Self;
    Card.Left := 150+(100*PlayerCards.Count);
    Card.Top := 300;
    Card.Suit := Deck[CardNo].CardSuit;
    Card.Value := Deck[CardNo].CardValue;
    CardNo := CardNo + 1;
    PlayerCards.Add(Card);
    Score := Score + CardScore[Card.Value];
    end;
    [/pascal]

    As can be seen in the example I'm incrementing a Score property with the values
    in CardScore. I've created a constant that represents the various values of
    each card.

    [pascal]
    Const
    CardScore : Array[1..13] of Integer = (11,2,3,4,5,6,7,8,9,10,10,10,10);
    [/pascal]

    The Array index represents the various values stored in the Value property of a
    TCards component. Currently I've forced all Aces to be worth 11 points.

    Properties are something that I don't see used nearly enough. Properties allow
    all sorts of 'automated' functionality. In the following example I'm using a
    property to basically control the whole state of the game.

    [pascal]
    private
    FScore: Integer;
    procedure SetScore(const Value: Integer);
    public
    Property Score : Integer read FScore write SetScore;

    ....

    procedure TForm1.SetScore(const Value: Integer);
    begin
    FScore := Value;
    lScore.Caption := IntToStr(Score);
    If FScore > 21 then
    lBust.Visible := True;
    If FScore = 0 then
    lBust.Visible := False;
    end;
    [/pascal]

    On the lBust label I have created an event that restarts the game. As you can
    only click on the label when it is visible this means that you can only click
    the label after you are bust - ie the game is over.

    The tutorial to this point is dowloadable as Cards3. http://www.cairnsgames.co.za/cardtut/cards3.zip

    4. Deal Animation

    Lots of card games show the card flying off the deck and ending at the location
    the card needs to be. This sort of animation is also used when moving cards from
    one stack to another.

    4.1 Creating the Timer

    The easiest way to animate a card is to implement a timer and a timing control
    class. The Timing control class stores information about the position of the
    card while it is being flown to the new location.

    [pascal]
    // TFlyCard stores the information for a moving card. This can be used whenever
    // you want to automate a cards motion.
    // I'd also suggest an event for when the card reaches its destination - this
    // would allow you to fly a face down card and turn it face up on reaching its
    // destination.
    TFlyCard = Class
    DestLeft, DestTop : Integer;
    Speed : Integer;
    Card : TCards;
    End;
    [/pascal]

    Basic trigonometry is needed to calculate how the card needs to move in each
    timer event. By using the theorem of pythagorus we can word out how far a card
    needs to move in each direction to move straight to its destination.

    [pascal]
    procedure TForm1.Timer1Timer(Sender: TObject);
    Var
    Fly : TFlyCard;
    DX,DY,DirX,DirY : Integer;
    DH : Single;
    I : Integer;
    begin
    If FlyingCards.Count = 0 then
    Exit;
    For I := FlyingCards.Count - 1 downto 0 do
    Begin
    Fly := TFlyCard(FlyingCards[I]);
    DX := ABS(Fly.Card.Left-Fly.DestLeft);
    DY := ABS(Fly.Card.Top-Fly.DestTop);
    // Calculate the distance of the hypotenuse
    DH := SQRT(DX*DX+DY*DY);
    If DH < 1 then DH := 1;
    // Move the card
    If Fly.Card.Top-Fly.DestTop > 0 then
    DirY := Trunc((DY/DH) * -Fly.Speed)
    else
    DirY := Trunc((DY/DH) * Fly.Speed);
    If Fly.Card.Left-Fly.DestLeft > 0 then
    DirX := Trunc((DX/DH) * -Fly.Speed)
    Else
    DirX := Trunc((DX/DH) * Fly.Speed);
    If (ABS(DirX) >= ABS(DX)) and
    (ABS(DirY) >= ABS(DY)) then
    Begin
    // If reached destination then ensure that Timing Control Class is
    // removed.
    Fly.Card.Top := Fly.DestTop;
    Fly.Card.Left := Fly.DestLeft;
    FlyingCards.Delete(I);
    End
    Else
    Begin
    Fly.Card.Top := Fly.Card.Top + DirY;
    Fly.Card.Left := Fly.Card.Left + DirX;
    End;
    End;
    end;
    [/pascal]

    Obviously a FlyCard can be manually insert into the FlyingCards list but I
    prefer doing it in a generic function.

    [pascal]
    procedure TForm1.MoveCardTo(Card: TCards; Speed, DLeft, DTop: Integer);
    Var
    Fly : TFlyCard;
    begin
    Fly := TFlyCard.Create;
    Fly.Card := Card;
    Fly.DestLeft := DLeft;
    Fly.DestTop := DTop;
    Fly.Speed := Speed;
    FlyingCards.Add(Fly);
    end;
    [/pascal]

    My best results come when setting a timer interval of 1 and a speed of 15.

    By using animations like this moving the cards to the various areas gives the
    game a bit of class.

    4.2 Drawing the Card

    The real beauty of the Delphi VCL is often you don't have to worry about doing
    certain actions. As the Parent for the Card has been set as the cards position
    changes it is automatically draw in the new location.



    The tutorial to this point is dowloadable as Cards4. http://www.cairnsgames.co.za/cardtut/cards4.zip

  7. #7

    Cards Tutorial

    Ba-bump. Some of the links (images) for this tutorial ain't showing up. Maybe an edit is in order? I'd make the changes myself but I don't have super mod-all-forums powers like in the evil old days (when I ruled by fear for approximately a week of DGDev, heh!)...
    "All paid jobs absorb and degrade the mind."
    <br />-- Aristotle

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •