hi, well im not a programmer and have a little time for working on my project so i couldn't figure out this problem.

for performance issues i did a lot of things and finaly reach the fixed 60 and above fps for pc, but i cant get the same performance from mobile (middle end device, not that bad).

so, i know a lot of people say that object pooling its not necessity for godot, but there are also people that show results (with data) that some times pooling give you performance you need. and project i working on it have a lot of bullet create and death cycle.

around 100-150 per second bullets are created and freed from tree. and its good, but only at pc.

so even tho i know i will strugle i start to object pooling part, its my last resort, seriously, i dont know what to do anymore for get the 60 fps from a mobile device. ( i did a lot of optimization work, generaly on the project. collisions, mesh sizes, reduce the computing stuff etc. )

any way, this is what im came up with:

  • i have a auotoloaded script named Global, that include the function that instance the bullets at the start of the runtime.
  • and another script that become parent of the bullets and handle the number of the bullets that should be instanced.
  • and another script that fire the bullets, and array stuff.

this is auotoloaded script Global:

var muzzle_1_fire_bullet_array :Array = []
var muzzle_1_fire_active_bullet : Array = []
var muzzle_1_fire_b 

func muzzle_1_fire_b_instance(count,add_child_node):
	for instance in range (count):
		muzzle_1_fire_b = preload("res://scenes/bullet.tscn").instance()
		add_child_node.add_child(muzzle_1_fire_b)
		muzzle_1_fire_b.global_transform.origin = Vector3(instance * 3 ,4,instance * 6)
		muzzle_1_fire_bullet_array.append(muzzle_1_fire_b)
		muzzle_1_fire_b.shoot = false

this one for bullet handler and parent of the instanced bullets:

extends Spatial


func _ready():
	Global.muzzle_1_fire_b_instance(20,self)

and this one is belong the player script, the one that shoot the already instanced but inactive bullets:

## muzzle 1
	func _on_fire_delay_timeout():
		if Input.is_action_pressed("fire"):

			var summoned_bullet = Global.muzzle_1_fire_bullet_array.pop_back()
			summoned_bullet.global_transform.origin = muzzle1.global_transform.origin
			Global.muzzle_1_fire_active_bullet.push_back(summoned_bullet)

			var active_bullet = Global.muzzle_1_fire_active_bullet[-1]
			active_bullet.shoot = true
			active_bullet.set_process(true)
			active_bullet.look_at(transform.origin,Vector3.UP)

this is works fine like this:

problem is append the bullets that gone outside of the screen,
for that i did add a VisibilityNotifiernode to bullet scene and use the signalnamed screen_exited.

but no mater what i did, i couldn't reach the bullet that gone to outside of the screen.

i mean, exactly bullet that inside of the active bullet array(muzzle_1_fire_active_bullet) gone the outside of the screen, i need to reach that bullet and remove it from active bullet array (muzzle_1_fire_active_bullet), after that add the same bullet the deactive bullet array again(muzzle_1_fire_bullet_array). you guys know the drill.

