This page does a slightly deeper dive into how DeckStacker works. I'm trying to be illuminating, here, but not dive toooo deep into everything. You should be able to find more detail into any of these subjects in the API documentation pages.
API Links
DeckStacker.DSCardPool
Card creation is managed through Card Pools (DeckStacker.DSCardPool GameObjects).
Card Pools are provided in all the sample scenes (including the TemplateScene), and are the default way of creating card objects in the scene, cutting down on the amount of instantiation and destruction of card GameObjects.
Let's go through a brief review of the DSCardPool component that you'll need to hook up in your scene.
API Links
DeckStacker.DSCardArt
DeckStacker.DSCardPlacementInDeck
DeckStacker.DSCardPool
DeckStacker.DSStack
Let's review each of the exposed properties, one by one:
Code-wise, do the following:
How a card is grabbed from the pool is an important moment to consider in your game code. DeckStacker manages the card pool, but grabbing a card from the pool with just DeckStacker code will result in a simple copy of the card prefab. Obviously, we want to do so much more, and a card's state will need to be updated as the card is grabbed from the pool. This is where we get into what a card is to your game, and what data needs to be updated as the card is leaving the pool.
Let's examine card pool management from the standpoint of "traditional" playing cards. DeckStacker provides some example code for these kinds of cards.
API Links
DeckStacker.DSCardPool
DeckStacker.DSTraditionalCardSpawner
The basic idea here is pretty straightforward:
Number 1 on the list is handled by the DSCardPool script, attached to a GameObject in the scene. It does the following:
So, in the case of traditional cards, the DeckStacker Card prefab in the Prefabs folder is setup to use traditional card art and data. So we've got a card prefab setup for our needs, here. For a breakdown of the card prefab please refer to the Card Prefab Setup.
Not only does DeckStacker provide traditional card prefabs (complete with bare-bones traditional card art), but DSTraditionalCardSpawner is a static script designed to do the data setup for a traditional card, as well.
Let's take a closer look at the DeckStacker.DSTraditionalCardSpawner script and pick out some interesting methods to help us make some cards.
Here is the code for DeckStacker.DSTraditionalCardSpawner.MakeTraditionalCard:
In more plain language, this method does the following:
The MakeTraditionalJoker method is very similar to MakeTraditionalCard, but with slightly different data handling of the card's values to account for the fact that Jokers are usually the odd-ball of a card deck, and can mean very different things between games. All the other cards have a standardized value progression.
That's all well and good, but we typically think of traditional card decks as having an established set of cards.
So, DeckStacker.DSTraditionalCardSpawner provides a method that sets up a typical card deck with this method:
public static List<DSCard> MakeTraditionalDeck(DSCardPool cardPool_, DSStack targetStack_, bool jokers_, int deckID_ = 0)
This method returns a List<DSCard> of a traditional deck of cards, given the following:
API Links
DeckStacker.DSCard
DeckStacker.DSCardPool
DeckStacker.DSTraditionalCardSpawner
That's a lot of information, so let's break it down to the key takeaways:
Hopefully this clears up an essential part of DeckStacker for you. As always, study the documentation on each of these classes, carefully.
API Links
DeckStacker.DSCardMover
Card movement is managed, using the DSCardMover static class. In that script, there are several lists that are being monitored by DSCardMover's update loops, RunUpdate() and RunLate().
These lists are:
The types of animation that these lists manage can be categorized as:
Let's briefly summarize how each of these styles of animation work, and why you would want to use them.
API Links
DeckStacker.DSCardMover
This is one of the most common ways a card will move.
When a group of cards has been marked for a "deal" (aka moving from one stack to another), the cards are added to the cardsWaitingToBeDealt list, in DSCardMover.
While in the cardsWaitingToBeDealt list, each card is evaluated by the following criteria:
Once the card is out of the cardsWaitingToBeDealt list, and in the cardsInMotion list, the card will be officially added to its destination stack, and the Restack operation will take hold of it, moving it to its proper place in the new stack.
API Links
DeckStacker.DSAction
DeckStacker.DSCard
DeckStacker.DSCardMovementHelper
DeckStacker.DSMoveCardInDirAction
DeckStacker.DSMoveCardToPositionAction
When a card has been added to the cardsInMotion list, the DSCardMover script will lerp the card's position between its starting location (DSCardMovementHelper.startPos) and the new target position (DSCardMovementHelper.endPos).
The most common way this will happen is during a Restack operation for a stack. Restacks are when the stack arranges all of its cards according to its stack type.
DeckStacker also provides DSActions that utilize this behavior, such as the DSMoveCardInDirAction and the DSMoveCardToPositionAction.
If you want more continuous control of a card's position, I would recommend having the card belong to an Undefined stack, or else Restack operations are going to constantly try and move your card(s) to where the stack thinks the card(s) should be.
You can use the DSCardMovementHelper.MoveCard() method to utilize this system, outside of the core DSActions provided in DeckStacker.
Note: This movement is applied to the base Transform of the card (the Transform attached to the same object that has the DSCard component). See Card Prefab Setup for more info.
API Links
DeckStacker.DSCardMovementHelper
DeckStacker.DSCardMover
A card "offset" is when the card maintains its position, but the card visuals are moved or scaled.
When making card games, I often find myself wanting to make small position and/or scale adjustments to a card to add visual emphasis to the card, like when a card is selected by the player. You can use the DSCardMovementHelper.OffsetCard() method to offset a card, which will then add the card to the DSCardMover.cardsOffset list. The DSCardMover script will then iterate through that list, every Update, and move / scale any cards that haven't finished their offset animations.
Note: This movement is applied to the DSCardMovementHelper.offsetParent object in the card prefab. See Card Prefab Setup for more info.
API Links
DeckStacker.DSCard
DeckStacker.DSCardMovementHelper
DeckStacker.DSCardMover
DeckStacker.DSRotateCardAction
Similar to the previous sections, describing moving a card object: Cards are rotated by adding them to the DSCardMover.cardsRotating list, and then the DSCardMover script will iterate through the list and lerp the rotations of the DSCard objects that haven't finished rotating to thier endRotation values.
This bit is primarily used for angling cards for Fan stacks, and/or correcting the rotation of cards being passed between stacks of different rotations.
This can also be used to "tap" cards in a Magic The Gathering style action.
If you want to lock the rotation of a card, and prevent the rest of these systems from rotating it (like when a restack happens), you can utlized the DSCardMovementHelper.lockRotation flag, disabling future card rotations until that flag has been reset.
You can use the DSCardMovementHelper.RotateCard() method to utilize this system, or use the DSRotateCardAction.
Note: This rotation is applied to the base Transform of the card object (DSCard object). See Card Prefab Setup for more info.
API Links
DeckStacker.DSCard
DeckStacker.DSCardMovementHelper
DeckStacker.DSCardMover
DeckStacker.DSScaleCardAction
Similar to the previous sections, describing moving a card object: Cards are scaled by adding them to the DSCardMover.cardsScaling list, and then the DSCardMover script will iterate through the list and lerp the scales of the DSCard objects that haven't finished scaling to thier endScale values.
You can use the DSCardMovementHelper.ScaleCard() method to utilize this system, or use the DSScaleCardAction.
Note: This scale animation is applied to the base Transform of the card object (DSCard object). See Card Prefab Setup for more info.
API Links
DeckStacker.DSCardGrabber
DeckStacker.DSCardMover
This style of movement works slightly different than the other types of movement.
A "grab" is intended to be a player-controlled, dynamic input that will move the card visuals.
Similar to a card "offset", a card grab does not move the base position of a card, but moves a DSCardGrabber.grabParent, instead.
It still uses a list in DSCardMover (cardsGrabbed) where cards that are marked as "grabbed" are then added to this list, and DSCardMover iterates through the list to update positions.
The ideal use cases for card grabs are when a player clicks and drags on a card to signal some kind of intention or action the player wants to take.
Example: A player grabs a card from their hand, dragging it to the center of the screen which then signals that they want to activate the card's abilities.
Note: This grab movement is applied to the DSCardGrabber.grabParent object. See Card Prefab Setup for more info.
API Links
DeckStacker.DSCardFlipper
Simply put: Card flipping is a fundamental need for a card game. If you're not flipping cards, then what are you doing with your life? jk
Each card has a DSCardFlipper attached to it that drives the card flipping animation for its card.
Let's examine some of the essential parts of this script to understand how flip animations work...
API Links
DeckStacker.DSCardFacing
DeckStacker.DSCardFlipper
In DSCardFlipper, there is a DSCardFacing enum saved that tells the card which side it should be showing.
When the DSCardFacing value doesn't match the object's state (ie When the card is showing it's back face, when the enum is DSCardFacing.Up), the appropriate card flip animation starts playing, bringing the card's state and the current DSCardFacing enum into alignment.
API Links
DeckStacker.DSCard
DeckStacker.DSCardFlipper
DeckStacker.DSCardMovementSettingsSO
DeckStacker.DSCardPool
This string is used as a key for searching what rotation lerp-curves the flip animation should use.
These curves are saved in the DSCardMovementSettingsSO Scriptable Object that you'll need for your project.
Why AnimationCurves instead of Animator Controllers for each card?
Default lerp-curves are provided in the DeckStacker Movement Settings asset:
DeckStacker/DSCore/Resources/GlobalData/DeckStacker Movement Settings.asset
This key is saved on a prefab-by-prefab basis, and is accessed through the DSCard.cardPool property for consistency. (See DSCardPool for more info)
API Links
DeckStacker.DSCardFlipper
Simply rotating the Y axis to achieve a card flip is pretty boring, so why not put a little more PAZAZZ on it!?
In the above image, you can see that the flip animation is broken up into individual axes (X, Y, Z), and that the X and Z curves are somewhat symetrical while the Y starts at 0 and ends at 1. This is because...
Obviously, your mileage may vary, and you may want a different rotation setup for X and Z axis curves to fit your aesthetic.
Just be mindful that the X and Z curves are aesthetic, while the Y curve has a function.
More info on card flipping can be found on the DSCardFlipper page.
API Links
DeckStacker.DSCardArt
DeckStacker.DSCardPool
DeckStacker provides a DSCardArt script that updates card art, based on what a traditional western-style card needs:
By default, card art is updated after the card is released from its corresponding DSCardPool.
Obviously, unless you are making a card game consisting of traditional western-style cards, you'll want to remake this script to support the needs of your game.
More info on DSCardArt can be found on its page.
Conceptually, any place that gathers and arranges cards, as a group, is what DeckStacker calls a "stack".
Stacks represent a group of cards in a place.
A stack can be a pile (deck), a row, a column, or a fan of cards.
An "undefined" stack does not arrange cards, and is ideal for when cards are not gathered into a group, and generally "float" around the table.
API Links
DeckStacker.DSCardMover
DeckStacker.DSRestacker
DeckStacker.DSRestackHelper
DeckStacker.DSStack
When a stack moves its cards into a particular layout, it is called a restack.
API Links
DeckStacker.DSRestackHelper
Each stack type has a default direction that it will position its cards.
Note that the DSRestackHelper of a stack has a "Reverse Direction" flag. This will reverse the direction of the stack.
API Links
DeckStacker.DSCard
DeckStacker.DSCardSpacing
DeckStacker.DSCardPool
DeckStacker.DSRestackHelper
Card spacing determines how far the cards are spaced apart in a restack.
The edge of the card that is being measured from is different, between stack types.
Note: If Reverse Direction is enabled in DSRestackHelper, the above edge directions are flipped, as well.
Card spacing settings can be found in the Scriptable Object:
DeckStacker/DSCore/Resources/GlobalData/DeckStacker Card Spacing.asset
How does a stack know what DSCardSpacing to use?:
That sounds complicated, but just make sure your Card prefab's cardSpacingKey matches with a setting in "DeckStacker Card Spacing" asset.
API Links
DeckStacker.DSCardSpacingOverrideSettings
DeckStacker.DSRestackHelper
In a stack's DSRestackHelper, there are card spacing overrides in the form of DSCardSpacingOverrideSettings.
This will override the spacing settings of any card that is put into it, universally.
This subject deserves its own page. See DSAction Guidelines and Use