This video demonstrates key performance optimization strategies for game development, including object pooling to manage thousands of bullets without slowdowns, using multi-mesh rendering for efficient bullet rendering, implementing spatial partitioning and physics server queries for collision detection, and creating a lazy-loaded decal system for blood effects that only uploads modified chunks to the GPU every 250 milliseconds. The developer emphasizes that built-in engine systems often outperform custom implementations, and that understanding when to use existing systems versus writing custom code is crucial for achieving optimal performance in games with many simultaneous objects.
Deep Dive
Prerequisite Knowledge
- No data available.
Where to go next
- No data available.
Deep Dive
Making a bullet heaven in Godot
Added:[music] Good evening, boys. I'll be honest with you. I ain't doing too great. The summer heat is killing me. My job is boring and I ain't got too much money. So, I got some creative inspiration back and I started making games again. My current project is called Boomzer Reloaded and it's a remake of one of my oldest games that I released in 2017 which was called [music] Boomzer of course and it was a top-down shooter somewhat inspired by Borderlands. Now at the time I was not a great coder so the result was kind of halfbaked and it barely worked but it had some interesting features in it [music] which I want to remake in this remake I guess. Uh, these features were big pools [music] of blood and gore and procedural gun generation. Now that I'm an actual software engineer and I have a pretty good understanding of how code and computers work, uh, I decided to f fully realize the vision that 18-year-old me had in 2017.
Also, this project is a way to learn the GDAU game [music] engine, which I never really learned and I kind of winged it until now. So, yeah. You know, there's also that. Oh, so here's the pitch for the game. Big guns, big poos of blood, all wrapped in a rogike bullet heaven package.
So now, now without further ado, let's get into the code.
[music] Being a shooter, bullets were an important part of Boom Terk and are going to be an important part of Boom Reloaded. So, I have to make sure I can support a lot of them without any slowdowns. And when I say a lot, I mean a lot, like somewhere in the thousands.
To achieve this, I couldn't make every single bullet a physics object and handle it like a normal scene. Creating a new instance for every bullet fired would slow down the game down to a crawl, which is why I'm using object pooling. In broad terms, using object pooling means keeping an array of objects that we never deallocate. We just disable and enable them when we need. This avoids a lot of expensive memory allocations and deallocations.
My system is a bit more complex. So, here's how it works. Whenever a bullet needs to be fired, whoever is going to fire it has to go through a bullet manager to get a currently inactive bullet and set its stats and then fire it with the spawn bullet function. The bullet manager has three arrays that act as my object pools. The inactive bullet pool contains allocated bullets that aren't currently being used, while the other two are the active enemy bullet pool and the active allied bullet pool, which are where I put bullets that are currently active. The pool they are in just determines which collision mass they use. So, for example, enemy bullets collide only with walls and allies, while allied bullets collide only with walls and enemies. The bullet manager is also responsible for handling physics and rendering. Bullets do not exist in the world as scenes. So I can't calculate collisions or even render them normally. They're basically just data points in arrays. So to calculate collisions, I use direct carries to the GDO physics server, which is extremely fast since it avoids scene tree signals and wrapper overhead. Another small optimization on top of that is caching and reusing the bullet shapes used in the carries instead of recreating the same shape every time or storing it in the bullet themselves.
To render bullets, I use a multi mesh, which is kind of a pain in the ass to set up, but it lets me render thousands of bullets basically for free since the GPU is just sent a single draw command with a single texture and all of the bullet transforms. This lets the GPU work extremely fast because API overhead is completely eliminated.
Basically a single mesh is loaded into video memory core in our case and then a full hardware instancing it is rendered multiple times almost for free even if it needs to be rendered thousands of times.
The original boom did not have many enemies running around and the few it had were all clipping into each other.
This time though I'm making a bullet heaven so I need to support not just a lot of bullets but a lot of enemies too.
The system works by having a single non-monitoring area 2D attached to every enemy which acts as the hitbox. This area of 2D is used by the bullets for checking collisions of course and it's also used by other enemies to avoid overlapping with each other, thus avoiding uh clipping into each other like the original game. Enemies do not have their own movement, but they're controlled by an enemy manager, which works similarly to the bullet manager, even if it is a bit less complicated because enemies are actual scenes and not just data points in an array. It still manages to spawning and removing enemies in the same way as the bullet manager. As for performance, I'm aiming for about 5 to 600 enemies on screen at once as the absolute limit. So, of course, I tested it with a thousand enemies in debug mode. To be very sure, the engine could handle it, and the engine kind of did handle it, but unfortunately or unfortunately, depending on who you ask, I am very stubborn. So, I wanted it to work even faster. [snorts] So, as a first simple optimization, I tried to stagger the targeting and separation forces calculation for every enemy. basically made it so that in the first frame only enemies in a even places of the active list would process those forces while only enemies in odd places would do so in the next frame and so on for the following frames. This worked [music] pretty well slashing the physics load almost in half. But I felt like it wasn't enough. So then I tried another simple optimization which is limiting the amount of collisions [music] a single enemy can process in a frame. I limited it to two since it looks fineish while being faster than before.
At this point, I was already maxing out my screen refresh rate, which is 165 frames per second with 500 more enemies than intended, all colliding at once in debug mode. But alas, it still wasn't enough for me. My hubris brought me to build my own spatial partitioning and physics system directly in GTScript, which worked, but was a lot slower than just using the integrated physics engine. Then I convinced myself that it must have been GDScript's fault. After all, it isn't a compiled language. So, of course, I tried to get an LLM to convert my GDScript implementation into C++ and build a GD native extension.
And of course, while a lot faster than the GDScript implementation, it still wasn't as fast as the built-in physics system. Moral of the story is that I'm not better than a thousand of experienced programmers, and you aren't either. So, it is better to stick to the already built-in systems unless you already know what you're doing. Really know what you're doing. With that said, the current up-to-date version of the game, I switched to using physics server queries for the enemy collisions, too.
And I got another pretty big performance boost while also significantly increasing the amount of collisions per enemy. I didn't use the same system I made for the bullets for the enemies because enemies are more complex than just bullets and [music] they all need to collide with each other. Also, handling multiple multi meshes for different enemy types was going to be a pain in the ass. So, [music] I decided to avoid doing that. [snorts] You might also be asking yourself, why didn't I use C if I care so much about performance? The answer to the question is very simple and it is that I think C is an ugly-looking language and code aesthetics is very important to me.
One thing the original Boomzer did not skimp on was gore. The blood was plentiful, it was permanent, and unfortunately it was also extremely unoptimized. Whenever something bled on the screen or you dropped a bolted casing, the game would grind to a halt if you had a weak PC. I'm trying not to replicate that in the remake. So, compared to the two previous systems, blood or decals in general, I should say, is a simpler system, but it has its nuances. Basically, there is yet another manager that manages a bunch of decal chunks. [music] Decal chunks are lazy loaded, meaning the map starts devoid of any decal chunks. They are only created and tracked by the manager if a decal is cued to be drawn in a location devoid of any chunks. Decal chunks are composed of a node 2D, a texture which is stored in a [music] GPU and an image which is stored in RAM. Whenever a decal is drawn to a chunk, it is only drawn on the image stored in memory. [music] GPU uploads are controlled by the manager and happen once every 250 milliseconds to avoid overloading the GPU buffer. A pretty big optimization I implemented is only to upload chunks that are marked as dirty. Meaning that the manager only uploads chunks that have been modified in the last 250 milliseconds.
The ready function of the decal manager is interesting [music] because you can see how I generate all decals and rotation of set decals at startup time from a much smaller subset of colorless sprites. I do this because I'm lazy and I'm not a very good at drawing. I do this also because chunk draws use the blend rect function which does not support rotation or coloring.
As you can see, this system [music] allows me to have basically infinite carnage on the map without any performance penalties, [music] which is how the original game should have been.
The other limitations are that I can't use shaders, arbitrary rotations, and scaling. An alternative fast method which I might actually implement in the future is using a hybrid viewport texture approach. I would basically add the decal sprites as children to a chunk owned sub viewport instead of drawing them directly to the image. Then every few seconds I would bake the viewport texture to the image and delete all sprites to keep chunk performance and memory efficient.
This would also come with the added benefit of [music] decals supporting shaders and arbitrary scaling and rotations, which is the main reason why I would do it, but as of now, I'm happy with the current system.
This is what I've worked on so far. I'm only a week into development of the game after all. I haven't done much. But next one is probably the procedural weapon generation system. I plan to make it more visually varied [music] and with a lot more effects and a lot more interesting guns in general. Hopefully, this was an interesting deep dive on how to optimize these kinds [music] of games. More videos to come soon probably. Have a good one.
Related Videos

