Reputation: 13
I am working on a system which spawns 8 new projectiles after a bullet hits an enemy (an area2D touches another area2D). However, the game loses about 10 FPS for a moment after these projectiles spawn.
Note: I have tried to make the projectiles spawn using different triggers (Timers and process functions), and none of them have resulted in any drops in performance.
This is the code I use for the enemy:
func _ready():
shooter.projectile = preload("res://Projectiles/Planet/LavaBall/LavaBall.tscn")
func _on_Planet_area_entered(area):
if("PlayerProjectile" in area.name):
if (randi() % 100 > 30):
shoot_burst()
func shoot_burst():
fire_projectile_burst(8, 30)
func fire_projectile_burst(projectile, amount: int, spread: int):
var begin_direction = fire_angle
fire_angle = fire_angle - ceil(spread/2)
for i in range(amount):
fire_projectile(projectile)
fire_angle += (spread/amount)
fire_angle = begin_direction
func fire_projectile(projectile):
var projectile_ins = projectile.instance()
projectile_ins.position = position
var rad_fire_angle = deg2rad(fire_angle)
projectile_ins.direction = Vector2(cos(rad_fire_angle),sin(rad_fire_angle))
get_tree().get_root().add_child(projectile_ins)
The code for the projectile:
var speed: float = 500
var direction : Vector2 = Vector2.ZERO
func _ready():
speed = 300
func _on_Projectile_area_entered(area):
if (area.name == "Player"):
area.take_damage(2)
self.queue_free()
func _physics_process(delta):
position += (direction * delta * speed)
func _on_DeleteDelay_timeout():
self.queue_free()
What causes this to happen?
Upvotes: 1
Views: 1869
Reputation: 40220
The way the question was originally written, I would have suggested to use a particle system. However, with the code, I see you are trying to spawn projectiles that can cause damage…
Before going further, I should point you to the profiler, which might give you some insight in what is actually happening. You can enable the Debugger profiler from the bottom panel of the editor, on the profile tab. Once enabled start the game. The profiler will make graphs (you can select what to graph on the left) showing you what takes more time. You may also be interested on the graphs on the Monitors tab. Then you would reproduce the problematic situation, and see what is eating more time (you would looking for some graph suddenly rising around the time your code spawns the projectiles).
I don't know what it is for sure, but I can make some educated guesses and give you some advice on how to handle them.
One of the reasons you might lose frames is shaders loading and compiling.
You can diagnose this problem by checking if this only happens the first time you spawn your projectiles. For the second time the shaders would have already been loaded and compiled, so there would be no frames drops due to this.
That also tells you how to address this: have something with the shader loaded and visible beforehand. On a similar note, try to reuse your shaders.
Anyway, I don't think shaders is the problem. A more likely culprit is physics: When the projectile spawns, it intercepts the other areas, and the multiple projectiles intercept each other. You would address that with collision_layer
and collision_mask
.
In an ideal scenario, you would not even need this check at all:
if (area.name == "Player"):
Instead you would have the player on its own layer. And these Area2D
have a mask that have only collide with that layer. That way Godot will not even check for collisions between the projectiles and something other than the player.
Something else that will ease the above issue is to spread the instantiations.
By the way, if the issue if that the projectile scene is just that heavy to instantiate, aside from trying to making it less heavy (smaller textures, simpler shapes, less expensive shaders…), you can also try spreading the instantiations.
The easiest way to do that is to insert this line in your loop:
yield(get_tree(), "idle_frame")
Or perhaps physics frame is better:
yield(get_tree(), "physics_frame")
This line will have the Godot halt execution until a frame (or physics frame) passes, then Godot resumes the execution of the code on the next line.
Similarly, if instantiating is expensive (or if the problem were a large number of instances being created, which does not seem to be the case), you could pool the instances.
This is probably not necessary in your case... Anyway, the idea is two fold:
queue_free
them, instead remove them from the scene (with remove_child
) and add them back to the array to be reused.Upvotes: 2