Array with Objects sorting by distance to player

Godot Version

4.2.1

Question

Greetings everyone!
I ask for help, because… I realized that I was too stupid to figure out what was missing. I really don’t understand what to do.
The idea is (seemingly) elementary:
The object “player1” has an Area2D (AttackArea) of a specific length (let’s call it the attack distance). When intersecting with bodies, these objects are written to an Array. Each frame updates this Array.
I’m trying to sort this list via sort _custom in descending order from the closest object of overlap with Area2d to the farthest. Thus, I want to create a mechanics for breaking through targets and the number of targets that a weapon (for example a laser) can penetrate.

extends Area2D

@onready var shape_pos:= $CollisionShape2D
var home_pos: Vector2 # Position of CollisionShape
var distance: float

func get_area_intersect() -> void:
	var area_array = get_overlapping_bodies() #Create Array of bodys
	if area_array:
		for target in area_array:
			home_pos = shape_pos.global_position #get position of players Attack shape
			distance = target.position.distance_to(home_pos)
			print(target.name, "- ", round(target.position.distance_to(home_pos)))
			
		#area_array.sort_custom(target_sorting) NOT working
		print(area_array)
//Prints :
StaticBody1- 155
StaticBody2- 242
StaticBody3- 330
[StaticBody2: <StaticBody2D#29343352035>, StaticBody3: <StaticBody2D#29393683701>, StaticBody1: <StaticBody2D#29444015352>]

When the player’s position relative to the “bodies” is different, the list is formed depending on the frame into which a new body is added, the distance is calculated correctly, but this list of objects cannot then be sorted. Every time it gives the error “It is impossible to take into account the “parameter” on the basis of “Object”. I understand that I need to somehow get to either the properties of objects inside the Array, or somehow convert this Array in order to gain access later.

Accordingly, the list needs to be sorted:

//Prints :
StaticBody1- 155
StaticBody2- 242
StaticBody3- 330

[StaticBody1: <StaticBody2D#29343352035>, StaticBody2: <StaticBody2D#29393683701>, StaticBody3: <StaticBody2D#29444015352>]

Perhaps there is a completely different, normal solution to this task. Please tell me.

You need to do the comparison in your target_sorting function, so as it’s written you’d need to do the calculation each time in that function, e.g.

func target_sorting(target1, target2):
	var distance1 = target1.position.distance_squared_to(shape_pos.global_position)
	var distance2 = target2.position.distance_squared_to(shape_pos.global_position)
	return distance1 <= distance2

However if you’ve got lots of targets to sort then this is probably not going to be very efficient on multiple counts, one being all that repetitive double calculating of distance_to.

If I was trying to find a low number of targets from a larger number of targets, I’d probably try finding the x number of results I wanted, rather than sorting the array. E.g. I’d make an array that I was going to put up to X items into, then populate that array with the first X items keeping track of the distances away (in particular the furthest distance), inserting closer items before this based on their distances. For the remaining targets I’d check them against the furthest one I’d saved so far. If it was closer then I’d remove the last item in the array and insert the replacement in the correct place in the list as per the original population.
This will be less efficient the higher X is.

1 Like

I don’t understand much what did you mean, but I will post out the code:

extends Area2D

@onready var shape_pos := $CollisionShape2D
var home_pos: Vector2 # Position of CollisionShape
var distance: float

func get_area_intersect() -> void:
	var area_array := get_overlapping_bodies() #Create Array of bodys
	home_pos = shape_pos.global_position #get position of players Attack shape

	if area_array:
		area_array.sort_custom(the_distance_sorter)

		for target in area_array:
			distance = target.position.distance_to(home_pos)
			print(target.name, "- ", round(distance))

func the_distance_sorter(a, b) -> bool:
	if a.position.distance_squared_to(home_pos) < b.position.distance_squared_to(home_pos):
		return true
	return false

this should print out a sorted array

1 Like

Your sorter looks fine to me. I’m therefore not sure why it’s causing you issues.

The rest was in regard to your use of the results.

Edit: I wrote this thinking coderjo’s message was in response to my message - it obviously wasn’t, I didn’t check who’d written it, I was thinking it was the original posters, e.g. GorevAndrew. :melting_face:

Personally I would do it from the side of the laser rather than the object. And count how lasers the object has hit so far.

You dont really need to work out which object is closest, you just discount that object once its been hit.

“I don’t understand much what did you …”
NO) Its I dont understand HOW THIS WORKS. YOU DO. I’M NOT.
You are God. Thank you so much!
But now I have another question:
How did you manage to make the comparison code work if “a” does not accept a property? (next comes a method with a parameter, this seems clear) Is position a property? Apparently the code is not mine.

@OriginalBadBoy
I was thinking this was in regard to some sort of area effect weapon rather than a laser type weapon, perhaps a bomb or aura type effect? TBH it wasn’t very clear.

don’t call me “god” please

so, Array.sort_custom will iterate through itself and comparing items by calling a function which is passed in as a parameter.
the function you pass in determines a is before or after b. if a should before b, you should return true, and so on.

you want the nearest to be the first, so just comparing the squared distance (squared distance avoids calculating the square root, so faster) from a and b to the home_pos.

and you can now working on your sorted array

1 Like

'm sorry, Ok.
In fact, I used comparison, but every time I ran into an error, apparently because I coded this:

func target_sorting(a, b):
       if a.distance < b.distance

In this case, the distance was a variable with the distance to the target and I assumed that it would turn out:

func target_sorting(a, b):
       if (187) < (210)
           return TRUE

And it was not successful. Thank you a lot !

yeah you reminded me, it can actually code it like this:

func target_sorting(a, b):
       return a.distance < b.distance

and I thought it should be written like this:

a.distance_to(home_pos) < b.distance_to(home_pos)

and it seems that you have learned expression language, like rust? expression language will return the value of a line of code without semicolon you know…

or you may mean:

func target_sorting(a, b):
       if a.distance < b.distance:
              return true
       return false

?

1 Like

You’re absolutely right, that’s exactly how I thought of writing it.

func target_sorting(a, b):
        if a.distance < b.distance:
               return true
        return false

…and it seems that you have learned expression language, like rust? expression language will return the value of a line of code without semicolon you know…

  • No, I didn’t learn it, but apparently it’s time to understand the difference)

After reading your code, I realized the main mistake: if you parse the MY code with variables (substitute the values of the variables), you get:
“(a)Object. (distance)Object.get distance(starting point) —> Object.189”.
It turns out I’m not calling a method on an object…And of course it will give an error that the compiler cannot obtain the data, because base “Object”.

1 Like

It’s kind of silly when I found it could be written as:

func target_sorting(a, b):
        return a.distance_to(home_pos) < b.distance_to(home_pos)

since if is checking the bool value of the expression, then why we just return that?
ha ha ha…well I’m ridiculous

1 Like