2D Shooter/STG optimization; Frame rate Drop

This thread has 3 sections, the nature of the problem, that things I’ve tried to fix it, and my suspicion as to what’s causing it. If you wish to help please read it all. The last thing I want is for someone to suggest object pooling, because they didn’t read that I tried that already.

The nature of the problem:

I am making a 2d shooter, if my tests look promising I may try and get serious about its development. However, when spawning lots of bullets at once, particularly if the bullets are spawned close or on top of each other, I suffer major frame rate dips.
The FPS dip occurs right before this moment, but it takes a quarter second for the stats to catch up, but as you can see, very low FPS when the bullets are spawned. See attached Image 1

But once the bullets spread out a bit, the FPS goes above 60, but there are the same amount of bullets on the scene, as you can see by the animator components on the bottom: See attached Image 2

I checked other Bullet Hell shooters like GunVein and Touhou, and they spawn bullets on top of each other all the time. They aren’t made in Unity, but I also double checked Bullet Hell Monday Final which was made in Unity, it does the same, spawning bullets in droves and yet it runs flawlessly.
That is the nature of the problem, but before I go on to my attempted solutions, here is a little more info on the bullets:
They do not use rigidbody2d components, they use a circular collider, which is a trigger, the player’s hitbox detects if it collided with an enemy bullets which is how the bullets damage the player. Here is the script that moves each bullet:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class enemybulletspeed : MonoBehaviour
{
private float speed; // Speed of the bullet

// Method to set the speed of the bullet
public void SetSpeed(float newSpeed) //the enemy ship that fires the bullet sets the speed of it
{
speed = newSpeed;
}

// Update is called once per frame
void Update()
{
// Move the bullet forward using its specific speed
transform.Translate(Vector2.right * speed * Time.deltaTime);
}
}

The solutions I tried:

Off screen bullet removal: Obviously I have already programmed the game to set any bullets that move off screen as inactive, which has a little buffer distance off camera. The bullets are not destroyed but are set as inactive because of my next solution

Object Pooling: Naturally, I have also setup an object pooler, during tests I can see how many bullets are in the hierarchy, it automatically instantiates bullets if the Pool for that bullet type is depleted, eventually hitting an equilibrium where it no longer needs to create new bullets. This hasn’t made a difference, when activating lots of bullets at once and seemingly on top of each other, I suffer frame rate dips each time these ships fire. Even after all the bullets would have been instantiated.

Collision layers: My next suspicion was that the bullets were colliding with each other. Although there is no code that triggers if an object that is tagged as “enemybullet” hits another object tagged as “enemybullet”. Only if it hits an object tagged as “player” or “terrain” does it actually do anything. Nevertheless I suspected that the game was still registering the collisions between “enemybullets” even if there was no code that triggers because of it. So I did some research and found that if you go into Edit>Project Settings, and from there go into Physics, I could prevent certain layers from interacting with each other, My bullets layers are 9, 10, 11. 9 for the larger bullets, 10 for medium sized and 9, for small bullets. Then I edited the layer collision matrix as follows: See attached Image 3
9790161--1404906--upload_2024-4-23_23-3-15.png
This should prevent Enemybullets from colliding with each other, and I tried making code to print a message in the console if an enemy bullet detects a collision with another enemy bullet, I never received that console message so I’m confident that the game isn’t registering collisions between all the enemy bullets that spawn on top of each other.

Spawn bullets not on top of each other: Well if the bullets are causing FPS dips when spawning on top of each other, I decided to make an offset so that the bullets spawn a bit like this:
See attached Image 4
9790161--1404909--upload_2024-4-23_23-13-31.png
That makes less bullets stack on each other, but it hasn’t helped, I still suffer frame rate dips. I double checked other bullet hells, I thought maybe that’s actually how they do it and I just never noticed, but no, their bullets very often spawn from a central point all in a huge stack.

