Ok, I happen to see this problem twice for now and
since my first ever released game 'vpop' (listed in the agk showcase, would appreciate much if andorid user fella's lend a hand that lol)
did not downloaded as much as I expected and set me sad about game programming, I can spare time for codes.
At the topic 'inverse square root' I'd suggested to not to use sqrt.
So, if you only check if a unit is in range or not (bool), you definetly do not have to use sqrt.
You check the absoulte value of x differences, if it is below the ranged distance then you check the absolute value of y distances and if it is below the ranged distance then the unit is in range.
So, instead of;
Distance = Sqrt((Ax - Bx) * (Ax - Bx) + (Ay - By) * (Ay - By))
If Distance <= Range Then InRange = True
If Abs(Ax - Bx) <= Range
If Abs(Ay - By) <= Range Then InRange = True
EndIf
Then, another problem occurs, do we have to check all units against each other?
No!
We may subdivide the map into grids and group the units according to their current grids.
And then we check only the units which are in the grid_range.
Let's say;
gridsize = 100
range = 190
xpos = 543
ypos = 409
so, our unit is in the grid 5, 4 and, our grid_range is +-2 (range = 190) -> (1 + Floor(Range/GridSize))
we check the units which are on the grids, from (3, 2) To (7, 6).
So, every grid will hold the ID of the unit which are on it and the number of total units on it.
About the performance, I compared @Virtual Nomad's code in equalavent circumtances (unit count, resolution) and saw that, when the unit count on the map is low, my code is slower but when it goes like 1024, my code is faster, besides no sprite usages.
The grid size and unit's ranges directly affects the performance on my code. And there is a limit which a grid can hold maximum units.
And If I was making a 2D game with sprites, I'd choose @Virtual Nomad's code for sure.
Hope this helps...
Simply drag the mouse on the screen to set our hero's new position, it is fun to watch, like a spider is walking on it's web.
This code may not be the best solution and is avaible for optimization for better performance;
// Project: enemy distance
// Created: 2022-02-16
// show all errors
SetRawWritePath(GetReadPath())
SetErrorMode(2)
#option_explicit
// set window properties
SetWindowTitle( "enemy distance" )
SetWindowSize( 1920, 1080, 0 )
SetWindowAllowResize( 1 ) // allow the user to resize the window
// set display properties
SetVirtualResolution( 1920, 1080 ) // doesn't have to match the window
SetOrientationAllowed( 1, 1, 1, 1 ) // allow both portrait and landscape on mobile devices
SetSyncRate( 0, 0 ) // 30fps instead of 60 to save battery
SetScissor( 0,0,0,0 ) // use the maximum available screen space, no black borders
UseNewDefaultFonts( 1 ) // since version 2.0.22 we can use nicer default fonts
#Constant _GRID_SIZE_ 64
#Constant _X_TILES_ 20
#Constant _Y_TILES_ 16
#Constant _UNIT_RADIUS_ 7
#Constant _MAX_UNITS_IN_A_GRID_ 32
Global _UNIT_GRID_MAP_ As Integer[_X_TILES_, _Y_TILES_, _MAX_UNITS_IN_A_GRID_]
//This Arrays first index will hold the unit count on it
//_UNIT_GRID_MAP_[gX, gY, 0] = UnitCount
//_UNIT_GRID_MAP_[gX, gY, 1] = First Unit Index
Type _UNIT_TYPE_
Status As Integer
xPos As Float
yPos As Float
xTile As Integer
yTile As Integer
Range As Float
GridRange As Integer
EndType
#Constant _MAX_UNITS_ 256
Global _UNIT_ As _UNIT_TYPE_[_MAX_UNITS_]
_CREATE_UNITS_()
//randomly place units to the map
//calculate their current grid positions
//randomy give them some ranges
//calculte how much grids will be checked depending on the range
Local mB As Integer, mX As Float, mY As Float
Do
Print( ScreenFPS() )
mB = GetPointerState()
If mB
mX = GetPointerX()
mY = GetPointerY()
If mX < _X_TILES_ * _GRID_SIZE_
If mY < _Y_TILES_ * _GRID_SIZE_
If mX >= 0
If mY >= 0
_UNIT_[0].xPos = mX
_UNIT_[0].yPos = mY
EndIf
EndIf
EndIf
EndIf
EndIf
_UPDATE_UNITS_()
_CHECK_RANGES_()
_DRAW_()
Sync()
Loop
Function _CHECK_RANGES_()
Local U As Integer, kX As Integer, kY As Integer, lX As Integer, lY As Integer, i As Integer, j As Integer, E As Integer, Z As Integer
Local Ax As Float, Ay As Float, Range As Float
Local GridRange As Integer, X0 As Integer, X1 As Integer, Y0 As Integer, Y1 As Integer
For U = 0 To _MAX_UNITS_ - 1
kX = _UNIT_[U].xTile
kY = _UNIT_[U].yTile
Ax = _UNIT_[U].xPos
Ay = _UNIT_[U].yPos
Range = _UNIT_[U].Range
GridRange = _UNIT_[U].GridRange
//Precalculate the grids index limits, which will be checked
X0 = kX - GridRange
If X0 < 0 Then X0 = 0
X1 = kX + GridRange
If X1 >= _X_TILES_ Then X1 = _X_TILES_ - 1
Y0 = kY - GridRange
If Y0 < 0 Then Y0 = 0
Y1 = kY + GridRange
If Y1 >= _Y_TILES_ Then Y1 = _Y_TILES_ - 1
For j = Y0 To Y1
For i = X0 To X1
For E = 0 To _UNIT_GRID_MAP_[i, j, 0] - 1 //unit count on that grid
Z = _UNIT_GRID_MAP_[i, j, E + 1]
If Z = U Then Continue //dont check ourselves
//If _UNIT_[U].Status = _UNIT_[Z].Status Then Continue //
If Abs(Ax - _UNIT_[Z].xPos) <= Range
If Abs(Ay - _UNIT_[Z].yPos) <= Range
//I N R A N G E ! ! !
DrawLine(Ax, Ay, _UNIT_[Z].xPos, _UNIT_[Z].yPos, -1, -1)
EndIf
EndIf
Next
Next
Next
Next
EndFunction
Function _DRAW_()
Local X As Integer, Y As Integer, kX As Integer, kY As Integer, Z As Integer
Local dX As Float, dY As Float
For Y = 0 To _Y_TILES_
DrawLine(0, Y * _GRID_SIZE_, _X_TILES_ * _GRID_SIZE_, Y * _GRID_SIZE_, 0xFF808080, 0xFF808080)
Next
For X = 0 To _X_TILES_
DrawLine(X * _GRID_SIZE_, 0, X * _GRID_SIZE_, _Y_TILES_ * _GRID_SIZE_ - 1, 0xFF808080, 0xFF808080)
Next
For X = 0 To _MAX_UNITS_ - 1
Y = 0xFFFF8000
If _UNIT_[X].Status = 1 Then Y = 0xFF0000FF
If X = 0 Then Y = 0xFF00FF00
DrawEllipse(_UNIT_[X].xPos, _UNIT_[X].yPos, _UNIT_RADIUS_, _UNIT_RADIUS_, Y, Y, 1)
Next
EndFunction
Function _UPDATE_UNITS_()
Local E As Integer, tGrids As Integer, z As Integer, kX As Integer, kY As Integer
Local Ax As Float, Ay As Float, Bx As Float, By As Float, i As Float, j As Float
For E = 0 To _Y_TILES_ - 1
For Z = 0 To _X_TILES_ - 1
_UNIT_GRID_MAP_[Z, E, 0] = 0 //The first index holds the unit count in that grid
Next
Next
For E = 0 To _MAX_UNITS_ - 1
Ax = _UNIT_[E].xPos
Ay = _UNIT_[E].yPos
kX = Floor(Ax/_GRID_SIZE_)
kY = Floor(Ay/_GRID_SIZE_)
_UNIT_[E].xTile = kX
_UNIT_[E].yTile = kY
tGrids = _UNIT_GRID_MAP_[kX, kY, 0] + 1
_UNIT_GRID_MAP_[kX, kY, 0] = tGrids
_UNIT_GRID_MAP_[kX, kY, tGrids] = E
Next
EndFunction
Function _CREATE_UNITS_()
Local E As Integer, j As Integer
Local xPos As Float, yPos As Float
For E = 0 To _MAX_UNITS_ - 1
Do
xPos = (Random() * _GRID_SIZE_ * _X_TILES_)/65535
yPos = (Random() * _GRID_SIZE_ * _Y_TILES_)/65535
For j = 0 To E - 1
If Abs(_UNIT_[j].xPos - xPos) <= _UNIT_RADIUS_
If Abs(_UNIT_[j].yPos - yPos) <= _UNIT_RADIUS_ Then Exit
EndIf
Next
If j = E Then Exit
Loop
_UNIT_[E].xPos = xPos
_UNIT_[E].yPos = yPos
j = Random()
_UNIT_[E].Status = 1 + (j && 1)
_UNIT_[E].Range = _GRID_SIZE_ + (Random() * _GRID_SIZE_/65535)
_UNIT_[E].GridRange = Floor(_UNIT_[E].Range/_GRID_SIZE_)
Next
_UNIT_[0].Range = _GRID_SIZE_ * 3
_UNIT_[0].GridRange = 1 + Floor(_UNIT_[0].Range/_GRID_SIZE_)
EndFunction
P.S. Also @Virtual Nomad's units are sorted due to their distances.