TOP 15 Data compression Interview Questions and Answers 2019 Part-2 | Data compression | Wisdom jobs
wisdomjobs
281 views•2019-06-28

CTS 158: 802.11w Management Frame Protection
ClearToSend
4K views•2019-02-04

NDSS 2019 Send Hardest Problems My Way: Probabilistic Path Prioritization for Hybrid Fuzzing
NDSSSymposium
496 views•2019-04-02

How realistic is Cities: Skylines?
CityBeautiful
159K views•2019-02-14

GUIs & TUIs: Choosing a User Interface for Your Python Project | Real Python Podcast
realpython
2K views•2025-04-04

The OSI Model - Explained by Example
hnasr
225K views•2019-05-12

Cloud Computing - Introduction
elithecomputerguy
98K views•2019-10-07

From Traveler's Dilemma to Dynamic Routing | Demystifying Networking
IITBombayJuly
5K views•2019-08-04
Trending

WOW! Judge TURNS THE TABLES on Trump in His OWN $10B LAWSUIT!!!
MeidasTouch
197K views•2026-07-23

Playstation NO DISC/NO BUY Fight Is Over...
DavidJaffeGames
4K views•2026-07-23

Steam and Xbox Just Dropped The Hammer On PlayStation
OhNoItsAlexx
9K views•2026-07-23

Americans Confused in Australia for 17 Minutes Straight
IWrocker
17K views•2026-07-23