Posted: 3rd Jun 2021 16:07
hello, some time ago I had a drop in FPS due to my ammunition presence.

dropping from 60 to 30 FPS when there are many and resumes at 60 when the ammunition is disappearing.

my ammunition is a type, which has an object which I move.
If the munitions are active, they calculate possible collisions with other planes or ships.

They only activate, when they are close to another object.

For example, if I fly with my plane alone, and shoot, the ammunition does not calculate collisions since they would not have to hit. And so I save the whole process of detecting whether or not they collide with something.

My ocean is always at height 0, so if its height is negative, I know they fell into the water.


I would like to consult you, what is the best method to have a lot of ammunition and that AppGameKit can handle it without falling.

and what method is better to calculate if a munition had an impact.



share some part of my code, this is when i create a ammo when someone fire.



----------------------------------------------

My idea, for now, is to replace the projectiles with a more effective method, I have everything separated into functions.

- projectile creation
- update projectile
- I calculate if it collides or not
- projectile death

damage calculations and others are in different functions.

it's obvious that I'm doing something wrong, and I lost track of when I started getting this drop in FPS.

Are there ammunition systems for shaders? Or is there a more polished system for this type of need?


----------------------------------------------

thanks for read, , im back to dev again.
regards!

+ Code Snippet
function crear_disparo(origen,tipo$,flag,origen_id)

	m as municion
	m.tipo$ = tipo$

	// estas dos deberia racionalizarlas en una sola, esforzate!
	for count = _molde_municion.length to 0 step-1
		if _molde_municion[count].tipo$ = tipo$
			if _molde_municion[count].obj <> 0
				existe = 1
				//print (" VOY A COPIAR LA SIGUIENTE MUNICIONNNNNNNNNNNNNNNNNNNNNNNNNNNN " + tipo$)
				m.obj = InstanceObject(_molde_municion[count].obj)
			endif
		endif
	next Count

	if existe = 0 //si no existe esa mucnicion, la crea y de paso copia la nueva municion al nuevo disparo
		//print (" NO EXISTE     NO EXISTE     NO EXISTE     NO EXISTE     NO EXISTE     NO EXISTE     NO EXISTE    ")
		crear_molde_municion(m.tipo$)
		for count = _molde_municion.length to 0 step-1
			if _molde_municion[count].tipo$ = tipo$
				if _molde_municion[count].obj <> 0
					existe = 1
					//print (" VOY A COPIAR LA SIGUIENTE MUNICIONNNNNNNNNNNNNNNNNNNNNNNNNNNN " + tipo$)
					m.obj = InstanceObject(_molde_municion[count].obj)
				endif
			endif
		next Count
	endif



	m.origen = origen_id
	m.flag = flag
	pos(m.obj,origen,"crear_disparo")
	rot(m.obj,origen,"crear_disparo")
	m.activa = 0

	m.vel# = 10.4 * 2
	if m.tipo$ = "proyectil aa" then m.vel# = 10
	force_add_vector(m.obj,m.obj,m.vel#,0)

	color = random(0,255)
	setobjectcolor(m.obj,color,color,color,255)

	_muni.insert(m)


endfunction m.obj




+ Code Snippet
function update_muni_proyectil(count,force_n_id, debe_morir)

	//force_n_id = force_getmyforce_n_ID(_muni[count].obj)

	if _muni[count].tipo$ = "proyectil" or _muni[count].tipo$ = "proyectil aa"

		//set_timer("proyectil A",0,3)
		if _muni[count].activa = 0
			if getdistance(cam_pivot,_muni[count].obj) < 100
				_muni[count].activa = 1
			endif
		endif
		//set_timer("proyectil A",1,3)

		//set_timer("proyectil B",0,3)
		x1# = getobjectx(_muni[count].obj)
		y1# = getobjecty(_muni[count].obj)
		z1# = getobjectz(_muni[count].obj)

		force_apply(_muni[count].obj  ,force_n_id)

		//MoveObjectLocalZ(_muni[count].obj,  1)


		//set_timer("proyectil B",1,3)


		if _muni[count].vida > 1 and _muni[count].activa = 1

			// posicion nueva de la municion ?choco algo?
			//set_timer("proyectil C",0,3)
			x2# = getobjectx(_muni[count].obj)
			y2# = getobjecty(_muni[count].obj)
			z2# = getobjectz(_muni[count].obj)
			//set_timer("proyectil C",1,3)

				//set_timer("proyectil D",0,3) //start timer
				//Calcular si va a chocar contra algo
				ObjectRayCast(0,x1#,y1#,z1#,x2#,y2#,z2#)
				n=GetObjectRayCastNumHits()
			//	set_timer("proyectil D",1,3) // end timer

			//	set_timer("proyectil E",0,3)
				for i = 0 to n-1
					victima = getObjectRayCastHitID(i)

					if victima <> 0
Posted: 3rd Jun 2021 16:20
maibe here is the problem, If I cancel this part where it detects collisions, I don't have the FPS drop


+ Code Snippet
function update_muni_proyectil(count,force_n_id, debe_morir)

	//force_n_id = force_getmyforce_n_ID(_muni[count].obj)

	if _muni[count].tipo$ = "proyectil" or _muni[count].tipo$ = "proyectil aa"

		//set_timer("proyectil A",0,3)
		if _muni[count].activa = 0
			if getdistance(cam_pivot,_muni[count].obj) < 100
				_muni[count].activa = 1
			endif
		endif
		//set_timer("proyectil A",1,3)

		//set_timer("proyectil B",0,3)
		x1# = getobjectx(_muni[count].obj)
		y1# = getobjecty(_muni[count].obj)
		z1# = getobjectz(_muni[count].obj)

		force_apply(_muni[count].obj  ,force_n_id)

		//MoveObjectLocalZ(_muni[count].obj,  1)


		//set_timer("proyectil B",1,3)

		
		if _muni[count].vida > 1 and _muni[count].activa = 1     // <<<<<<<<<<<<< HERE START THE PROBLEM

			// posicion nueva de la municion ?choco algo?
			//set_timer("proyectil C",0,3)
			x2# = getobjectx(_muni[count].obj)
			y2# = getobjecty(_muni[count].obj)
			z2# = getobjectz(_muni[count].obj)
			//set_timer("proyectil C",1,3)

				//set_timer("proyectil D",0,3) //start timer
				//Calcular si va a chocar contra algo
				ObjectRayCast(0,x1#,y1#,z1#,x2#,y2#,z2#)
				n=GetObjectRayCastNumHits()
			//	set_timer("proyectil D",1,3) // end timer

			//	set_timer("proyectil E",0,3)
				for i = 0 to n-1
					victima = getObjectRayCastHitID(i)

					if victima <> 0


						for s = _squad.length to 0 step -1
							for airplane = 1 to 16
								if victima = _squad[s].airplane_pivot[airplane]
									obj = GetObjectRayCastHitID(i)
									x#=GetObjectRayCastx(i)
									y#=GetObjectRayCastY(i)
									z#=GetObjectRayCastz(i)
									crear_explo(0,"red",x#,y#,z#,8)
									critical_hit = random(0,100)
									if critical_hit > 30
											crear_explo(0,"red",x#,y#,z#,random(10,20))
									endif
								endif
							next airplane
						next s
					endif



					//colision de municion con BARCOS
					for barco_n =_barco.length to 0 step-1
						if victima = _barco[barco_n].obj and _barco[barco_n].obj <> _muni[count].origen

								obj = GetObjectRayCastHitID(i)
								x#=GetObjectRayCastx(i)
								y#=GetObjectRayCastY(i)
								z#=GetObjectRayCastz(i)
								nx#=GetObjectRayCastNormalX(i)
								ny#=GetObjectRayCastNormalY(i)
								nz#=GetObjectRayCastNormalZ(i)
								tx#=GetScreenXFrom3D(x#,y#,z#)
								ty#=GetScreenYFrom3D(x#,y#,z#)
								crear_explo(0,"red",x#,y#,z#,2)
								last_vida = _barco[barco_n].vida
								_barco[barco_n].vida = _barco[barco_n].vida- random(1,10)*10
								_muni[count].vida = 1000

								critical_hit = random(0,100)
								if critical_hit > 90
									crear_splash(_muni[count].obj,random(20,60),"explo")
									crear_explo(0,"red",x#,y#,z#,random(10,20))
								endif

								// points for hit
								if _muni[count].flag = _barco[barco_n].flag

									send_radio_msg(_barco[barco_n].tipo$ ,msg_origen_txt_by_id(_muni[count].origen),"CEASE FIRE!!!! you hit one of our ships!!!!",_muni[count].flag)

									// friendli fire!!!   NEGATIVE POINTS
									// hit an friendly ship
									points = -10
									// kill an friendly ship!!
									if _barco[barco_n].vida <= 0
										 points = -5000
											send_radio_msg(_barco[barco_n].tipo$,msg_origen_txt_by_id(_muni[count].origen),"You  have just sunk a ship of your own side!",_muni[count].flag)

									endif
									shooter = _muni[count].origen
									add_points(shooter ,points)

									else


									// Hit points, you hit a enemy aircraft
									points = 5
									// kill an enemy ship!!
									if _barco[barco_n].vida <= 0 and last_vida > 0
										_human[player_human_id].ships_sink = _human[player_human_id].ships_sink + 1
										save_humans()
										points = 1000

											send_radio_msg(msg_origen_txt_by_id(_muni[count].origen),"","Enemy  "+ _barco[barco_n].tipo$ + " ship destroyed!!!",_muni[count].flag)


									endif
									shooter = _muni[count].origen
									add_points(shooter ,points)
								endif
						endif
					next barco_n

					//colision de municion contra otros aviones
					for avion_n = _avion.length to 0 step-1



						if victima = _avion[avion_n].lowpoly and  _avion[avion_n].obj <>  _muni[count].origen
							 obj = GetObjectRayCastHitID(i)

								//if _avion[avion_n].control$ = "player" or _muni[count].origen = player_avion_obj_id
									check_3d_damage(avion_n, -1, count)
								//endif


								x#=GetObjectRayCastx(i)
								y#=GetObjectRayCastY(i)
								z#=GetObjectRayCastz(i)
								crear_explo(0,"red",x#,y#,z#,5)
								last_vida =  _avion[avion_n].vida
								_avion[avion_n].vida = _avion[avion_n].vida - random(1,10)*30
								_muni[count].vida = 1000

								//points for hit
								if _muni[count].flag = _avion[avion_n].flag

									send_radio_msg(msg_origen_txt_by_id(_avion[avion_n].obj) ,msg_origen_txt_by_id(_muni[count].origen),"CEASE FIRE!!!! you hit a friendly aircraft!!!!",_avion[n].flag)


									// friendli fire!!!   NEGATIVE POINTS
									points = -10
									if _avion[avion_n].vida <= 0
										 points = -1000
									endif
									shooter = _muni[count].origen
									add_points(shooter ,points)
									else
									// Hit points, you hit a enemy aircraft
									points = 5
									if _avion[avion_n].vida <= 0 and last_vida > 0
										 points = 500
										_human[player_human_id].airplanes_down = _human[player_human_id].airplanes_down + 1
										send_radio_msg(msg_origen_txt_by_id(_muni[count].origen),"","Enemy " + _avion[avion_n].tipo$ + " aircraft destroy! ",_muni[count].flag)

									endif
									shooter = _muni[count].origen
									add_points(shooter ,points)
								endif
						endif
					next
					// here i SUM THE NEW POINTS FOR HITING!
					origen_points = origen_points + points
				next

				//set_timer("proyectil E",1,3)

		endif

		//set_timer("proyectil F",0,3)
		MoveObjectLocalZ(_muni[count].obj,-2) // ??? y esto??
		_muni[count].vida = _muni[count].vida + 1
		//set_timer("proyectil F",0,3)

	endif

endfunction debe_morir
Posted: 3rd Jun 2021 21:48
For best Performance you want to avoid Arrays with > 2,500 Elements
You are also going to want to use REPEAT...UNTIL or WHILE...ENDWHILE over FOR...NEXT., I don't know why but the FOR...NEXT Loop is about 20% Slower than the Conditional Branch Loops; or of course you could use DO...LOOP with a Conditional "EXIT"... not tested the performance for that., but I'd imagine it should be pretty fast.

Another thing to note is while IF...ENDIF has reasonable performance., any Conditional with an ELSE has a noticeable performance hit.
If possible try to use Branchless Conditionals (i.e. Binary True / False) with a Finite State Machine (SELECT...CASE)., this provides the best performance especially with Recursive Code (i.e. Loops of large volumes of Data)

Now the last thing to consider is that the Physics Engine is SLOW, especially with Ray Casting.
I'm not sure why, as it should be SSE / VMX Accelerated; but yeah the performance is somewhat dire... so really you want to performance distance calculations., in-fact a Simplified BVH (Binary Volume Hierarchy) is best; which is a fancy way of saying you want to break down the problem to where you're creating virtual boxes and instead check to see if bullets are within those Volumes; then check the Vector to see if any will pass through the Box containing the Target Collider (Object).
ONLY then do you perform the Ray Cast to check to see if a Collision has occurred.

It's one of the downsides of AppGameKit Script being well a Scripting Engine, and not a very well optimised one at that... is for more ambitious things, like Bullet Hell Shooters for example; you have to be a bit more creative to get Decent Performance., which I'd argue somewhat ends up going against the Concept of AppGameKit being more Approachable and New Programmer Friendly.
But then AppGameKit is contradictive in so many ways.
Posted: 3rd Jun 2021 22:21
I would suggest the following;
1) Do odds and evens. Odd array elements one frame and even the next. You could split this up further as well
2) Add a broad proximity check beore you go raycasting
Posted: 3rd Jun 2021 23:43
"You are also going to want to use REPEAT...UNTIL or WHILE...ENDWHILE over FOR...NEXT., I don't know why but the FOR...NEXT Loop is about 20% Slower than the Conditional Branch Loops" - Raven

I did not know that. Thank you!
Posted: 4th Jun 2021 1:00
Quick loop test results (counting to 10,000,000)

Classic:


Studio:


Didn't expect Do/Loop to be the slowest (64-bit compiles, if it matters?)

+ Code Snippet
// Project: Loops 
// Created: 21-06-03

// show all errors

SetErrorMode(0)

// set window properties
SetWindowTitle( "Loops" )
SetWindowSize( 1024, 768, 0 )
SetWindowAllowResize( 1 ) // allow the user to resize the window

// set display properties
SetVirtualResolution( 1024, 768 ) // doesn't have to match the window
SetOrientationAllowed( 1, 1, 1, 1 ) // allow both portrait and landscape on mobile devices
SetSyncRate( 30, 0 ) // 30fps instead of 60 to save battery
SetScissor( 0,0,0,0 ) // use the maximum available screen space, no black borders
UseNewDefaultFonts( 1 )

Last = 10000000
Start# = Timer()
For x = 1 to Last

Next x
ForTime# = Timer()-Start#

x = 0
Start# = Timer()
Repeat
 INC x
Until x = Last
RepeatTime# = Timer()-Start#

x = 0
Start# = Timer()
While x < Last
  INC x
EndWhile
WhileTime# = Timer()-Start#

x = 0
Start# = Timer()
Do
  INC x
  If x = Last then Exit
Loop
DoTime# = Timer()-Start#

x = 0
Start# = Timer()
_MyLoop:
INC x
If x < Last then Goto _MyLoop
GoTimer# = Timer() - Start#


do
	Print( "For:" + STR(ForTime#))
	Print("Repeat:"+STR(RepeatTime#))
	Print("While:"+STR(WhileTime#))
	Print("Do:"+STR(DoTime#))
	Print("GoTo:"+STR(GoTimer#))

    Sync()
loop
Posted: 4th Jun 2021 13:15
You are also going to want to use REPEAT...UNTIL or WHILE...ENDWHILE over FOR...NEXT., I don't know why but the FOR...NEXT Loop is about 20% Slower than the Conditional Branch Loops


Cannot confirm 100%...
REPEAT is the fastest loop in both Studio and Classic
WHILE is faster than FOR in Studio, but MUCH SLOWER in Classic

Win 10 64bit
I-7 2600K
Posted: 4th Jun 2021 18:42
Have you tried making your ammunition objects invisible, so that they're there for the calculations, but not actually drawn to the screen?
Posted: 5th Jun 2021 0:42
Have you tried making your ammunition objects invisible, so that they're there for the calculations, but not actually drawn to the screen?


Drawing Objects actually has a pretty negligible performance hit., of course GPU / CPU Dependant; but typically unless you're drawing A LOT of High Polygon Objects; you're going to hit Script Performance Limits LONG before you hit Rendering Performance Limits.
As I noted in my original response., essentially the Ray-Casting (or anything to do with the built-in Physics Engine) will simply destroy performance.

AGK uses Bullet Physics., which is an excellent Physics Engine... but at the same time, it also doesn't support 64bit, Multi-Core or Multi-Threading... meaning it's running on a Single Core / Thread with 32bit Operations.
While this is "Fine" for Physics Engines like Open Dynamics Engine and PhysX., which were developed and designed when Uni-Core 32-bit Processors were still Commonplace., Bullet Physics was instead developed by Sony for the PlayStation 3.
This means the backend has a HEAVY reliance on Multi-Threading across multiple SIMD Units for acceleration.

While there is a "Software" Fallback that can use minimal resources., it was never really intended for Retail Products; but rather as a baseline for when porting to New Platforms.
A major failing of AppGameKit is that TGC seems highly reluctant to abandon what are essentially obsolete Mobile Platforms., and while this to a degree is "Okay" for say AppGameKit Classic (as v2 tends to get called)., I think this was a major mis-step when it comes to AppGameKit Studio; and would've been an excellent way to differentiate them by increasing the Minimum Specifications; and working to more Modern Architecture considerations.

?

As a result., you have to be exceptionally frugal with the number of Rays, Dynamic Objects, etc. that you have the Physics System handling.
Ideally speaking you want to use it as little as possible., or even better use a more bespoke solution that entirely removes it's usage.
With the performance it has; and again this isn't a fault of Bullet Physics, it is an excellent, capable and performant Physics Engine when properly implemented... it more or less feels like it's there to allow for a Feature Checkbox to be ticked off., and is useful in a more limited scope.

For something that uses A LOT of entities., I'd instead suggest relying exclusively on just a basic Distance Check; with a prescribed Distance denoting the "Collision" Area; so if it's within a given Distance you have a Hit, and if not; you don't.
On top of this breaking the problem down into a BVH Tree; or even just a simple Octree to perform more general distance checks and then only actually perform a check and compare on those within a proximity Volume; would again allow for much larger numbers of Entities without any real performance hits.
Posted: 6th Jun 2021 15:19
hello, thank you very much for the answers!

Some things I do not understand, since being self-taught and gross I find it difficult to understand a little things that are surely basic.

From what I see, raycast is a slow function, both from what they say, and also that if I cancel that function, the performance of the game does not drop.

so it is obvious that there I have the problem.

the main solution is to have distance detection.
and only use the raycast on a few special occasions.

in my case, my planes have "objects" of impacts, to detect if you hit the engine, the wing, the cockpit, the ailerons, etc.

but I could use that, which I get using raycast, only for very special occasions.

also used RAYCAST for the collisions of the plane when it passes near a ship.

I must evaluate well how to approach the general solution of all this.

since the yield is something non-negotiable.

regards!


What I can do is add the usage counters that I have, to see how many times I use raycast, and how much time it takes.

and so know how much I can use it.
Posted: 7th Jun 2021 13:27
Santiago., you may have to test this... but I don't believe AppGameKit will automatically generate and use (Bounding) Box Collision for your Objects.
It instead likely uses Polygon (Triangle) Collision., which is great for Accuracy but not so good for Performance.

Let's say you're using a Low Triangle (Polygon) Count Model., like maybe 500 Triangles... even if we were to use Triangular Collision., a Box Representation of the Object is going to be substantially Faster to Calculation when Collision occurs as it made up from 12 Triangles.
Even still., Box Collision is typically even faster still; because it doesn't NEED to be a Comprehensive approach, where each individual Triangle has to be Calculated and Intersected to Detect if Collision has occurred.
Instead a more simplified approach of "Is the Ray within this Volume?" needs to be done, which is essentially half a dozen calculations and binary checks as opposed to Triangular Collision which is several hundred calculations with branching checks.

And realistically., we don't actually need to rely on Ray Casting to even handle said Collision; a more simplified Distance Calculation and then Volume Check can be utilised.
I'd suggest experimenting with that and work from there.
If it helps I can see about writing some code to showcase this and draw some pictures that explain what's happening.

There's nothing wrong with being "Self Taught", so don't fret about that... you'll likely find more than a few here have been there too.
Doing new things is always going to be a learning experience., the best programmers are those who are always open to learning; as there is never a "Perfect" or "Right" way to do something... and more often than not; it's a case of Trial & Error until you get the results you hope / want.
Posted: 9th Jun 2021 18:55
Thanks for the answers.

i have this function to messure distances

+ Code Snippet
function getdistance(origen,destino)

	//Returns the distance between the start point for the ray and the collision point.
	//float GetObjectRayCastDistance( index )

	//x1, y1, z1 - x, y, z coordinates of the first object
	//x2, y2, z2 - x, y, z coordinates of the second object

	if origen <> 0 and destino <> 0
		if origen = cam
			x1# = GetCameraworldx(cam)
			y1# = GetCameraworldy(cam)
			z1# = GetCameraworldz(cam)
				else
			x1# = getobjectworldx(origen)
			y1# = getobjectworldy(origen)
			z1# = getobjectworldz(origen)
		endif

		if destino = cam
			x2# = GetCameraworldx(cam)
			y2# = GetCameraworldy(cam)
			z2# = GetCameraworldz(cam)
			else
			x2# = getobjectworldx(destino)
			y2# = getobjectworldy(destino)
			z2# = getobjectworldz(destino)
		endif
	endif

	//dist# = sqrt(pow(x1# - x2#, 2) + pow(y1# - y2#, 2) + pow(z1# - z2#, 2))

	dist#=sqrt(((x2#-x1#)*(x2#-x1#))+((y2#-y1#)*(y2#-y1#))+((z2#-z1#)*(z2#-z1#)))

endfunction dist#




you could use it to detect projectiles near potential targets.

as if it were a collision of sphere to sphere.

since the distance from one point to another would be like the radius, if it is within the radius I am within the sphere.


reading the answers, I also think that maybe it would be faster if I make 3 conditions to detect if two objects are close, in the shape of a rectangle.


something like this to check if oneobject is near to other very fast?
if abs (x1 - x2) <R then
if abs (y1 - y2) <R then
if abs (z1 - z2) <R then
// box collision detected
endif
endif
endif

What do you think? Both options are fast?
in this way I would have 3 if that would be faster than a combined condition

and could have a point-sphere or box sector collision
Posted: 9th Jun 2021 20:28
reading the answers, I also think that maybe it would be faster if I make 3 conditions to detect if two objects are close, in the shape of a rectangle.

It may be best to create a sample test program to see which is fastest but it looks like your making great progress looking forward to seeing more
Posted: 9th Jun 2021 21:46
I would divide the area into a grid. Then check cells for proximity
Posted: 16th Jun 2021 13:51
My knowledge of BASIC tanks when the English is changed to a different language, sorry.

My ocean is always at height 0, so if its height is negative, I know they fell into the water.




"

Sounds like you have it figured out.

Looking forward to playing this when it is finished.
Posted: 24th Jul 2021 6:24
From what I've read so far, a good solution is to use bounding boxes to test whether a round is even within proximity to something that it could hit, then use a distance squared method (since you don't need the accurate distance) to test if it is a hit, then move on to testing specific hit points on the aircraft/ship. You could even arrange hitpoints the same way using bounding boxes and that would limit the use of dot product and raycasting. Also, test for bounding box hits and add those to an array for processing on the next frame. Then test for distance and add those to a processing array for the next frame, continuing on splitting things up so that the frame rate doesn't dip.