Nearest entity...

Blitz3D Forums/Blitz3D Programming/Nearest entity...

Here's the thing.

I have lots of turrets around a map, and they each try and choose a target nearest to them. This involves running through all the targetable objects and doing an "EntityDistance" with each one.

This was slow. So, optimised it. Turrets only check for new targets every four seconds (the turrets begin with a random time offset to spread the workload). But it's still a bit on the slow side with a lot of turrets (about 10-15). And I'd like to have quite a few.

So, can anyone think of a way of optimising this further? Some alternative to EntityDistance? It doesn't need to be precise.

Couple of questions:

- How many turrets are we talking about here?
- Are turrets always placed at the same locations or are they randomly positioned?
- Are turrets stationary or can they move around?
- Do turrets have a maximum firing range?

OK, that's more than a couple. :P

Okay...

Before I answer your questions, an addendum/errata.

Despite being called far less often, the deltayaw and deltapitch commands used for lining up the turrets seem to be eating a fair chunk of runtime too. Any suggestions here would be appreciated. :D

Here we go:

-The game starts to take a speed hit with around 10 turrets. I've stress-tested the game with 80. It was pretty slow. That's quite a lot, probably a realistic aim would be between those two figures. Turrets are made from usually three objects: a base, a rotating turret and a gun barrell that pitches up and down.

-Turrets are placed according to the mission.

-Some can move (those attached to spaceships) some are stationary (attached to the ground or space stations).

-Yes, turrets have a maximum firing range. Unfotunately, an entitydistance needs to be performed on everything to make sure it's outside that maximum range.

Thanks for the response!

I fail to see how 10-15 EntityDistance() calls every 4 seconds would consume any CPU time whatsoever.

But I'll throw some psuedu towards ya anyways... Break your map up into grids.. these grids can be waypoints, nodes or single pivots with a distance entity around them..

Your turrents would then only check the grids in it's location therefore not having to check every targetable mesh on the map.

ie: if targetable=turrentgridrange then ;preform check

A simple equation can be used to automatically break your worldspace into grid chunks based on max x,z... Y should'nt filter into grid locales unless you have very high stacking maps.

It's not 10-15, though.

Each turret can target other buildings...

Oh, wait, there's one solution right there. Immobile turrets shouldn't bother checking immobile buildings. If they can't hit each other at the start, they'll never be able to. Thought I'd got rid of stupid oversights like that.

Anyway...

Each turret performs and EntityDistance on every other shootable item in the scene. So if I have 80 buildings and 10 ships in my scene, a turret attached to a ship has to do 90 checks to find the closest. Multiply by three, for three turrets on a single ship, and we have 270. Multiply by four ships and we have over 1080 Entitydistance checks.

Ahh i see... well if you want a quick fix you can do something like this:

Break your turrent checking code into timeslices..Ie: only check say 5 turrents per render then move on to the next 5.. This will guarentee that the game logic only uses 5 entitydistance calls per render... on the flip side you'll have less responsive turrent reactions but in reality it would probably benefit you the most as it'll break up the routine pattern and provide you with some variety.

pesudeo (cheap and nasty example):

if turrentcurrent>totalturrents then turrentcurrent=1
for t=turrentcurrent to turrentcurrent+5
..
..
..
next
turrentcurrent=turrentcurrent+5

That's similar to what I'm already doing. Each turret has a 4 second timer which, when it expires, triggers the turret to scan for a new target. As the level is loaded, each turret's timer is set to a random offset between zero and four seconds.

But your way's actually better, since it sets a maximum number of turrets to process, and doesn't allow it to be exceeded, whereas my way is prone to "clumping" of timers expiring.

EntityPick()?

i'm not sure how fast it is, but supposedly it returns the nearest pickable entity.

Why not grid the whole thing...

Take the entityx and z of your items, divide it by a scale (quite a big number depending on what your world scale is), and floor that number.

This will give you a low res grid of where things are.

If any of these positions are in the grid position of a turret or any of the surronding 8 squares then do the code to spin the turret or whatever.

That's an idea, Rob. Thanks, folks, I've fused a few ideas together and things are ticking over a lot better.

I was thinking of a solution along the lines of what's already been suggested here so didn't bother replying again... until I had a thought about another way it can be achieved: use a collision sphere. :)

