Drag and drop
Drag and drop is a key component of any card game. CardEngine relies on Godot own drag and drop implementation, especially the following functions of the Control node:
Most of the complexity is however hidden and and requires minimal actions from you. Containers, cards visual layer and the DropArea widget take care of all the details for you.
Containers can be source for a drag and drop action. To activate this feature you have to make sure drag is enabled for your container, see here for more details. This is all you have to do, however by default the drag action will move the card itself with the mouse cursor. If you want your drag action to have a different visual, you can set a widget for that (see below).
Assigning a drag widget
You define which widget you want to use using the "Drag Widget" property of your container.
To do so, open the container scene file under the
res://containers/<container ID>.tscn and select the root node, you will have access to the property in the Inspector. Load the widget using the property editor menu.
Creating a drag widget
A drag widget must be a control based scene. Create a new scene using the "Scene" menu of the Godot editor and choose "User Interface" as a root node. You can then build your widget using any Control or Node2D based nodes. If you want to customize your widget depending on the card being dragged, attach a new script to the root node. In the
_ready() function you can connect to the
drag_started() signal of the
GeneralManager singleton accessible through
CardEngine.general(). Then you can retrieved the dragged card using the
CardEngine.general().get_dragged_card() function. Here a complete example from the demo:
extends Control onready var _name = $Background/Name func _ready() -> void: CardEngine.general().connect("drag_started", self, "_on_drag_started") func _on_drag_started(): var card = CardEngine.general().get_dragged_card() _name.text = card.data().get_text("name")
DropArea is very simple to use, this is a Control based Node. It can be added in any scene as you would add any UI elements. You can then make your script react to a card being dropped into the area by using the
dropped(card, source, on_card) signal, where:
- card (CardInstance) is the card being drop into the area.
- source (String) is the ID of the container from where the card is coming from.
- on_card (CardInstance) if the DropArea contains cards (typically a container) this gives the card on which the dragged card is being dropped.
Here an example from the demo:
func _on_CardDrop_dropped(card: CardInstance, source: String, on_card: CardInstance) -> void: var card_mana = card.data().get_value("mana") if _mana >= card_mana: _hand.play_card(card.ref(), _discard_pile) if card_mana < 0: _mana = 0 else: _mana -= card_mana _update_mana()
By the dragged card
You might not want all cards being dropped into your area, you can set a filter by calling the
set_filter(filter) function of
DropArea. The filter is of type
Query, find out how to write queries at the bottom of this page.
By the source container
You can also filter the cards being dropped by the container they are coming from. To do so, add the allowed container IDs in the "Source Filter" array property of DropArea. If the array is empty the DropArea will accept cards from any source. You can also use the
set_source_filter() function to set this filter in script.
By the targeted card
Container to container
Containers can be receivers for a drag and drop action. To activate this feature you have to make sure drop is enabled for your container, see here for more details. You can retrieve the container's DropArea by using the
get_drop_area() function, it is particularly useful if you need to set some filters on it (see above).
Once activated your container will emit the
card_dropped(card, source, on_card) signal, connect to it to decide what to do with the card, here an example from the demo:
func _on_TokenGrid_card_dropped(card: CardInstance, source: String, on_card: CardInstance) -> void: if _tokens.count() >= MAX_TOKENS: return var card_mana = card.data().get_value("mana") if _mana >= card_mana: _hand.play_card(card.ref(), _discard_pile) _tokens.add_card(CardInstance.new(card.data())) if card_mana < 0: _mana = 0 else: _mana -= card_mana _update_mana()