How can I merge two groups into one?

Godot Version

4.4 Dev 3

Question

Hi everyone, I’m trying to organize a multiple number of Node2D into groups. For example: I have a group 1 with three Node2D and a group 2 with three Node2D. If group 1 collides with group 2 and the player presses a button, then group 1 and group 2 should be merged with all Node2D from group 1 and group 2. How can I best implement this?

The background is that I would like to have only one variable, e.g. speed or brake, for all Node2D in a group.

You could create a group class that has an array that stores all nodes and variable for speed etc.

On collision create a new group and add the nodes from those groups in the new one.

Hi, first of all thank you for the answer, but I think you need to go into more detail.

I am currently working on a small prototype with your suggestion.

  1. At the moment I am filling the Node2DArray by hand, is there a better way?

  2. How do I merge the two groups into one group?

Class Group

class_name Group
extends Node2D

@export var group_number: int = 0
@export var Node2DArray: Array[Node2D]
@export var speed: float = 0.0
@export var brake: float = 0.0

Object Script:

extends Group

var pressed: bool = false


func _process(delta: float) -> void:
	if pressed == true:
		position = get_global_mouse_position()


func _input(event):
	if event is InputEventMouseButton:
		if event.button_index == MOUSE_BUTTON_LEFT and not event.pressed:
			pressed = false


func _on_area_2d_input_event(viewport, event, shape_idx):
	if Input.is_action_just_pressed("click"):
		pressed = true


func _on_area_2d_area_entered(area: Area2D) -> void:
	var parent = area.get_parent()
	print(parent.group_number)

I feel that using an array might be not be the best answer here. Really it depends on whether you want them to be able to split up again in an organized way or not. I would just make like, the main node in charge of what groups get added together, and just add them as children of a new, higher up group node.

extends Node
# main script, on the root node

var Hey: GroupNode = $Hey # your groups
var What : GroupNode = $What

func conjoin( A : GroupNode, B : GroupNode) -> GroupNode:
	var Parental : GroupNode = GroupNode.new()
	#give the new parent node the coordinates of one of the groups, or make it somwhere in between
	#if you wanted it to be the average between the two of them, im pretty sure using = A.Transform2D - B.Transform2D works        
	Parental.Transform2D = A.Transform2D 
	A.reparent(Parental, true)
	B.reparent(Parental, true) # Set to true otherwise it moves to its parents coordinates
	return Parental

if you want them to only be able to do that while theyre colliding AND while a player is pressing a button, you’ll want to have access to some internal state from the groups to check when the player pressses a button, and im guessing you’d want them to still be like, some kind of platform for the player to jump on? so you could do this

class_name Group
extends Area2D

# export so you can choose the shape in the editor
# go with collisionpolygon not collisionshape becase we can do stuff with its polygons
@export var HitBox : CollisionPolygon2D
var AmIColliding : bool = false
var WhoIsTouchinMe : Array[Group]

func _physics_process(delta: float) -> void:
	if(has_overlapping_areas()):
		WhoIsTouchinMe.clear()
		var Areas : Array[Area2D] = get_overlapping_areas()
		for Area in Areas:
			if Area is Group:
				AmIColliding = true
				WhoIsTouchinMe.append(Area)
	else:
		AmIColliding = false

func polygons_please() -> PackedVector2Array:
	HitBox.disabled
	return HitBox.polygon

and then on your main script

extends Node
# still the main script, on the root node

var ChildGroups : Array[Group]

func _ready() -> void:
	var Children : Array[Node] = self.get_children()
	for Child in Children:
		if Child is GroupNode: # gather all the groups in one place
			ChildGroups.append(Child)

func player_pressed_the_button() -> void:
	var GroupCollision : bool = false
	for GroupNode in ChildGroups: 
		if(GroupNode.AmIColliding):
			GroupCollision = true
	if(!GroupCollision):
		return
		# end the function here if theres no colliding groups
	var NewGroupArray : Array[Group]
	var NewPolygons : PackedVector2Array
	var NewGroupParent : Group
	for GroupNode in ChildGroups:
		if(GroupNode.AmIColliding):
			NewPolygons.append_array(GroupNode.polygons_please())
			GroupNode.reparent(NewGroupParent, true)
		else: # probably set the transform2d somwehere, im tired
			NewGroupArray.append(GroupNode) # keep any uncollided groupnodes in an array where we can find them
	var HitBox : CollisionPolygon2D = CollisionPolygon2D.new()
	# the way 2d collision shapes are calculared is pretty simple, and somhow this actually works
	HitBox.polygon = NewPolygons
	NewGroupParent.add_child(HitBox)
	# joined node rright at the end of the array, easy to find. just use array.back()
	NewGroupArray.append(NewGroupParent)
	ChildGroups.clear() # replace old array with new one
	ChildGroups.append_array(NewGroupArray)

and you can just check from whatever script you put on the groups that join up to check if they’ve had their hitboxes disabled to know if thheve been adjoined. this is a bit overkill if you only wanted two groups, but technically it can work for however many you want. if you want them to be able to break up again just make the new parent nodes a class that extends group like “groupparent” or something. they could either join more groupparents together and make a tree-like structure, or you could just have one megaparent that holds them all and can break them off individually.

but yeah i spent way longer on this than i thought i would. Just ask if you need any further help or anything

Thank you for the detailed answer. Yes, the topic is more difficult than expected. I was able to transfer the solution to my game. So far it works. :+1: Thank you very much. :slight_smile:

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.