How to set up a Drag and Drop list with swappable items

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Blooobird

I am very new to coding. I am learning Godot on the fly to build my football manager type sim. The first insurmountable problem I have come up against is how to set up the Squad Page so that I have a list of players with various stats that I can drag and drop and swap with each other. Any help or advice would be really appreciated!

I have watched various videos on inventories and GUIs but nothing has the functionality I’m looking for. Specifically to be able to pick up a player and drag him so that he can be swapped with another player in the team with all of the underlying data swapping with him.

If anyone has any tips or could perhaps point me in the direction of some videos or tutorials on this I’d be very grateful.

Thanks very much.
JP

i think the bare minimum is to create a Control object (panel+label+other things) object for each player, and place them into a list (a parent VBoxContainerYou need to have one list for each team).
The player-object also needs an area (i suggest to use aTextureButton, the area could even be as big as the entire object) : with a click on that area the object is removed from the VBoxContainer and placed into a momentary parent node, then you link your mouse movement to the player rect_position property.
The VBoxContainer (the list) also need a similar 2d area to check if the mouse entered or not, so that when mouse is released the player-object is moved into a secondary VBoxContainer (the other team)

Andrea | 2020-05-26 11:19

Hi Andrea. Thanks for your message. I think I understand the general idea of clicking and dragging but my problem is I would like to be able to swap the player I am dragging with another that is in place in the team.
So, for instance I might want to swap a substitute with a 1st team player. I would click on the sub, drag him over the first team player and release which would snap the sub into the first team player’s spot and the first team player would snap into the newly vacant sub’s slot. If you get me?

Blooobird | 2020-05-26 12:30

In that case the “squad” object need to be a little bit more complex than a simple list, but the concept is not different: point is you create a game-tree structure similar to PC folders (goalkeeper folder, attacker folder, sub folder), you place players inside and move them by simply changing folder (check add_child(), move_child(), remove_child()).
For example when you release one player on top of the current goalkeeper a sequence similar to this one happens:

goalkeeper_folder.remove_child(old_goalkeeper)
sub_folder.add_child(old_goalkeeper)
goalkeeper_folder.add_child(dragged_new_player)

After your structure is done, there are hundreds of way to show it visually, either with Control nodes that arrange the rect position automatically on the screen (like, setting the goal-keeper position to be on top, ecc) or even using Node2D and your personal code.

I know it’s not a very specific solution, the specific one depends on what you are trying to achieve!

Andrea | 2020-05-26 13:08

Thanks for your help Andrea. Can I ask some more questions? :slight_smile:

Lets say I set up two squads : sqaudA and squadB
Squad A is the first team and the stats of the players combine to make the team stats (shooting, passing, stopping).
SquadB is the reserves and the stats do not affect the team’s stats.

I then create individual nodes for each player and put them in the two squads. when I swap one player from squadB with a player in squadA how do I get the new player’s stats to update the team’s stats?

I’m guessing the squads need to be stored in an array? Is that right?

Thanks again, I am really pulling my hair out over this!

Blooobird | 2020-05-27 22:35

:bust_in_silhouette: Reply From: Andrea

Well again, there are hundreds of way to do it, and yes you can create your personal array to store the squad composition, however if you use the folder-structure, keep in mind the the function parent_node.get_children() already returns an array containing the nodes below it.
The easiest solution is to create a swap_player function that is called when a player is dragged and release on top of another, that both move the player and re-calculate the stats of the entire squad. Something like:

Squad:

Subs:

Player1
Player2

Goalkeepers:

Player3
Player4

Attackers:

Player5
Player6

func swap_player(dragged_player, old_player, squad):  
  dragged_player.get_parent().remove_child(dragged_player)
  old_player.get_parent().add_child(dragged_player)
  old_player.get_parent().remove_child(old_player)
  sub.add_child(old_player)
  squad.calculate_stats()

func calculate_stats():  #inside squad node script
  for role in get_children():
    for player in role.get_children():
      do_your_calculation()

Hey. Are you enjoying the sun? (I have no idea where you are, but I hope its sunny! :slight_smile:

Thanks again for your help. Are you a football fan?

Sorry but I am struggling to keep up. I hope you don’t mind me asking for more help but if you could help me get my head around this?

I have set up the nodes like this:

Squad Page (Control Node)
____Scroll Container
________Squad slots (Say GK, Left Back, reserves) as a texturebutton
_____________Players (John, Harvey, jack etc) also a texture button
__________________stats (name, shooting, passing, stopping, picture) as labels

I should be able to set up the Squad Slot as a landing zone and the Players as moveable objects for the drag and drop but where is the information stored so I can get at it?

For instance, I would like to to be able to work out the team stats for shooting by adding the shooting stats of just the two strikers (position 10 and 11 in the 1st team), the passing would be the passing stats of the 4 midfielders (positions 6,7,8,9) and the stopping would be the stopping stats of the 5 defenders (including the GK). But obviously these stats would change every time I moved a player in and out of the first team.

I would also like to be able to set different formations, which would mean you could have 4 defenders, 3 midfielders and 3 forwards for instance.

Thanks again

Blooobird | 2020-05-28 16:03

it’s sunny indeed!
Not a football fan though…

first of all: i have to say control nodes are quite counter-intuitive for new users, they have a lot of setting to arrange things automatically, but it is kinda confusing imo.
If this is your very first project maybe switch to Node2D (and control the nodes through their position value) unless impossible to to otherwise (like, the scroll container is not that easy to reproduce with node2D).

If i understood correctly, what you are doing wrong is storing the information you need inside Labels, instead of inside the player script: remembers that nodes do not have to be “visual” objects only, they can serve any purpose, even just as variable store.
For example you could attach a script to the player nodes, and assign them many variable for each stat you need. Everytime you want to access to this stat, you simply have to call the player node and its stat, like
In the player script:

var shooting=1
var passing=5
var stopping=2

you do not have to create a script for EACH player (John, Harvey, Jack) you can creat an empty “template” and instance it multiple time (meaning you place a new scene as node in the tree, see instancing https://docs.godotengine.org/en/stable/getting_started/step_by_step/scripting_continued.html ), after you instance you fill the value you want

When you need to access their stats, you call the player node and access to its variables.
E.g in the squad script:

func calculate_stopping_stat():
 var defenders_node=get_node(path to defender node)
 squad_stopping=0   #resets any possible value you calculated before
 for player in defender_node.get_children():
   squad_stopping+=player.stopping
 show_updated_quad_stats()

Only then you use Labels to show the stats, just using Label.text properties

func show_updated_squad_stats():
  Label1.text="Shooting: " + str(shooting)
  Label1.text+="\nPassing: " + str(passing)

\n creates a new line, so you can write a lot of things in one single label.

Andrea | 2020-05-28 18:59

Thanks Andrea.

I realised my coding skills are pretty poor so I’m working my way through Codeacademy’s Python course to get a better understanding then I will work through your email. Thanks so much for your help.

I hope you won’t mind me asking some more questions when I get stuck in to it?! :slight_smile:

Thanks again
JP

Blooobird | 2020-05-29 12:36