Overcrowded layer?: My final attempt involved putting the bullets on separate layers, as you can see from the above screen shots, the outer most bullets travel a bit faster than the inner most bullets, because they are assigned a different speed when fired, but they spawned at the same time and on top of each other. So I suspected that the sprite renderer was having difficulty because all of these bullets were spawning on layer 10, so I made the faster bullets a part of layer 9, thinking that somehow… having too many bullets on layer 10 made Unity unsure of which bullets should be displayed first and that somehow affected the FPS

Sprite Atlas: I don’t know much about Sprite atlas, but I created one and added all of my bullet types to it so far. I have no idea if I did it right: See attached image 5

My final suspicion:

I still think that somehow, the issue is caused by sprites stacking on each other, why that causes problems, I don’t know. But to test, I tried spawning a bunch of ships, these guys are programed to follow the player to attempt a ram. I would upload more screenshots but I am limited to 5, just take my word that if they are spread out, even if I have 80-100 on screen, I suffer no FPS issues. But over time since they are all converging on a central point they eventually stack up, and causes horrendous FPS issues. Of course I would design the game so that these ramships won’t stack on top of each other to that degree, but I need the bullets to be able to do that.

That is my suspicion, I have no idea where to go from here. No optimization tutorial mentions anything like this, nothing for 2D games at least, I can’t find any forum here or on google that mentions an issue like this. Any help is greatly appreciated

Interesting. I would use the built-in unity profiler tool to capture the series of frames where the bullets start clustered then spread out. This should allow you to analyze exactly which function calls are relatively slow when the sprites are stacked. The frame debug function might be useful as well. Good luck!

I note also you have 336 animator components. Do you have an animator for each bullet? This might be quite expensive.
Animators auto-switch off when the gameobject goes out of the screen so you may find the speed up is because some of the animators are not visible due to the spread. Maybe try spawning all the bullets at a single location off-screen and see what happens.

So the bullets are all prefabricated objects, the bullet prefab has an animator component, so I suppose that means each one does. If I deactivate my bullet remover, the animator components remain at 336 as they move off screen, beyond the camera. I would assume that anything outside of the camera bounds would automatically not be rendered, but maybe that’s not the case.

I would love to attach some info from the Unity profiler tool, but I’m a bit new to Unity and development in general. I may have to look into that tomorrow.

Also, with the bullet removal script active, the number of animator components goes down as they bullets are set to inactive. In addition, the FPS dip mainly occurs while sprites are stacked.

In fact, here is an example involving those Ramships I mentioned, I will try to attach them

Not very stacked, and the FPS is good.
Here is what happens when I move about and allow them more time to converge upon me:

FPS is way worse than those stacking bullets, so stacking sprites must be the issue. These are also not animated, yet the FPS is terrible as long as they are stacked
Also, here is what happens when they all explode, the FPS goes up even though there are around 400 bullets on screen, not to mention the explosion animations. yet still, 90 FPS:

Use the Profiler, it’s what it’s there for. EDIT: I see you mention that you want to look at this in one of your posts. This is the ONLY way to know what you’re doing wrong. :slight_smile:

I hope those don’t have physics components on them because the main rule with physics is that you use the Rigidbody2D API to perform movement and attached colliders move with them. Also, physics doesn’t run per-frame. If these are just renderers with physics queries then that’s fine.

Finally, here’s how to post code on the forums: https://discussions.unity.com/t/481379

If you’re using 3D physics yes but you use “Physics 2D” for 2D. Maybe that was just a typo above?

None of the bullets have rigidbody2D components

Also, regarding the Physics 2D, I see that now, I did check the 2D collision matrix and corrected it:

However, it hasn’t helped. My frame rate still dips heavy when bullets or other sprites are stacked

But they have 2D colliders?

and yes, 2d circular colliders, they do have that

Considering that it’s now 1AM where I live, I’ll have to check back tomorrow. Will also try to post info from the profiler once I figure out how it works

1 Like

That means they’re Static (non-moving). You never ever modify the Transform when using physics as this causes it to be recreated at the new position i.e. teleport there. It also doesn’t happen until the simulation runs as a Transform change doesn’t update anything beyond the Transform.

The only thing that moves in physics is a Rigidbody2D, colliders live attached to them. If you ever find yourself changing the Transform, you’re doing it wrong. The Rigidbody2D writes its position/rotation back to the Transform. That’s how physics works. :slight_smile:

When you do this to a Static collider, it is completely recreated from scratch when the simulation runs which isn’t per-frame by the way. If you wanted to move something but not have a collision response then you’d use a Rigidbody2D with a body-type of Kinematic.

It’s not even clear why you’re using colliders here and TBH you don’t need them. You can use a physics query on a script to detect it hitting stuff such as Physics2D.CircleCast or Physics2D.Raycast and get it to only detect things like players etc.

To note though, Static colliders never interact with Static colliders so time is likely spent just recreating hundreds/thousands of colliders because you’re teleporting them by modifying the Transform.

In the end though, my comments above are about using physics correctly. The actual performance issues are likely that and other stuff too and only the profiler will tell you what the time is being spent on.

Here is that Frame data, Each rise in the chart is when the bullets are spawned in:

I noticed it’s very busy with physics calculations, I removed the rigidbody component from the enemy ships and the FPS dips got a lot better, now it dips to 60-90 FPS, which is hardly noticeable. Problem is if I do that, I can’t shoot it anymore, my bullets just pass right through even though I have a Polygon collider attached to it.

On the topic of colliders, I do need them, as I can control the size of the bullets hit boxes, as is typical in bullet hell style games, the bullets must appear larger than their actual hitbox.

In the beginning I did experiment with giving all bullets Rigidbody2d components, but it resulted in odd behavior, enemy bullets blocking player bullets for example, causing player bullets to bounce off them. that is why I ditched RB components early on and kept it only on ships so that collisions could be detected

Since I’m only making this for test purposes, I just want to make a basic shooter so I can experiment and try to create interesting weapons for the player. so I suppose I need a way to prevent enemy bullets and enemy ships from interacting with each other in anyway. I tried doing that in the Physics layers, I moved the ships, which I call Drones onto layer 6, and made it so the bullets shouldn’t interact with them:

This approach isn’t working, I still suffer frame rate dips due to physics calculations

Unforutnately a graph doesn’t provide any useful information. You need to dig into what part is taking the time.

That doesn’t change what I said. You can control the “size” of the query you use i.e. the size of the circle etc. Bullets using physics queries by casting shapes is by far the best way to do this.

That’s because you likely added a Rigidbody2D and left it as it. There’s three body-types available and the default of Dynamic isn’t what you’d use for bullets as you don’t want a collision response.

Issue resolved

That profiler did actually help, it shows that the issue was that needless physics calculations were occurring, so it wasn’t an issue of lots of sprites stacking up.
Turns out the Physics layering was the solution, I just thought the sprite layer determined what physics layer the game object was on:
9795936--1405818--upload_2024-4-25_13-24-0.png
9795936--1405812--upload_2024-4-25_13-21-51.png
9795936--1405815--upload_2024-4-25_13-22-52.png

So the frame rate is now stable, and adequate for me to proceed with my experiments.

1 Like

Yes, physics is unrelated to rendering. It’ll work just fine without any renderers.

For added performance though, add a Rigidbody2D and set it to be Static. This will remove the need for it to recreate the collider shapes. You’ll see this in the profiler with Destroy/Create when the simulation runs in SyncTransforms.

If you want to have colliders then I’d suggest adding a Kinematic Rigidbody2D and just setting the velocity once; it’ll continue moving in that direction until you destroy it and won’t bounce off anything. If you add Interpolation then it’ll move smoothly too.

Also, for bullet-hell style games, you can go into the Physics 2D settings and set the simulation mode to “Update” which means the physics will update per-frame and you no longer need to use FixedUpdate as everything runs immediately after your Update calls per-frame. Interpolation is ignored too. Obviously this’ll mean you’re getting a variable-rate physics so determinisim is massively reduced if not completely wiped out but this hardly matters in most games TBH. That said, there’s also sub-stepping in later Unity versions which is the best of both worlds; it gives you fixed-sub-steps and doesn’t suffer from issues of very low frame-rates.

Pleased you got it sorted. :slight_smile:

2 Likes