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.
- 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.
- Installation
- Getting Started
- Classes
- Creating Card Info JSON Files
- Sample Projects
- Contributing
- License / Credits
- Thanks To
- Changelog
- 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
- 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
- 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 CardManager
- In the Inspector for your
CardManager
node, configure:card_size
: The default width/height for each card.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
- Assign a CardFactory
- Under the
CardManager
, choose theCardFactory
class to use. - You can use the default
CardFactory
or create a custom factory (by extendingCardFactory
) and set it here.
- Under the
- 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
The Root Node for the Card Framework.
- Oversees all card-related nodes, manages card factories, and coordinates card creation, movement, and container relationships.
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) |
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. |
A Node representing a single playing card.
- Stores information about name, card image, or custom data.
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). |
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. |
A Class responsible for creating cards.
- Instanced by the
CardManager
to spawnCard
nodes.
Type | Name | Default | Description |
---|---|---|---|
PackedScene | default_card_scene |
null | A base card scene to instantiate. (Required) |
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. |
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).
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. |
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) -> 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, 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. |
A CardContainer implementation for a stack of cards.
- Useful for decks, discard piles, or any form of stacked card structure.
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 ). |
Method Signature | Description |
---|---|
func get_top_cards(n: int) -> Array | Returns an array of up to n cards from the top of the pile. |
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.
Type | Name | Default | Description |
---|---|---|---|
Vector2 | hand_area |
(0,0) | The area in which cards can be laid out for this hand. |
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) |
Method Signature | Description |
---|---|
func get_random_cards(n: int) -> Array | Returns an array of up to n randomly chosen cards from the hand. |
-
Create a JSON file In the
card_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.,
Filename: club_2.json
{
"name": "club_2",
"front_image": "cardClubs2.png",
"suit": "club",
"value": "2"
}
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.
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.
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.
- Path:
res://freecell/assets/images/cards/
,res://example1/assets/images/cards/
- Source: Kenney - Boardgame Pack
- License: CC0 (Creative Commons Zero)
- 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.
- initial release