It might be a bit of a hacky way of doing it but it seems to work fairly well - and fast. Why do all the hard work when you can get blitz to do it?! ;)

Here's a demo:

;
; Radial targeting demo using a collision sphere i.e. a bit of a hack :P
;
; By Big10p (A.K.A. Chris Chadwick) 2004
;
; Targets are found for any given turret by placing a sphere (with a radius equal to the turret's
; firing range) directly under the turret, then moving it up to surround the turret, and then seeing
; what enemies were hit in the process.
;
; This method also has the benefit of targeting the closest enemies (posing the greatest threat), as
; the sphere will hit them first.
;
; This demo only operates on a single plane but the same method should still be quite effective in
; targetting enemies above/below, too. For a flying turret, you could also do a check by placing the
; collision sphere above it and moving it downwards. This would find enemies below the turret, then.
;

	Graphics3D 800,600,32
	SetBuffer BackBuffer()

	Type turretT
		Field ent
		Field vx#, vz#
		Field shell, shell_dist#, loaded%
		Field col_type
		Field target, locked
	End Type

	Type gridT
		Field x#,z#
	End Type
		
	WireFrame 0
	AntiAlias 0

	SeedRnd MilliSecs()

	Const GROUND_SCALE#		= 50.0
	Const GROUND_SEG#		= 5.0
	Const TURRETS_PER_SIDE%	= 30
	Const RANGE#			= 30.0	; Turrets' firing range.
	Const RED_COL%			= 1
	Const GREEN_COL%		= 2
	Const LAZER_COL%		= 3
	Const RANGE_COL%		= 4
	
	Global frame_count%
	Global fps%
	Global slowest_fps%
	Global fps_timeout%
	Global frame_time%
	Global slowest_frame%
	Global frame_start%
	fps_timer = CreateTimer(60)
	slowmo% = False
	
	piv = CreatePivot()
	cam = CreateCamera(piv)
	PositionEntity cam,0,60,-60
	PointEntity cam,piv
	
	light = CreateLight(1,cam)
		
	Global ground = CreateCube()
	ScaleMesh ground,GROUND_SCALE,1,GROUND_SCALE
	EntityColor ground,100,100,200
	PositionEntity ground,0,-2,0
	EntityAlpha ground,.4
	m = CreateMirror()
	PositionEntity m,0,-1,0
	
	Global turret_mesh,shell_mesh
	create_meshes()

	; Collision sphere used to check for in-range enemies.
	; (using sphere for debug - could use a pivot, though)
	Global range_sphere = CreateSphere()
	ScaleEntity range_sphere,RANGE,RANGE,RANGE
	EntityType range_sphere,RANGE_COL
	EntityRadius range_sphere,RANGE
	;EntityAlpha range_sphere,.5
	HideEntity range_sphere
	
	init_turrets()
	set_collisions()

	this_turret.turretT = First turretT
	
	
	; --- Main loop ---
	
	While Not KeyHit(1)
		frame_start = MilliSecs()

		TurnEntity piv,0,.1,0

		If KeyDown(205) Then TranslateEntity cam,.5,0,0
		If KeyDown(203) Then TranslateEntity cam,-.5,0,0
		If KeyDown(200) Then TranslateEntity cam,0,0,.5
		If KeyDown(208) Then TranslateEntity cam,0,0,-.5
		If KeyDown(30) Then MoveEntity cam,0,0,.5
		If KeyDown(44) Then MoveEntity cam,0,0,-.5
		If KeyHit(57) Then slowmo = Not slowmo

		act_on_collisions()
		
		; Take it in turns to update each turret's target every frame.
		get_target(this_turret)
		this_turret = After this_turret	
		If this_turret = Null Then this_turret = First turretT

		update_turrets()
		
		UpdateWorld	
		RenderWorld
		
		frame_time = MilliSecs() - frame_start	
		show_info()

		WaitTimer(fps_timer)
		Flip(1)

		If slowmo Then Delay 500
	Wend

	ClearWorld	

	End


;
;
;
Function act_on_collisions()

	For t.turretT = Each turretT
		If EntityCollided(t\shell,GREEN_COL) Or EntityCollided(t\shell,RED_COL)
			t\shell_dist = 0
			t\loaded = True
			HideEntity t\shell
		EndIf
		If EntityCollided(t\ent,GREEN_COL) Or EntityCollided(t\ent,RED_COL)
			t\vx = -t\vx
			t\vz = -t\vz
		EndIf
	Next

