Godot Version
4.6
Question
I’ve been making a card game and wanted to implement a card selection system. i.e. Each round from a hand of 5 cards, I’d like to choose up to 3 cards and use them (thereby ending the round).
My question pertains to how I should keep a track of selected cards .
I approached this problem trying to add the individual card which was selected into an array, however I’ve found that selecting a second card clears the other from the array and deselects it.
# Card component script
var is_hovered : bool = false
var is_selected: bool = false
func hover():
if !is_selected and is_hovered and Input.is_action_just_pressed("click"):
var up_tween: Tween = get_tree().create_tween()
up_tween.tween_property(texture_holder, "position", Vector2(texture_holder.position.x, texture_holder.position.y - 50), 0.1).from_current()
if is_selected and !is_hovered and Input.is_action_just_pressed("click"):
var down_tween: Tween = get_tree().create_tween()
down_tween.tween_property(texture_holder, "position", Vector2(0,0), 0.1).from_current()
func select():
if !is_selected and is_hovered and Input.is_action_just_pressed("click"):
is_selected = true
is_hovered = false
if is_selected and is_hovered and Input.is_action_just_pressed("click"):
is_selected = true
is_hovered = false
```
I’d like help for the logic to somehow save cards that I select. The above code is set into a card class that is assigned to every card.
If the above system isn’t great, please tell me a possible different approach.
Note: I am a beginner, please be patient if I don’t get stuff right away.
Please format your code correctly. It is nearly impossible to read without indentation.
Do this:
```gd
#Paste your code here
```
It’ll look like this:
#Paste your code here
1 Like
I have updated my post. Thank you for telling me how to do it.
2 Likes
So you need to copy and paste your code from the Godot editor into your post again, because all the indentation has been lost.
For example this:
func hover():if !is_selected and is_hovered and Input.is_action_just_pressed(“click”):
var up_tween: Tween = get_tree().create_tween()
up_tween.tween_property(texture_holder, “position”, Vector2(texture_holder.position.x, texture_holder.position.y - 50), 0.1).from_current()
Should probably look like this:
func hover():
if !is_selected and is_hovered and Input.is_action_just_pressed(“click”):
var up_tween: Tween = get_tree().create_tween()
up_tween.tween_property(texture_holder, “position”, Vector2(texture_holder.position.x, texture_holder.position.y - 50), 0.1).from_current()
But I don’t know how you formatted your code.
1 Like
I fixed it to include the indentation.
1 Like
Ok, let’s clean this up a bit. Comments in the code
# Card component script
var is_hovered : bool = false
var is_selected: bool = false
func hover():
# Moving this to the top because it always has to be
# true for us to do anything.
if Input.is_action_just_pressed("click"):
# not is easier to read than bang (!)
if not is_selected and is_hovered:
var up_tween: Tween = get_tree().create_tween()
up_tween.tween_property(texture_holder, "position", Vector2(texture_holder.position.x, texture_holder.position.y - 50), 0.1).from_current()
# only one of these happens on a click, only need to
# test the second if the first doesn't happen
elif is_selected and not is_hovered:
var down_tween: Tween = get_tree().create_tween()
down_tween.tween_property(texture_holder, "position", Vector2(0,0), 0.1).from_current()
func select():
if Input.is_action_just_pressed("click") and is_hovered:
if not is_selected:
is_selected = true
is_hovered = false
else:
is_selected = true
is_hovered = false
So I cleaned up your logic to make it easier to read. In the select() fucntion, you always check if is_hovered is true, so I moved it up and made that clear. If that was a typo, and the second one should be not hovered, that could be the problem. Which is why I suggest using not instead of bang !. It’s much easier to read and see when you missed something.
Is that code correct?
I’m assuming that this part at the end
func select():
if Input.is_action_just_pressed("click") and is_hovered:
if not is_selected:
is_selected = true
is_hovered = false
else:
is_selected = true
is_hovered = false
```
should look more like this:
func select():
if Input.is_action_just_pressed("click") and is_hovered:
if not is_selected:
is_selected = true
is_hovered = false
else:
is_selected = true
is_hovered = false
```
Otherwise, you’re right this is the same functionality but more cleaned up. Regarding the usage of bang (!), I will practice using not for more clear code.
Yeah you’re right. I forgot to indent that section. I did the same thing in the first function. I fixed them both.
Having said all that, I don’t see his code manipulating an Array.
I cleared all the code and rebuilt it prior to creating this post. The array code consisted of something like this:
func select():
if Input.is_action_just_pressed("click") and is_hovered:
if not is_selected:
is_selected = true
is_hovered = false
selected_cards.append(self.owner)
else:
is_selected = true
is_hovered = false
# I can't exactly remember the function to remove item from array. It was correct in the initial code
selected_cards.remove(self.owner)
```
This code resulted on the entire array being cleared every time a new card was selected, leaving only the most recently selected card in the array.
I’m sorry that I cannot give you any screenshots or actual tested code as of now because I cleared it while trying to find a fix.
I’d simply like a way to fix this issue in any way. It need not be through my original method.
extends Node
func _ready() -> void:
var deck: Array = [
"Card 1",
"Card 2",
"Card 3",
"Card 4",
]
print("Starting Deck: %s" % str(deck))
deck.append("Card 5")
print("Draw a card: %s" % str(deck))
var card = deck.pick_random()
print("Select a random card: %s" % card)
deck.erase(card)
print("Play the card and now the deck is: %s" % str(deck))
- create a new scene with a Node as the root node.
- Add this script to the node.
- Save the scene and script.
- Press the Run Current Scene button.