i mean i dont know how to work with arrays with more efficient way, like i said im not a programmer, so this maybe a noob question, but i need help. i know the problem, and solution but dont know the syntax i suppose.

  • i change few things.

    this is auotoloaded script Global:

    var muzzle_fire_bullet_pool :Array = []
    var muzzle_fire_active_bullet : Array = []
    var muzzle_fire_bullet

    and this one script handdle the all pooling stuff,

    ## instance the bullets
        func _ready():
    	muzzle_fire_bullet_instance(60,self)
    
    
    ## here instancing bullets and add them pool array named 'muzzle_fire_bullet_pool' and make their process false so they dont do anything.
    ## bullet instance settings
    func muzzle_fire_bullet_instance(count,add_child_node):
    	for instance in range (count):
    		Global.muzzle_fire_bullet = preload("res://sahneler/bullet.tscn").instance()
    		add_child_node.add_child(Global.muzzle_fire_bullet)
    		Global.muzzle_fire_bullet.global_transform.origin = Vector3(instance + 400 ,10,instance * 10)
    		Global.muzzle_fire_bullet_pool.append(Global.muzzle_fire_bullet)
    		Global.muzzle_fire_bullet.shoot = false
    		Global.muzzle_fire_bullet.global_transform.origin.y = 10
    		Global.muzzle_fire_bullet.hide()
    		
    
    		
    
    	## here call a bullet from pool. i call the last element so that array will not arrange every instanced bullets id again and again. 'pop_back() ' 
    	##after that add that bullet the active bullet array named  'muzzle_fire_active_bullet' and move it to muzzle1 position. i have a 6 diffrent muzzle attact the player that fire bullets. so i did use same code with 6 difrent muzzle.
    ## active bullets muzzle_1
    func add_to_active_bullet_m1_fb():
    	var summoned_bullet = Global.muzzle_fire_bullet_pool.pop_back()
    	summoned_bullet.global_transform.origin = muzzle1.global_transform.origin
    	Global.muzzle_fire_active_bullet.push_back(summoned_bullet)
    
    
    
    
    ## active bullets muzzle_2
    func add_to_active_bullet_m2_fb():
    	var summoned_bullet = Global.muzzle_fire_bullet_pool.pop_back()
    	summoned_bullet.global_transform.origin = muzzle2.global_transform.origin
    	Global.muzzle_fire_active_bullet.push_back(summoned_bullet)
    #	summoned_bullet.global_transform.origin.y = 4
    
    	
    ## active bullets muzzle_3
    func add_to_active_bullet_m3_fb():
    	var summoned_bullet = Global.muzzle_fire_bullet_pool.pop_back()
    	summoned_bullet.global_transform.origin = muzzle3.global_transform.origin
    	Global.muzzle_fire_active_bullet.push_back(summoned_bullet)
    #	summoned_bullet.global_transform.origin.y = 4
    
    	
    ## active bullets muzzle_4
    func add_to_active_bullet_m4_fb():
    	var summoned_bullet = Global.muzzle_fire_bullet_pool.pop_back()
    	summoned_bullet.global_transform.origin = muzzle4.global_transform.origin
    	Global.muzzle_fire_active_bullet.push_back(summoned_bullet)
    #	summoned_bullet.global_transform.origin.y = 4
    
    	
    ## active bullets muzzle_5
    func add_to_active_bullet_m5_fb():
    	var summoned_bullet = Global.muzzle_fire_bullet_pool.pop_back()
    	summoned_bullet.global_transform.origin = muzzle5.global_transform.origin
    	Global.muzzle_fire_active_bullet.push_back(summoned_bullet)
    #	summoned_bullet.global_transform.origin.y = 4
    
    	
    ## active bullets muzzle_6
    func add_to_active_bullet_m6_fb():
    	var summoned_bullet = Global.muzzle_fire_bullet_pool.pop_back()
    	summoned_bullet.global_transform.origin = muzzle6.global_transform.origin
    	Global.muzzle_fire_active_bullet.push_back(summoned_bullet)
    #	summoned_bullet.global_transform.origin.y = 4
    	
    
    	##here bullets got active, i just call the last bullet got added the active bullet array, and that bullet allready  at the muzzle position, so what left is just make the process true for that bullet, and other little position stuf.
    ## bullet movments 
    func movment_of_active_fire_bullet():
    	var active_bullet = Global.muzzle_fire_active_bullet[-1]
    	active_bullet.shoot = true
    	active_bullet.set_physics_process(true)
    	active_bullet.look_at(player.transform.origin,Vector3.UP)
    #	active_bullet.transform.origin.y = 4
    	active_bullet.show()
    	active_bullet.transform.origin.y = muzzle1.global_transform.origin.y
    
    	##this timer watch the lifetime of the bullet, if bullet do not collide with enemy after 1 second from actived ##process, its call below function and deactivite itself.
    	var timer = Timer.new()
    	add_child(timer)
    	timer.set_wait_time(1)
    	timer.one_shot = true
    	timer.connect("timeout",self,"add_to_deactive_fire_bullet",[active_bullet])
    	timer.start()
    	x_counter = 0
    	z_counter = 0
    
    
    ##first fired active bullet (its means also oldest and first added the active bullet array) called and removed from acive bullet array 'pop_front()', and added the bullet pool named muzzle_fire_bullet_pool. 'push_back()'
    ##and process turn false again. and make sure that bullets do not touch each other when they in the pool, so i add a counter so every bullet have 5 unit space between each other.
    ## deactive bullets
    func add_to_deactive_fire_bullet(active_bullet):
    	Global.muzzle_fire_active_bullet.pop_front()
    	Global.muzzle_fire_bullet_pool.push_back(active_bullet)
    	active_bullet.set_physics_process(false)
    	x_counter += 5
    	z_counter += 5
    	active_bullet.set_translation(Vector3(400 + x_counter , 15 , 400 + z_counter)) 
    #	print ("adding to deactive : " ,Global.muzzle_fire_bullet_pool.size())
    	active_bullet.hide()


    and this script fire the bullets:

    ##call the functions from node that pooling functions in it.
    ## active bullet and its movment called for every muzzle. i didnt write all of it here, well they all are same anyway.
    ## muzzle 1
    func _on_fire_delay_timeout():
    	if first_m_bullet_sellect.socked_1_fire_b_sellected == true:
    		if Input.is_action_pressed("fire"):
    			muzzle_fire_bullet_pool.add_to_active_bullet_m1_fb()
    			muzzle_fire_bullet_pool.movment_of_active_fire_bullet()

    and lastly bullet script. (area node)

            	##and bullet movment and etc.etc.
    func _ready():
    	set_as_toplevel(true)
    	set_physics_process(false)
    
    
    func _physics_process(_delta):
    	if shoot:
    		transform.origin.y = 4
    		translation -= -transform.basis.z
    
    
    
    ## enemy 1 damage 
    func _on_bullet_body_entered(body):
    	 ### damage ###
    	if body.is_in_group("enemy"):
    		body.health -= damage
    		enemy_got_hit_sound.play()
    		body.dissolve_ani.play("e1_dissolve_shader_ani")
    		body.state = body.bounce
    		body.e1_hp_bar.visible = true
    		body.e1_hp_bar_visible_timer.start()
    		hide()
    		set_physics_process(false)
    	if body.is_in_group("enemy_3"):
    		body.health -= damage
    		enemy_got_hit_sound.play()
    		hide()
    		set_physics_process(false)

    and yes, the gameplay that i have in my mind can be played with this performance.
    my phone is middle end device, and its run the game 55-60 fps around 10 minutes no performance lost. ( i didnt wait more so...)

    bdw bullets also have cpu particle in them, if they dont have that, performances return is fix 60fps on phone and pc. if i remove the fps cap, its return 200+ fps.
    i think i gonna progres with this...
    this is output..