End Function


;
;
;
Function update_turrets()

	For t.turretT = Each turretT

			; Turret movement control.
			tx# = EntityX(t\ent,1)+t\vx
			ty# = EntityY(t\ent,1)
			tz# = EntityZ(t\ent,1)+t\vz
			PositionEntity t\ent,tx,ty,tz
			If Abs(tx) > GROUND_SCALE Then t\vx = -t\vx
			If Abs(tz) > GROUND_SCALE Then t\vz = -t\vz

			; Turret aiming control.
			If t\target
				; Consider pitch and yaw in order to aim at targets above/below.
				; (only really need yaw for this demo, though)
				dp# = DeltaPitch(t\ent,t\target)
				dy# = DeltaYaw(t\ent,t\target)
				TurnEntity t\ent,dp/10.0,dy/10.0,0
				If Abs(dp) < 5.0 And Abs(dy) < 5.0 Then t\locked = True
			Else
				TurnEntity t\ent,0,1,0
			EndIf
			
			; Turret firing control.
			If Not t\loaded
				MoveEntity t\shell,0,0,1
				t\shell_dist = t\shell_dist + 1
			EndIf

			If t\locked And t\loaded
				; FIRE!!!!!!!!!
				t\loaded = False
				PositionEntity t\shell,EntityX(t\ent,1),EntityY(t\ent,1),EntityZ(t\ent,1)
				RotateEntity t\shell,EntityPitch(t\ent,1),EntityYaw(t\ent,1),EntityRoll(t\ent,1)
				MoveEntity t\shell,0,0,2.5
				ShowEntity t\shell
				t\shell_dist = 2.5
			EndIf
						
			If t\shell_dist > RANGE
				t\shell_dist = 0
				t\loaded = True
				HideEntity t\shell
			EndIf
			
	Next	

End Function


;
;
;
Function set_collisions()

	ClearCollisions
	Collisions RED_COL,RED_COL,1,1
	Collisions RED_COL,GREEN_COL,1,1
	Collisions GREEN_COL,GREEN_COL,1,1
	Collisions GREEN_COL,RED_COL,1,1
	Collisions LAZER_COL,GREEN_COL,1,1
	Collisions LAZER_COL,RED_COL,1,1
	
End Function


;
;
;	
Function init_turrets()

	w# = GROUND_SCALE*2
	rc = w/GROUND_SEG
	gc = 0

	For row = 1 To rc
		For col = 1 To rc
			g.gridT = New gridT
			g\x = (-(w/2) + (((col-1) * GROUND_SEG) + GROUND_SEG/2))+Rnd(-(GROUND_SEG/3),(GROUND_SEG/3))
			g\z =  ((w/2) - (((row-1) * GROUND_SEG) + GROUND_SEG/2))+Rnd(-(GROUND_SEG/3),(GROUND_SEG/3))
			gc = gc + 1
		Next
	Next
	
	For red = 1 To TURRETS_PER_SIDE
		this.gridT = First gridT
		rg = Rand(1,gc)
		For n = 2 To rg
			this = After this
		Next
		create_turret(RED_COL,this\x,0,this\z)
		Delete this
		gc = gc - 1 
	Next

	For green = 1 To TURRETS_PER_SIDE
		this.gridT = First gridT
		rg = Rand(1,gc)
		For n = 2 To rg
			this = After this
		Next
		create_turret(GREEN_COL,this\x,0,this\z)
		Delete this
		gc = gc - 1
	Next
	
	Delete Each gridT

End Function


;
;
;
Function get_target(t.turretT)

	tx# = EntityX(t\ent,1)
	ty# = EntityY(t\ent,1)
	tz# = EntityZ(t\ent,1)

	If t\col_type = GREEN_COL Then targ_col = RED_COL Else targ_col = GREEN_COL
	
	ClearCollisions
	Collisions RANGE_COL,targ_col,1,1
	PositionEntity range_sphere,tx,ty-RANGE-1,tz
	ShowEntity range_sphere
	PositionEntity range_sphere,tx,ty,tz
	UpdateWorld 0
	new_targ = EntityCollided(range_sphere,targ_col)
	If new_targ <> t\target
		t\locked = False
		t\target = new_targ
	EndIf
		
	HideEntity range_sphere
	set_collisions()
		