You’ll get output like this:
Starting Deck: ["Card 1", "Card 2", "Card 3", "Card 4"]
Draw a card: ["Card 1", "Card 2", "Card 3", "Card 4", "Card 5"]
Select a random card: Card 2
Play the card and now the deck is: ["Card 1", "Card 3", "Card 4", "Card 5"]
1 Like
I understand the code and the logic behind this. My question is how I implement the same based on player selection instead of it being randomised.
func _process(_delta):
select()
# Card component script
var is_hovered : bool = false
var is_selected: bool = false
func _on_mouse_entered():
if not is_selected and not is_hovered:
var up_tween: Tween = get_tree().create_tween()
up_tween.tween_property(texture_holder, "position", Vector2(texture_holder.position.x, texture_holder.position.y - 50), 0.1).from_current()
is_hovered = true
func _on_mouse_exited():
if is_hovered and not is_selected:
var down_tween: Tween = get_tree().create_tween()
down_tween.tween_property(texture_holder, "position", Vector2(0,0), 0.1).from_current()
is_hovered = false
func select():
if Input.is_action_just_pressed("click") and is_hovered:
if not is_selected:
is_selected = true
print("selected")
elif is_selected:
is_selected = false
print("deselected")
```
This is the latest version of my cards script. I’m not sure why but upon selecting a new card, the previous card gets deselected.
Video showing the problem. I think this was the same problem I was facing as before where items were getting deleted from my selected cards array. How can I fix this?
Ok, well I went back to basics because the code you posted literally does not work. You are using incorrect function names. So that told me that you do not understand the syntax at least. Using the correct function name is pretty basic and I have to evaluate your knowledge based on what you post - not what’s in your head.
The randomization was not really the point. It was just a way to remove something from the mix as if it was a player.
That cannot be the whole script. Please post the whole thing. What is it extending? Based on the code you are showing, it is defaulting to extending RefCounted. That’s why it’s not working.
class_name Card
extends Control
@export var suit: String
@export var type: String
@export var power: int
@export var ID: int
@export var texture_holder: Sprite2D
const SIZE := Vector2(280, 400)
func _ready():
get_viewport().physics_object_picking_first_only = true
func _process(_delta):
select()
# Card component script
var is_hovered : bool = false
var is_selected: bool = false
func _on_mouse_entered():
if not is_selected and not is_hovered:
var up_tween: Tween = get_tree().create_tween()
up_tween.tween_property(texture_holder, "position", Vector2(texture_holder.position.x, texture_holder.position.y - 50), 0.1).from_current()
is_hovered = true
func _on_mouse_exited():
if is_hovered and not is_selected:
var down_tween: Tween = get_tree().create_tween()
down_tween.tween_property(texture_holder, "position", Vector2(0,0), 0.1).from_current()
is_hovered = false
func select():
if Input.is_action_just_pressed("click") and is_hovered:
if not is_selected:
is_selected = true
Globals.selected_cards.append(self.owner)
print("selected")
elif is_selected:
is_selected = false
Globals.selected_cards.erase(self.owner)
print("deselected")
```
Ok, and what does the Globals script look like?
# GLOBALS
extends Node
var selected_cards: Array = []
I created the globals script literally before this to access the selected cards across many scenes.
1 Like
Ok so walking through your code, it looks like:
- If you hover your mouse over a card, it animates up and
is_hovered is set to true
- If you stop hovering, the card goes back down and
is_hovered is set to false
- Anytime you click and
is_hovered is true one of two things happens:
A. If the card is not selected, is_selected is set to true and you append the Card’s owner (which is the root of the scene) to the list of selected cards.
B. If the card is selected, is_selected is set to false, you remove the root of the scene from the array.
I think this is your bug. You are appending self.owner when you want to be appending self.
This is a reason I prefer strict typing. If you declared your global array as an Array that only holds Card objects, you would have got an error telling you that your scene is not of type Card. Also, you don’t need to initialize and empty array.
# GLOBALS
extends Node
var selected_cards: Array[Card]
See if this works for you. (I formatted it according to the GDScript Style Guide.)
class_name Card extends Control
const SIZE := Vector2(280, 400)
@export var suit: String
@export var type: String
@export var power: int
@export var ID: int
@export var texture_holder: Sprite2D
# Card component script
var is_hovered : bool = false
var is_selected: bool = false
func _ready():
get_viewport().physics_object_picking_first_only = true
func _process(_delta):
select()
func _on_mouse_entered():
if not is_selected and not is_hovered:
var up_tween: Tween = get_tree().create_tween()
up_tween.tween_property(texture_holder, "position", Vector2(texture_holder.position.x, texture_holder.position.y - 50), 0.1).from_current()
is_hovered = true
func _on_mouse_exited():
if is_hovered and not is_selected:
var down_tween: Tween = get_tree().create_tween()
down_tween.tween_property(texture_holder, "position", Vector2(0,0), 0.1).from_current()
is_hovered = false
func select():
if Input.is_action_just_pressed("click") and is_hovered:
if not is_selected:
is_selected = true
Globals.selected_cards.append(self)
print("selected")
elif is_selected:
is_selected = false
Globals.selected_cards.erase(self)
print("deselected")
You understanding of my scripts is correct, however appending self instead of self.owner changes nothing. I believe the problem stems from somehow the select/deselect being called on a card instance even when interacting on another card.
Eg. If i click to select on card #2, it get selected and appended to the array however the last selected card #1 is deselected, thereby showing a deselect statement in Output.
The code you show is attached to each card?
When you stop hovering over a card that is selected what changes that card to is_hovered = false?
It looks to me like that never happens. So you have two selected cards both will have is_hovered = true and that means the select() function runs for each every time you click the mouse button on a card.
The code you show is attached to each card?
Yes
When you stop hovering over a card that is selected what changes that card to is_hovered = false?
func select():
if Input.is_action_just_pressed("click") and is_hovered:
if not is_selected:
is_selected = true
Globals.selected_cards.append(self)
print(Globals.selected_cards)
elif is_selected:
is_selected = false
Globals.selected_cards.erase(self)
print(Globals.selected_cards)
This function controls the selection. I dont set is_hovered = false as it bugs out, preventing me from deselecting cards entirely. Though this does fix selecting, it breaks deselecting.
If the select() function runs on a card that is selected but not hovered it will be deselcted.
So at a guess maybe:
func _on_mouse_exited():
if is_hovered and not is_selected:
var down_tween: Tween = get_tree().create_tween()
down_tween.tween_property(texture_holder, "position", Vector2(0,0), 0.1).from_current()
is_hovered = false
elif is_hovered:
is_hovered = false
But I would reorganize so that the process function isn’t running on non-interacted cards. You can use the set_process(false) function and turn off processing for that card.
It should be the default for cards to have no processing and then when the mouse is over it you set_process(true) to start polling it.
However going a little further, if you make each card a button you can turf the process function altogether and just use the signals.
1 Like