i think, i solve it. After add a few things, polish and test it, i will share it.

10 days later

i change few things.

this is auotoloaded script Global:

var muzzle_fire_bullet_pool :Array = []
var muzzle_fire_active_bullet : Array = []
var muzzle_fire_bullet

and this one script handdle the all pooling stuff,

## instance the bullets
    func _ready():
	muzzle_fire_bullet_instance(60,self)


## here instancing bullets and add them pool array named 'muzzle_fire_bullet_pool' and make their process false so they dont do anything.
## bullet instance settings
func muzzle_fire_bullet_instance(count,add_child_node):
	for instance in range (count):
		Global.muzzle_fire_bullet = preload("res://sahneler/bullet.tscn").instance()
		add_child_node.add_child(Global.muzzle_fire_bullet)
		Global.muzzle_fire_bullet.global_transform.origin = Vector3(instance + 400 ,10,instance * 10)
		Global.muzzle_fire_bullet_pool.append(Global.muzzle_fire_bullet)
		Global.muzzle_fire_bullet.shoot = false
		Global.muzzle_fire_bullet.global_transform.origin.y = 10
		Global.muzzle_fire_bullet.hide()
		

		

	## here call a bullet from pool. i call the last element so that array will not arrange every instanced bullets id again and again. 'pop_back() ' 
	##after that add that bullet the active bullet array named  'muzzle_fire_active_bullet' and move it to muzzle1 position. i have a 6 diffrent muzzle attact the player that fire bullets. so i did use same code with 6 difrent muzzle.
## active bullets muzzle_1
func add_to_active_bullet_m1_fb():
	var summoned_bullet = Global.muzzle_fire_bullet_pool.pop_back()
	summoned_bullet.global_transform.origin = muzzle1.global_transform.origin
	Global.muzzle_fire_active_bullet.push_back(summoned_bullet)




## active bullets muzzle_2
func add_to_active_bullet_m2_fb():
	var summoned_bullet = Global.muzzle_fire_bullet_pool.pop_back()
	summoned_bullet.global_transform.origin = muzzle2.global_transform.origin
	Global.muzzle_fire_active_bullet.push_back(summoned_bullet)
#	summoned_bullet.global_transform.origin.y = 4

	
## active bullets muzzle_3
func add_to_active_bullet_m3_fb():
	var summoned_bullet = Global.muzzle_fire_bullet_pool.pop_back()
	summoned_bullet.global_transform.origin = muzzle3.global_transform.origin
	Global.muzzle_fire_active_bullet.push_back(summoned_bullet)
#	summoned_bullet.global_transform.origin.y = 4

	
## active bullets muzzle_4
func add_to_active_bullet_m4_fb():
	var summoned_bullet = Global.muzzle_fire_bullet_pool.pop_back()
	summoned_bullet.global_transform.origin = muzzle4.global_transform.origin
	Global.muzzle_fire_active_bullet.push_back(summoned_bullet)
