What is the most efficient way to spawn enemies/collectibles?

My game is a fast paced 2d platformer. It takes 1-3 minutes to beat a level, they can be pretty long. Each level features a few branching paths with a lot of enemies and collectibles that have to be spawned in just before they get in range of the camera.
I was handling spawning by having a Spawner gameobject with the spawner script and with a spawnpoint gameobject childed to it. The spawner script would use physics2d.overlapcircle in the FixedUpdate to tell when the player was in range and then instantiate the collectible or enemy at the spawnpoint’s position. The collectibles also used overlapcircle in fixedupdate to tell when contacting the player. The enemies each have their own script as well and use box/circle collider triggers to hit/hurt the player but overlap circle to tell if they’ve been jumped on to be killed. Most levels were running fine aside from the occasional stutter, but a couple of the later levels with more complex backgrounds and a larger number of enemies and collectibles were really tanking the frame rate. I started researching and learning more about optimization and profiling, I figured that instantiating and destroying a large number of objects can cause a lot of slow down and that the most time taken up by the cpu was because of physics2d.fixedupdate. So I changed all my collectibles and enemies to be active or inactive rather than instatiated and destroyed and then I changed my spawner script to use ontriggerenter2d(with a big circle collider) to detect the player instead of overlapcircle.
Now levels that were previously performing fine or great tank hard when there’s a lot of stuff on screen. Is ontriggerenter2d really more expensive to use than overlapcircle in fixedupdate? could it have something to do with all the inactive gameobjects in the hierarchy? So what is the best practice here? Should I go back to overlapcircle for spawning, possibly in a coroutine rather than fixedupdate or is there a better way?
Also every enemy and collectable has a script attached that deactivates it when it detects that the camera has gone passed it with an ontriggerenter2d function.
Any help is much appreciated and I can provide more information if needed but this question was already really long.

So, if u have only one player - best practice is “drow invisible line” from every object to player, for determining collision check lenght this line. And don’t do it in update, better create custom coroutine for this. Try learning pooling system, this must help u too.

@xofear @wainwork Hey guys. So what I wound up doing was moving the physics2d overlap circle from the fixed update to a coroutine that runs 10 times per second. I also made all the enemies and collectables load into the scene inactive and become active, rather than instantiated, when the overlap circle is triggered. My coroutines previously had all been returning new waitforseconds and I instead set the time to wait beforehand to cut down on caching. Finally, I did learn a little about object pooling and used it on some projectiles. It’s running WAYYYYY better now. Thank you, I’ll research the drow invisible line. Maybe I can make it even better.