Card Framework
by chunuiyu
Card Framework
Card Framework is a lightweight, extensible toolkit for creating 2D card games in the Godot Engine. Whether you're building a classic Solitaire, a TCG (Trading Card Game), or a deck-building roguelike, the Card Framework provides flexible card handling and UI structures to speed up development. Use this framework as a starting point for card-based gameplay in any 2D project.
Features
- Card Creation & Management: Easily define and instantiate cards with custom attributes or visuals.
- Drag-and-Drop Interactions: Built-in 2D control nodes to handle common card movements.
- Card Container: Create and manage various modules like Piles or Hands, enabling flexible card organization in different game scenarios.
- Scalable Architecture: Extend or modify the base classes to suit various genres (Solitaire, TCG, etc.).
- Lightweight & Modular: Include only the parts you need, so it won't bloat your project.
Table of Contents
- Card Framework
Installation
- Download from Godot Editor’s AssetLib
- Open Godot and navigate to the AssetLib tab.
- Search for Card Framework and download the latest version.
- Manual Download to
addons/card-framework
- Alternatively, download the latest version directly.
- Copy or move the contents to your project under
res://addons/card-framework
.
- Check Usage Examples
- The folders
example1
andfreecell
demonstrate usage in real scenarios. - If you don’t need them, you can remove those folders from your project.
- The folders
Getting Started
- Instantiate the Card Manager
- In any scene that needs card functionality, instantiate the scene at
card-framework/card_manager.tscn
.
- In any scene that needs card functionality, instantiate the scene at
- Assign a CardFactory
- Under the
CardManager
, assign theCardFactory
class to use for card creation. - By default, the framework provides a
JsonCardFactory
that loads card data from JSON files (recommended for most use cases). - If you need custom card creation logic (e.g., loading from a database, procedural generation, or supporting non-JSON formats), you can create your own factory by extending the base
CardFactory
class and assign it here. - See the CardFactory section for details on implementing a custom factory.
- Under the
Note:
Steps 3, 4, and 5 below apply only if you are usingJsonCardFactory
.
- Organize Card Images
- Save the images for your card fronts (and other card-related art) inside the designated
card_asset_dir
folder.
- Save the images for your card fronts (and other card-related art) inside the designated
- Prepare Card Metadata
- Create JSON files that describe each card’s metadata (e.g., name, rank, suit, custom properties), and place them into the
card_info_dir
folder. See example
- Create JSON files that describe each card’s metadata (e.g., name, rank, suit, custom properties), and place them into the
- Set Up the JsonCardFactory
- In the Inspector for your
CardManager
node, configure:default_card_scene
: The default card scene.card_asset_dir
: The folder containing your card images.card_info_dir
: The folder containing your JSON metadata.back_image
: The texture to use for the card’s backside.
- In the Inspector for your
- Set Up the CardManager
- In the Inspector for your
CardManager
node, configure:card_size
: The default width/height for each card.
- In the Inspector for your
- Add Card Containers
- Within
CardManager
, instantiate and arrangePile
,Hand
, or any customCardContainer
nodes you’ve created. - Use these containers to organize the deck, discard piles, player hands, or any other card layout required by your game.
- Within
Classes
CardManager
The Root Node for the Card Framework.
- Oversees all card-related nodes, manages card factories, and coordinates card creation, movement, and container relationships.
Properties
Type | Name | Default | Description |
---|---|---|---|
Vector2 | card_size |
(150, 210) | The default size (width × height) for each card. |
String | card_asset_dir |
null | Directory containing the card image assets. (Required) |
String | card_info_dir |
null | Directory containing JSON files for card information. (Required) |
Texture2D | back_image |
null | The texture used for the backside of all cards. (Required) |
PackedScene | card_factory_scene |
null | The scene responsible for spawning new card objects. (Required) |
Methods
Method Signature | Description |
---|---|
func undo() -> void | Reverts the last recorded move in the history. |
func reset_history() -> void | Clears all move records from the history. |
Card
A Node representing a single playing card.
- Stores information about name, card image, or custom data.
Properties
Type | Name | Default | Description |
---|---|---|---|
String | card_name |
null | The name of the card. |
Vector2 | card_size |
(150, 210) | The width/height of the card. |
Texture2D | front_image |
null | The texture used for the card’s front face. |
Texture2D | back_image |
null | The texture used for the card’s back face. |
bool | show_front |
true | Determines whether the front face is shown (true ) or the back face (false ). |
int | moving_speed |
2000 | The speed at which the card moves during animations or transitions. |
bool | can_be_interacted_with |
true | Whether the card can be interacted with (e.g., clicked, dragged). |
int | hover_distance |
10 | How many pixels the card hovers above its position when interacted with (e.g., picking up a card). |
Methods
Method Signature | Description |
---|---|
func set_faces(front_face: Texture2D, back_face: Texture2D) -> void | Sets the card’s front and back textures (front_face_texture and back_face_texture ). |
func return_card() -> void | Return card to stored destination . |
func move(target_destination: Vector2, degree: float) -> void | Moves the card to target_destination at a given rotation angle (degree ). |
func start_hovering() -> void | Initiates a hover effect (raising the card visually). |
func end_hovering(restore_card_position: bool) -> void | Ends the hover effect. |
func set_holding() -> void | Marks the card as holding. |
func set_releasing() -> void | Unmarks the card as holding. |
func get_string() -> String | Returns card_name as a string representation of the card. |
CardFactory
A Class responsible for creating cards.
- Instanced by the
CardManager
to spawnCard
nodes.
Methods
Method Signature | Description |
---|---|
func create_card(card_name: String, target: CardContainer) -> Card | Creates a new Card under the specified CardContainer .
|
func preload_card_data() -> void | Preloads all card data in card_info_dir . Any card not preloaded here will be loaded on-demand when create_card() is called. |
CardContainer
A Node that holds one or more Card
nodes.
- Placed as a child of
CardManager
. - Manages how cards are organized, displayed, and interacted with (e.g., piles, hands).
Properties
Type | Name | Default | Description |
---|---|---|---|
bool | enable_drop_zone |
true | Enables or disables the drop zone functionality. |
Vector2 | sensor_size |
null | The size of the sensor. If not set, it follows the size of the card. |
Vector2 | sensor_position |
null | The position of the sensor. |
Texture | sensor_texture |
null | The texture used for the sensor. |
bool | sensor_visibility |
true | Determines whether the sensor is visible or not. |
Methods
Below is a reference for CardContainer methods you may override when implementing a custom card container. Override these in your subclass to tailor card behavior to your specific game mechanics:
Method Signature | Description |
---|---|
func add_card(card: Card, index: int = -1) -> void | Adds a card to the container. |
func remove_card(card: Card) -> bool | Removes card from the container. Returns true if successful, false if the card was not found. |
func has_card(card: Card) -> bool | Checks if the container currently holds the given card . |
func clear_cards() -> void | Removes all cards from the container. |
func check_card_can_be_dropped(cards: Array) -> bool | Determines if cards can be dropped onto this container (e.g., rules validation). Returns true if allowed. |
func shuffle() -> void | Shuffles the order of cards in the container. |
func move_cards(cards: Array, index: int = -1, with_history: bool = true) -> bool | Moves the specified cards into this container, optionally recording the move in history if with_history is true . It returns true if move successes |
func undo(cards: Array) -> void | Reverses a recorded move (undo) for the specified cards . |
func hold_card(card: Card) -> void | Holds the given card in this container (e.g., marking it as temporarily selected or locked). |
func release_holding_cards() -> void | Releases all currently held cards in this container, returning them to normal state. |
func get_string() -> String | Returns a string representation of the container’s state or contents. |
func on_card_move_done(_card: Card) -> void | Called after a card has finished moving into this container. |
func on_card_pressed(_card: Card) -> void | Called when a card in this container is clicked. |
Pile
A CardContainer implementation for a stack of cards.
- Useful for decks, discard piles, or any form of stacked card structure.
Properties
Type | Name | Default | Description |
---|---|---|---|
float | stack_display_gap |
8 | The distance (in pixels) between each card in the pile. |
int | max_stack_display |
6 | The maximum number of cards to visually display at once; extra cards may be hidden or overlapped. |
bool | card_face_up |
true | Whether cards in the pile are shown face-up (true ) or face-down (false ). |
PileDirection | layout |
PileDirection.UP |
The direction in which cards are stacked: UP , DOWN , LEFT , or RIGHT . |
bool | allow_card_movement |
true | Enables or disables card movement in this pile. |
bool | restrict_to_top_card |
true | If true , only the top card can be moved (Requires allow_card_movement to be true ). |
bool | align_drop_zone_with_top_card |
true | If true , the pile’s drop zone follows the position of the top card (Requires allow_card_movement to be true ). |
Methods
Method Signature | Description |
---|---|
func get_top_cards(n: int) -> Array | Returns an array of up to n cards from the top of the pile. |
Hand
A CardContainer implementation for a player’s hand of cards.
- A Curve resource can be used to control how cards are distributed along an arc. This allows you to create a more natural “fanned” or curved hand appearance rather than a flat line.
Properties
Type | Name | Default | Description |
---|---|---|---|
int | max_hand_size |
10 | The maximum number of cards this hand can hold. |
float | max_hand_spread |
700 | The maximum horizontal spread (in pixels) used when laying out cards in the hand. |
bool | card_face_up |
true | Whether the hand displays cards face up (true ) or face down (false ). |
int | card_hover_distance |
30 | The distance (in pixels) that a card hovers above the hand when interacted with. |
Curve | hand_rotation_curve |
null | Used to adjust the rotation of each card in the hand; works best as a 2-point linear curve (left to right). (Required) |
Curve | hand_vertical_curve |
null | Used to adjust the vertical positioning of each card in the hand; works best as a 3-point ease in/out curve (0→X→0). (Required) |
bool | align_drop_zone_size_with_current_hand_size |
true | Determines whether the drop zone size follows the hand size. (requires enable drop zone true) |
Methods
Method Signature | Description |
---|---|
func get_random_cards(n: int) -> Array | Returns an array of up to n randomly chosen cards from the hand. |
Creating Card Info JSON Files
-
Create a JSON file
In thecard_info_dir
directory, create a JSON file named after your card, e.g.,card_name.json
. -
Required Fields
name
: The card’s identifier in the game.front_image
: The filename (relative tocard_asset_dir
) of the texture used for the card’s front face.
-
Additional Fields
- You can add any custom properties (e.g.,
suit
,value
, or other game-specific data). - To utilize these properties, consider extending the
Card
class and handling the extra data accordingly. Refer to thePlayingCard
node example in thefreecell/card
directory.
- You can add any custom properties (e.g.,
Example
Filename: club_2.json
{
"name": "club_2",
"front_image": "cardClubs2.png",
"suit": "club",
"value": "2"
}
Sample Projects
Example1
A simple demonstration of the Card Framework.
- Run the scene:
res://example1/example1.tscn
- Uses Pile and Hand nodes to create basic deck, hand, and discard piles.
- Each pile showcases different properties, allowing you to explore how these settings affect card behavior.
Freecell
A full FreeCell Game built on top of the Card Framework.
- Run the scene:
res://freecell/scenes/menu/menu.tscn
PlayingCard
: An extendedCard
class for standard playing cards.Foundation
,Tableau
, andFreecell
: CustomCardContainer
subclasses implementing FreeCell’s unique rules.- Notably,
Tableau
supports moving multiple cards at once.
- Notably,
- Main implementation is found under
scenes/main_game/freecell_game
. - Also includes seed-based game generation, statistics, and additional details for a production-ready solitaire-like game.
Contributing
Contributions are welcome! Please follow these steps to contribute:
- Fork the repository.
- Create a new branch for your feature or bugfix.
- Commit your changes with clear and descriptive messages.
- Open a pull request detailing your changes and the problem they solve.
Please ensure your code adheres to the existing style and includes relevant documentation.
License / Credits
Kenney.nl Card Assets
- Path:
res://freecell/assets/images/cards/
,res://example1/assets/images/cards/
- Source: Kenney - Boardgame Pack
- License: CC0 (Creative Commons Zero)
ChatGPT-Generated Spot Images
- Path:
res://freecell/assets/images/spots/
- Description: These spot images were generated with assistance from ChatGPT.
- Usage: They can be used freely within this project.
Thanks To
Changelog
1.0.0 (2025-01-03)
- initial release
1.1.0 (2025-06-02)
- Improved drop zone handling logic.
- Enhanced
Hand
functionality: you can now reorder cards in the hand by dropping. - Refactored
CardFactory
: separatedJsonCardFactory
and madeCardFactory
generic for custom implementations.
1.1.1 (2025-06-06)
- fix a bug that
card_size
doesn't work.
1.1.2 (2025-06-20)
- Fixed a bug where cards in a full
Hand
could not be reordered. - Refactored: Drag and Drop functionality previously in
Card
has been separated intoDraggableObject
, allowing not onlyCard
but any object to inherit and use drag-and-drop features. - Added
accept_type
toDropZone
, making it usable beyond justCardContainer
for broader compatibility. - The
enable_drop_zone
property inCardContainer
now controls not only the creation of the drop zone, but also allows you to enable or disable the drop zone dynamically at runtime.
Download
Support
If you need help or have questions about this plugin, please contact the author.
Contact Author