#	summoned_bullet.global_transform.origin.y = 4

	
## active bullets muzzle_5
func add_to_active_bullet_m5_fb():
	var summoned_bullet = Global.muzzle_fire_bullet_pool.pop_back()
	summoned_bullet.global_transform.origin = muzzle5.global_transform.origin
	Global.muzzle_fire_active_bullet.push_back(summoned_bullet)
#	summoned_bullet.global_transform.origin.y = 4

	
## active bullets muzzle_6
func add_to_active_bullet_m6_fb():
	var summoned_bullet = Global.muzzle_fire_bullet_pool.pop_back()
	summoned_bullet.global_transform.origin = muzzle6.global_transform.origin
	Global.muzzle_fire_active_bullet.push_back(summoned_bullet)
#	summoned_bullet.global_transform.origin.y = 4
	

	##here bullets got active, i just call the last bullet got added the active bullet array, and that bullet allready  at the muzzle position, so what left is just make the process true for that bullet, and other little position stuf.
## bullet movments 
func movment_of_active_fire_bullet():
	var active_bullet = Global.muzzle_fire_active_bullet[-1]
	active_bullet.shoot = true
	active_bullet.set_physics_process(true)
	active_bullet.look_at(player.transform.origin,Vector3.UP)
#	active_bullet.transform.origin.y = 4
	active_bullet.show()
	active_bullet.transform.origin.y = muzzle1.global_transform.origin.y

	##this timer watch the lifetime of the bullet, if bullet do not collide with enemy after 1 second from actived ##process, its call below function and deactivite itself.
	var timer = Timer.new()
	add_child(timer)
	timer.set_wait_time(1)
	timer.one_shot = true
	timer.connect("timeout",self,"add_to_deactive_fire_bullet",[active_bullet])
	timer.start()
	x_counter = 0
	z_counter = 0


##first fired active bullet (its means also oldest and first added the active bullet array) called and removed from acive bullet array 'pop_front()', and added the bullet pool named muzzle_fire_bullet_pool. 'push_back()'
##and process turn false again. and make sure that bullets do not touch each other when they in the pool, so i add a counter so every bullet have 5 unit space between each other.
## deactive bullets
func add_to_deactive_fire_bullet(active_bullet):
	Global.muzzle_fire_active_bullet.pop_front()
	Global.muzzle_fire_bullet_pool.push_back(active_bullet)
	active_bullet.set_physics_process(false)
	x_counter += 5
	z_counter += 5
	active_bullet.set_translation(Vector3(400 + x_counter , 15 , 400 + z_counter)) 
#	print ("adding to deactive : " ,Global.muzzle_fire_bullet_pool.size())
	active_bullet.hide()


and this script fire the bullets:

##call the functions from node that pooling functions in it.
## active bullet and its movment called for every muzzle. i didnt write all of it here, well they all are same anyway.
## muzzle 1
func _on_fire_delay_timeout():
	if first_m_bullet_sellect.socked_1_fire_b_sellected == true:
		if Input.is_action_pressed("fire"):
			muzzle_fire_bullet_pool.add_to_active_bullet_m1_fb()
			muzzle_fire_bullet_pool.movment_of_active_fire_bullet()

and lastly bullet script. (area node)

        	##and bullet movment and etc.etc.
func _ready():
	set_as_toplevel(true)
	set_physics_process(false)


func _physics_process(_delta):
	if shoot:
		transform.origin.y = 4
		translation -= -transform.basis.z



## enemy 1 damage 
func _on_bullet_body_entered(body):
	 ### damage ###
	if body.is_in_group("enemy"):
		body.health -= damage
		enemy_got_hit_sound.play()
		body.dissolve_ani.play("e1_dissolve_shader_ani")
		body.state = body.bounce
		body.e1_hp_bar.visible = true
		body.e1_hp_bar_visible_timer.start()
		hide()
		set_physics_process(false)
	if body.is_in_group("enemy_3"):
		body.health -= damage
		enemy_got_hit_sound.play()
		hide()
		set_physics_process(false)

and yes, the gameplay that i have in my mind can be played with this performance.
my phone is middle end device, and its run the game 55-60 fps around 10 minutes no performance lost. ( i didnt wait more so...)

bdw bullets also have cpu particle in them, if they dont have that, performances return is fix 60fps on phone and pc. if i remove the fps cap, its return 200+ fps.
i think i gonna progres with this...
this is output..

8 months later