End Function


;
;
;
Function create_turret(col_type,x#,y#,z#)

	t.turretT = New turretT
	t\ent = CopyEntity(turret_mesh)
	t\col_type = col_type
	t\vx = Rnd(-.1,.1)
	t\vz = Rnd(-.1,.1)
	t\loaded = True
	t\locked = False
	t\target = 0
	t\shell_dist = 0
	EntityType t\ent,col_type	
	EntityRadius t\ent,1
	PositionEntity t\ent,x,y,z
	t\shell = CopyEntity(shell_mesh)
	HideEntity t\shell
	EntityType t\shell,LAZER_COL
	EntityRadius t\shell,.09
	
	If col_type = GREEN_COL
		EntityColor t\ent,0,100,0
		EntityColor t\shell,0,255,0
	Else
		EntityColor t\ent,100,0,0
		EntityColor t\shell,255,0,0
	EndIf
	
End Function


;
;
;
Function create_meshes()

	turret_mesh = CreateCylinder(16)
	gun = CreateCylinder()
	ScaleMesh gun,.2,1,.2
	RotateMesh gun,-90,0,0
	PositionMesh gun,0,0,1
	AddMesh gun,turret_mesh
	FreeEntity gun
	HideEntity turret_mesh

	shell_mesh = CreateCylinder()
	RotateMesh shell_mesh,-90,0,0
	ScaleMesh shell_mesh,.09,.09,1
	EntityFX shell_mesh,1
	HideEntity shell_mesh	
	
End Function


;
; Display debug info
;
Function show_info()
	
	If fps_timeout
		frame_count = frame_count + 1

		If MilliSecs() > fps_timeout Then
			fps_timeout = MilliSecs() + 1000 
			fps = frame_count 
			frame_count = 0 
		
			If fps < slowest_fps Or slowest_fps = 0 Then slowest_fps = fps
		EndIf 
		
		If frame_time > slowest_frame Then slowest_frame = frame_time
		
		Color 0,255,0
		Text 10,10," Triangles: " + TrisRendered()
		Color 255,255,0
		Text 10,25," Millisecs: " + frame_time
		Text 10,40,"   Slowest: " + slowest_frame
		Color 0,255,255
		Text 10,55,"       FPS: " + fps
		Text 10,70,"     Worst: " + slowest_fps
		Color 255,255,255
		Text 10,100,"Total Turrets: " + TURRETS_PER_SIDE*2
		Text 10,115,"Press ARROW KEYS to move camera."
		Text 10,130,"Press A/Z to zoom camera."
		Text 10,145,"Press SPACE to toggle slowmo."
	Else
		; First call initialization.
		fps_timeout = MilliSecs() + 1000 
	EndIf
	
End Function


big10p, I think a couple of 100 collision spheres all colliding with each other will shurely kill his framerate (havn't read your code, but I presume it's something like that you want do). For a quick cutdown on CPU useage you can replace EntityDistance(). It uses a squareroot that you can avoid, and maybe you will not bother about the y-distance:
If (x1-x2)*(x1-x2)+(z1-z2)*(z1-z2)>=maxdistance*maxdistance Then ...
Mix this with the grid that Rob Farley suggested, I'm shure that'll speed things up.


big10p, I think a couple of 100 collision spheres all colliding with each other will shurely kill his framerate (havn't read your code, but I presume it's something like that you want do).



No, that's not what I'm doing - there's only one collision sphere. As it stands, the demo searches for a new target for 1 turret per frame. I originally had it checking for every turret on every frame - it was still pretty fast but seemed like overkill. :)

I'm sorry to disappoint you, but normal for/next method seems to be faster:
;
; Radial targeting demo using a collision sphere i.e. a bit of a hack :P
;
; By Big10p (A.K.A. Chris Chadwick) 2004
;
; Targets are found for any given turret by placing a sphere (with a radius equal to the turret's
; firing range) directly under the turret, then moving it up to surround the turret, and then seeing
; what enemies were hit in the process.
;
; This method also has the benefit of targeting the closest enemies (posing the greatest threat), as
; the sphere will hit them first.
;
; This demo only operates on a single plane but the same method should still be quite effective in
; targetting enemies above/below, too. For a flying turret, you could also do a check by placing the
; collision sphere above it and moving it downwards. This would find enemies below the turret, then.
;

	Graphics3D 800,600,32
	SetBuffer BackBuffer()

	Type turretT
		Field ent
		Field vx#, vz#
		Field shell, shell_dist#, loaded%
		Field col_type
		Field target, locked
	End Type

	Type gridT
		Field x#,z#
	End Type
		
	WireFrame 0
	AntiAlias 0

	SeedRnd MilliSecs()

	Const GROUND_SCALE#		= 50.0
	Const GROUND_SEG#		= 5.0
	Const TURRETS_PER_SIDE%	= 30
	Const RANGE#			= 30.0	; Turrets' firing range.
	Const range2#=900
	Const RED_COL%			= 1
	Const GREEN_COL%		= 2
	Const LAZER_COL%		= 3
	Const RANGE_COL%		= 4
	Const method=0
				
	Global frame_count%
	Global fps%
	Global slowest_fps%
	Global fps_timeout%
	Global frame_time%
	Global slowest_frame%
	Global frame_start%
	fps_timer = CreateTimer(60)
	slowmo% = False
	
	piv = CreatePivot()
	cam = CreateCamera(piv)
	PositionEntity cam,0,60,-60
	PointEntity cam,piv
	
	light = CreateLight(1,cam)
		
	Global ground = CreateCube()
	ScaleMesh ground,GROUND_SCALE,1,GROUND_SCALE
	EntityColor ground,100,100,200
	PositionEntity ground,0,-2,0
	EntityAlpha ground,.4
	m = CreateMirror()
	PositionEntity m,0,-1,0
	
	Global turret_mesh,shell_mesh
	create_meshes()

	; Collision sphere used to check for in-range enemies.
	; (using sphere for debug - could use a pivot, though)
	Global range_sphere = CreateSphere()
	ScaleEntity range_sphere,RANGE,RANGE,RANGE
	EntityType range_sphere,RANGE_COL
	EntityRadius range_sphere,RANGE
	;EntityAlpha range_sphere,.5
	HideEntity range_sphere
	
	init_turrets()
	set_collisions()

	this_turret.turretT = First turretT
	
	
	; --- Main loop ---
	
	While Not KeyHit(1)
		frame_start = MilliSecs()

		TurnEntity piv,0,.1,0

		If KeyDown(205) Then TranslateEntity cam,.5,0,0
		If KeyDown(203) Then TranslateEntity cam,-.5,0,0
		If KeyDown(200) Then TranslateEntity cam,0,0,.5
		If KeyDown(208) Then TranslateEntity cam,0,0,-.5
		If KeyDown(30) Then MoveEntity cam,0,0,.5
		If KeyDown(44) Then MoveEntity cam,0,0,-.5
		If KeyHit(57) Then slowmo = Not slowmo

		act_on_collisions()
		
		; Take it in turns to update each turret's target every frame.
;		get_target(this_turret)
;		this_turret = After this_turret	
;		If this_turret = Null Then this_turret = First turretT
		For this_turret = Each turrett
			get_target(this_turret)
		Next

		update_turrets()
		
		UpdateWorld	
		RenderWorld
		
		frame_time = MilliSecs() - frame_start	
		show_info()

		WaitTimer(fps_timer)
		Flip(1)

		If slowmo Then Delay 500
	Wend

	ClearWorld	

	End


;
;
;
Function act_on_collisions()

	For t.turretT = Each turretT
		If EntityCollided(t\shell,GREEN_COL) Or EntityCollided(t\shell,RED_COL)
			t\shell_dist = 0
			t\loaded = True
			HideEntity t\shell
		EndIf
		If EntityCollided(t\ent,GREEN_COL) Or EntityCollided(t\ent,RED_COL)
			t\vx = -t\vx
			t\vz = -t\vz
		EndIf
	Next

End Function


;
;
;
Function update_turrets()

	For t.turretT = Each turretT

			; Turret movement control.
			tx# = EntityX(t\ent,1)+t\vx
			ty# = EntityY(t\ent,1)
			tz# = EntityZ(t\ent,1)+t\vz
			PositionEntity t\ent,tx,ty,tz
			If Abs(tx) > GROUND_SCALE Then t\vx = -t\vx
			If Abs(tz) > GROUND_SCALE Then t\vz = -t\vz

			; Turret aiming control.
			If t\target
				; Consider pitch and yaw in order to aim at targets above/below.
				; (only really need yaw for this demo, though)
				dp# = DeltaPitch(t\ent,t\target)
				dy# = DeltaYaw(t\ent,t\target)
				TurnEntity t\ent,dp/10.0,dy/10.0,0
				If Abs(dp) < 5.0 And Abs(dy) < 5.0 Then t\locked = True
			Else
				TurnEntity t\ent,0,1,0
			EndIf
			
			; Turret firing control.
			If Not t\loaded
				MoveEntity t\shell,0,0,1
				t\shell_dist = t\shell_dist + 1
			EndIf

			If t\locked And t\loaded
				; FIRE!!!!!!!!!
				t\loaded = False
				PositionEntity t\shell,EntityX(t\ent,1),EntityY(t\ent,1),EntityZ(t\ent,1)
				RotateEntity t\shell,EntityPitch(t\ent,1),EntityYaw(t\ent,1),EntityRoll(t\ent,1)
				MoveEntity t\shell,0,0,2.5
				ShowEntity t\shell
				t\shell_dist = 2.5
			EndIf
						
			If t\shell_dist > RANGE
				t\shell_dist = 0
				t\loaded = True
				HideEntity t\shell
			EndIf
			
	Next	

End Function


;
;
;
Function set_collisions()

	ClearCollisions
	Collisions RED_COL,RED_COL,1,1
	Collisions RED_COL,GREEN_COL,1,1
	Collisions GREEN_COL,GREEN_COL,1,1
	Collisions GREEN_COL,RED_COL,1,1
	Collisions LAZER_COL,GREEN_COL,1,1
	Collisions LAZER_COL,RED_COL,1,1
	
End Function


;
;
;	
Function init_turrets()

	w# = GROUND_SCALE*2
	rc = w/GROUND_SEG
	gc = 0

	For row = 1 To rc
		For col = 1 To rc
			g.gridT = New gridT
			g\x = (-(w/2) + (((col-1) * GROUND_SEG) + GROUND_SEG/2))+Rnd(-(GROUND_SEG/3),(GROUND_SEG/3))
			g\z =  ((w/2) - (((row-1) * GROUND_SEG) + GROUND_SEG/2))+Rnd(-(GROUND_SEG/3),(GROUND_SEG/3))
			gc = gc + 1
		Next
	Next
	
	For red = 1 To TURRETS_PER_SIDE
		this.gridT = First gridT
		rg = Rand(1,gc)
		For n = 2 To rg
			this = After this
		Next
		create_turret(RED_COL,this\x,0,this\z)
		Delete this
		gc = gc - 1 
	Next

	For green = 1 To TURRETS_PER_SIDE
		this.gridT = First gridT
		rg = Rand(1,gc)
		For n = 2 To rg
			this = After this
		Next
		create_turret(GREEN_COL,this\x,0,this\z)
		Delete this
		gc = gc - 1
	Next
	
	Delete Each gridT

End Function


;
;
;
Function get_target(t.turretT)

	tx# = EntityX(t\ent,1)
	ty# = EntityY(t\ent,1)
	tz# = EntityZ(t\ent,1)

	If t\col_type = GREEN_COL Then targ_col = RED_COL Else targ_col = GREEN_COL
	
	If method=1 Then
		ClearCollisions
		Collisions RANGE_COL,targ_col,1,1
		PositionEntity range_sphere,tx,ty-RANGE-1,tz
		ShowEntity range_sphere
		PositionEntity range_sphere,tx,ty,tz
		UpdateWorld 0
		new_targ = EntityCollided(range_sphere,targ_col)
		If new_targ <> t\target
			t\locked = False
			t\target = new_targ
		EndIf
			
		HideEntity range_sphere
		set_collisions()
	Else
		myvar3#=range2
		For myvar.turrett = Each turrett
			myvar2#=(EntityX(myvar\ent)-tx)*(EntityX(myvar\ent)-tx)+(EntityZ(myvar\ent)-tz)*(EntityZ(myvar\ent)-tz)
			If myvar\col_type=targ_col And myvar2<myvar3# Then
				myvar3#=myvar2
				t\locked = False
				t\target = myvar\ent
			End If
		Next
	End If
		
End Function


;
;
;
Function create_turret(col_type,x#,y#,z#)

	t.turretT = New turretT
	t\ent = CopyEntity(turret_mesh)
	t\col_type = col_type
	t\vx = Rnd(-.1,.1)
	t\vz = Rnd(-.1,.1)
	t\loaded = True
	t\locked = False
	t\target = 0
	t\shell_dist = 0
	EntityType t\ent,col_type	
	EntityRadius t\ent,1
	PositionEntity t\ent,x,y,z
	t\shell = CopyEntity(shell_mesh)
	HideEntity t\shell
	EntityType t\shell,LAZER_COL
	EntityRadius t\shell,.09
	
	If col_type = GREEN_COL
		EntityColor t\ent,0,100,0
		EntityColor t\shell,0,255,0
	Else
		EntityColor t\ent,100,0,0
		EntityColor t\shell,255,0,0
	EndIf
	
End Function


;
;
;
Function create_meshes()

	turret_mesh = CreateCylinder(16)
	gun = CreateCylinder()
	ScaleMesh gun,.2,1,.2
	RotateMesh gun,-90,0,0
	PositionMesh gun,0,0,1
	AddMesh gun,turret_mesh
	FreeEntity gun
	HideEntity turret_mesh

	shell_mesh = CreateCylinder()
	RotateMesh shell_mesh,-90,0,0
	ScaleMesh shell_mesh,.09,.09,1
	EntityFX shell_mesh,1
	HideEntity shell_mesh	
	
End Function


;
; Display debug info
;
Function show_info()
	
	If fps_timeout
		frame_count = frame_count + 1

		If MilliSecs() > fps_timeout Then
			fps_timeout = MilliSecs() + 1000 
			fps = frame_count 
			frame_count = 0 
		
			If fps < slowest_fps Or slowest_fps = 0 Then slowest_fps = fps
		EndIf 
		
		If frame_time > slowest_frame Then slowest_frame = frame_time
		
		Color 0,255,0
		Text 10,10," Triangles: " + TrisRendered()
		Color 255,255,0
		Text 10,25," Millisecs: " + frame_time
		Text 10,40,"   Slowest: " + slowest_frame
		Color 0,255,255
		Text 10,55,"       FPS: " + fps
		Text 10,70,"     Worst: " + slowest_fps
		Color 255,255,255
		Text 10,100,"Total Turrets: " + TURRETS_PER_SIDE*2
		Text 10,115,"Press ARROW KEYS to move camera."
		Text 10,130,"Press A/Z to zoom camera."
		Text 10,145,"Press SPACE to toggle slowmo."
	Else
		; First call initialization.
		fps_timeout = MilliSecs() + 1000 
	EndIf
	
End Function
Set method to 1 for your method, and to 0 for normal distance checks.

A simple method would be to have each turret set its next scan (for closest enemy) time to a random interval in the future, which will help eliminate clumping. (Take the same approach for all costly behavior.) Or to offset the scan times of each turret when they are instanced.

lol, I'm not disappointed at all - or surprised that method is faster.

I already said that I was already thinking of doing it along the line of how Rob H. etc. had suggested. I just thought I'd present a different approach. I did say it was a bit of a hacky way of doing it, too. :)

did anyone see my post? or is there a reason why EntityPick is not useable here?

All those nifty Blitz functions are in general really slow, I don't think EntityPick is an exeption.

Plus, EntityPick only detects other entities 'ahead' of the entity you use it with. You would have to point that entity in every feasible direction to ensure it detected any entities surrounding it. :/

>Each turret performs and EntityDistance on every other
>shootable item in the scene. So if I have 80 buildings and
>10 ships in my scene, a turret attached to a ship has to
>do 90 checks to find the closest. Multiply by three, for
>three turrets on a single ship, and we have 270. Multiply
>by four ships and we have over 1080 Entitydistance checks.

How many of those checks do you expect will yield a different result than the same check did the last time? My guess is that 99% will be yielding the same result, so you should look for a way to not waste checks on those 99% every frame... Divide and conquer.

In stead of checking every turret against all targets, why not set a radius a little larger than you need for the whole ship and then check 1 ship to 1 turret every frame. If they are inside the radius, you put them on a close_to_carrier list. With 4 ships in the water, you would check 4 ship to turret checks every frame and be able to process the list for each carrier also 1 each frame without ever dropping a single frame.

Andy