Skip to content

10 Things I Wish I Knew Before Starting a Large Godot Project (Cost Me Months)

Learn 10 crucial tips for large Godot projects to avoid costly mistakes and improve workflow, from debugging to best practices.

Ask about this video. Answers come from its transcript only — with the timestamp, so you can check them.

Generated from the transcript and can be wrong — check the timestamp.

Key Takeaways

  • Understand the tools and concepts deeply rather than copying tutorials blindly.
  • Separate visual transformations from physics to avoid bugs and unpredictable behavior.
  • Use debugging tools like god mode to accelerate development and inspire creativity.
  • Manage memory and scene loading carefully to prevent leaks and performance issues.
  • Optimize enemy management by updating only when necessary to improve game performance.

What the video covers

  • Avoid blindly trusting tutorials; understand the underlying concepts to prevent future issues as your project scales.
  • Use Sprite2D's offset property instead of position for animation alignment to avoid issues with child nodes inheriting transformations.
  • Implement a debug 'god mode' for the player early on to speed up testing and potentially inspire new gameplay ideas.
  • Never flip physics nodes by negatively scaling; use Sprite2D.flip_h for visuals and reposition physics nodes explicitly.
  • Be cautious with Godot's preload as it can cause unintended memory retention if large scenes are preloaded globally.
  • Preload only small reusable assets close to where they are used to avoid accidental memory leaks.
  • Avoid placing enemy scenes directly in the level to prevent unnecessary updates when off-screen; use VisibleOnScreenNotifier2D instead.
  • VisibleOnScreenNotifier2D allows efficient enemy spawning and respawning based on player proximity, optimizing performance.
  • Choose resolution scaling carefully to support modern aspect ratios and maintain visual quality.
  • Use project settings and organizational tools like favorites and color-coded folders to improve project navigation and management.

Answers

Questions about this video

Why should I avoid flipping nodes by scaling negatively in Godot?

Negatively scaling a parent node flips all child nodes including physics areas, causing invisible bugs like incorrect collision detection and raycast errors. Instead, use Sprite2D.flip_h for visuals and reposition physics nodes explicitly.

What is the difference between Sprite2D's position and offset properties?

Position affects the node and all its children, representing real movement, while offset only shifts the sprite's texture for animation alignment without affecting child nodes. Using offset prevents unintended movement of child nodes.

How can adding a god mode help during game development?

A god mode debug tool allows quick movement through levels, disabling hazards and enabling faster testing. It saves time and can inspire new gameplay ideas by experimenting with player states and mechanics.

Full Transcript — Download SRT & Markdown

00:00
Speaker A
I spent 10 years as a professional software engineer working on fighter jets. Then I decided to learn the Godot Engine by rebuilding Mega Man 2 from scratch as a learning exercise.
00:09
Speaker A
And I made a mistake so costly that I had to throw away the entire project and start over.
00:15
Speaker A
The worst part is that it wasn’t even a bug. It was a best practice I followed blindly.
00:19
Speaker A
But at least it gave me the chance to try out a few things. More on that later. In this video, I’m sharing 10 things I wish I had known from day one so you don’t make the same mistake.
00:27
Speaker A
We’ll cover each one at a high level. And if you want a deeper dive on any of them, let me know in the comments. I’ll be turning those into full tutorials next. My first tip is don’t blindly trust every tutorial you find. And that includes this one. Godot has a ton of great tutorials,
00:42
Speaker A
and most of them work exactly as advertised. The problem is that a lot of patterns work now, but break later, especially as your project grows. Nobody’s acting in bad faith here. The advice usually makes sense in specific context. But when you copy it without understanding what’s
00:56
Speaker A
happening under the hood, you can bake in problems that won’t show up until it’s way too late.
01:00
Speaker A
That’s exactly what happened to me. One of these perfectly reasonable tips eventually forced me to restart my entire project. So, treat tutorials as learning tools, not blueprints. If you’re building anything larger than a small prototype, make sure you understand why something works, not
01:14
Speaker A
just that it does. There’s an extremely important and very easy to miss property on Sprite2D nodes that can cause massive headaches later in your project. Once again, this is something that can work perfectly at first and then quietly break things as your game grows. When you’re animating
01:29
Speaker A
movement or just trying to line up a sprite with its collision shape, the natural instinct is to change the position. That’s exactly what I did in my first project, and it worked until it didn’t.
01:39
Speaker A
But Sprite2D has another property, offset. At first glance, it looks like it does the same thing. Even the documentation doesn’t make this distinction feel especially important, but here’s the key difference. Position is inherited by the Sprite2D’s children. Offset only affects
01:56
Speaker A
the sprite’s texture. To show why this matters, here’s a marker I use for spawning projectiles.
02:01
Speaker A
When I change the position, the marker moves with the sprite. But when I change the offset, the marker stays exactly where it should. Looking at a full animation makes the difference even clearer.
02:10
Speaker A
This is my original version using position. And here’s the updated version using offset. Now, changing the position isn’t necessarily wrong. There are absolutely times when it’s what you want. The key is using the right tool for the job. Position is game truth. Offset is animation
02:28
Speaker A
fiction. Position implies real movement. Offset is lining up for art. This next tip will save you an absurd amount of time. Give your player character a god mode. This is strictly a debug tool, not something that you ship, but it’s one of the best decisions you can make early on.
02:45
Speaker A
I spent months working on this project without any special debug controls. While testing, I could move the player around in the editor while debugging, but once the game was running, I had no way to quickly move through the level. This meant waiting for hazards, shooting through the enemies
03:02
Speaker A
or missing a jump and starting all the way back at the beginning. Eventually, I spent less than 20 minutes adding a new player state, faster movement, no gravity, and the hurt box disabled.
03:15
Speaker A
Simple. And honestly, most of that 20 minutes went into adding this completely unnecessary animation, which I could have skipped entirely, but I mean, come on. The payoff was huge. I could instantly jump to any part of the level and test things in seconds instead of minutes. This comes with
03:32
Speaker A
an unexpected bonus. Debug modes like this can actually spark new gameplay ideas. I originally added a sprint just to stress test the camera by doubling the player speed. Turns out it was fun.
03:43
Speaker A
Running around this level like this made me realize there were new gameplay opportunities I hadn’t even considered. I’d bet the more than a few core mechanics and great games we all know started life as nothing more than a debug tool. All right, now it’s time to talk about the mistake
03:57
Speaker A
that killed my first project. While researching for this video, I found something frustrating. Many of the top search results recommended doing the exact thing that caused this. I wanted an easy way to flip the facing direction of an entity, including markers and non-symmetrical
04:12
Speaker A
physics areas like projectile spawn points or ledge detection area 2Ds. The solution I found was to multiply the X scale of the entire node by negative 1. Do not do this. This appears to work at first and a lot of beginners do it. I mean, I did it. The core problem is that the engine
04:30
Speaker A
doesn’t always behave the way we intuitively expect. Without going too deeply into the weeds, there’s an important distinction between visual transforms and physical space. When you negatively scale a parent node, that scaling propagates to every child, including area 2Ds, collision shapes,
04:48
Speaker A
raycasts, and offsets. And here’s the worst part. When you turn on visible collision shapes, everything looks correct, but the actual physics interaction is happening somewhere else entirely.
04:59
Speaker A
This leads to bugs that are nearly impossible to reason about. Hits that miss. Raycasts lie. Collision normals that flip directions. Dogs and cats living together mass hysteria. Never flip physics by negatively scaling nodes. When the facing direction changes, use Sprite2D.flip_h for visuals. Explicitly reposition physics nodes or store to local positions or transforms and swap between them. If there’s one takeaway from this entire video,
05:04
Speaker A
it’s this. Never let visual convenience dictate physics truth. For this next one, I’m going to try very hard not to bore you with technical jargon. I originally treated Godot’s preload like a C++ compile time constant. Well, in reality, preload behaves much more like a
05:27
Speaker A
global static reference that increments a ref count. You’re boring the commander with tech. Anyways, in practical terms, if a scene is preloaded by a node that never goes away, like a main game manager, it can keep the entire chain of assets alive in memory, too. Here’s a
05:46
Speaker A
simple example of how this can go wrong. My main game script preloads the main menu. The main menu preloads the opening cutscene, and the opening cutscene preloads level one. At first glance, this looks reasonable. That is the startup flow of the game, and treating these scenes like constants
06:03
Speaker A
feels safe. The problem is that main game never leaves a scene tree which means that every scene in that preload chain is loaded into memory and more importantly never freed even when they’re no longer in the scene tree. This is the key thing to remember. What matters isn’t is the scene still
06:19
Speaker A
in the tree. It’s does anything still reference the resource. And preload makes it very easy to accidentally create permanent references. Because of that, it’s usually a bad idea to preload large scenes that contain lots of nodes. A better use of preload is for small reusable gameplay pieces
06:36
Speaker A
and to keep the preload as close as possible to where it’s actually used. For example, the player preloads the projectile scene. The player always needs it, and when the player is removed from the scene, that reference disappears, too. That’s the difference between intentional ownership and accidental memory leaks. When I first started adding enemies to my level,
06:53
Speaker A
I did pretty much what everybody does. I place the enemy scenes directly where I wanted them to appear. Godot even encourages
07:06
Speaker A
ownership and accidental memory leaks. When I first started adding enemies to my level, I did pretty much what everybody does. I place the enemy scenes directly where I wanted them to appear. Godot even encourages this by instancing the enemy as a child of the level. And at first,
07:21
Speaker A
everything works perfectly, but the problem starts showing up as soon as you add movement.
07:26
Speaker A
If an enemy tracks the player, that carefully placed enemy might be anywhere by the time the player actually reaches it. And it gets worse. What about enemies at the far end of the room?
07:38
Speaker A
Look at this guy. He's happily marching across the whole level long before the player ever gets close. And then there's all these. Do we really want the game updating their movement and checking collisions when the player is three screens away? This is where VisibleOnScreenNotifier2D comes in.
07:56
Speaker A
This node emits a signal when its rectangle enters and exits the screen. By connecting those signals to your enemy logic, you can control exactly what that enemy does, when it becomes visible, and when it doesn't. If you want something even simpler, there's VisibleOnScreenEnabler2D. Just
08:12
Speaker A
attach it as a child of the node, and by default, it automatically disables processing whenever the object is offscreen. No signals, no code, it just works. One important note, the notifier rectangle needs to stay enabled or visible for this to work. In Godot 4.5, there's a handy show rect option
08:32
Speaker A
which you can click to hide the rectangle in the editor while still keeping the logic active.
08:37
Speaker A
What I ultimately ended up with was taking this a step further and building a custom spawner. The spawner controls when enemies spawn, where they spawn, and under what conditions they respawn.
08:46
Speaker A
And it's flexible enough to handle pretty much anything I throw at it. For example, I could spawn this enemy once and only respawn it if it's been killed and the spawner comes back on screen. Or I can configure enemies to continuously respawn at random intervals, but only while the
09:01
Speaker A
spawner itself is visible. If you'd like a deeper dive into spawners and all the things you can do with them, let me know in the comments cuz there's a lot to get into on this subject. Another part of the Godot Engine that's easy to overlook early and extremely painful to fix later is choosing the
09:16
Speaker A
correct aspect ratio and scaling settings for your project. This is especially important for pixel art games. Every pixel in your art ends up being stretched across multiple real screen pixels, and how that scaling is handled depends entirely on the player's display resolution.
09:32
Speaker A
If this isn't set up correctly from the start, you can end up with visual glitches, shimmering, or ugly artifacting that shows up everywhere, and fixing it later can mean reworking a lot of content. And that's why it's critical to decide on your base resolution and scaling strategy before
09:47
Speaker A
you commit to a large project. There's a lot of valid ways to handle this depending on the type of game you're making. So, I don't want to get too deep into it. Instead, we'll go over common solid starting point. For modern pixel art games, a very common base resolution is 640 x 360 or 320 x 180,
10:03
Speaker A
1 half of it. These resolutions scale cleanly into standard modern aspect ratios using whole numbers. For example, x2 gives you 720p, x3 gives you 1080p, x4 gives you 1440p, x6 gives you 4K.
10:18
Speaker A
These values are set in project settings, display window under viewport width and viewport height.
10:24
Speaker A
This defines your game's native resolution. If you enable the advanced settings, you can also set a default window size, for example, 1920 x 1080, so the game launches at a comfortable resolution without manual resizing. In the same section, you can control how the engine scales the game when
10:39
Speaker A
the window size changes. In most cases, you'll want scale mode set to canvas items or viewport, aspect set to keep. This preserves the aspect ratio of your art when the window is resized.
10:50
Speaker A
If you enable integer scaling, the engine will snap scaling to clean whole number multiples of your base resolution. Fractional scaling allows in between sizes, which can be useful, but may introduce artifacts depending on your art style. The right settings depend on your
11:04
Speaker A
game and your goals, but the key takeaway is this. Decide early and test often. If you'd like a deeper breakdown of these options and want to use them, let me know in the comments.
11:13
Speaker A
Coming from an embedded C and C++ background, using static typing was a no-brainer for me, and it's something I strongly recommend in GDScript as well. By default, GDScript allows dynamic typing where variables are declared without specifying a type. Static typing is simply adding the type
11:29
Speaker A
directly to the declaration like this. Doing that tells Godot what the variable is ahead of time instead of forcing it to guess at runtime. One immediate benefit is much better editor support.
11:40
Speaker A
You get cleaner autocomp completion and you can control-click on variables or functions to jump straight to their definitions or even directly into the documentation for native types. But the bigger win is earlier and easier error detection. When your variables are statically typed,
11:55
Speaker A
the editor can tell you immediately if you're using something incorrectly. Without that, you're often left discovering mistakes at runtime, which can be extremely painful to debug in a large project. Static typing can also help with performance. Dynamic typing means the
12:08
Speaker A
engine has to spend time figuring out what each variable actually is while the game is running.
12:12
Speaker A
In most cases, the performance difference will be small, but with the fixes easy, there's no real reason not to give the engine that information up front. If you want to enforce this habit, you can have the editor warn you whenever a variable isn't typed. Go to project settings, general, debug,
12:28
Speaker A
GDScript, find untyped declaration, and set it to warn. If you're already partway through a project, you can then work through these warnings incrementally and clean things up as you go. This is one of those small decisions that pays dividends every single day you work on your game.
12:43
Speaker A
Coming from a software engineering background, version control was something I understood the importance of from day one, but it's so essential that I couldn't leave it off this list, especially for game development. There are a few options out there, but the one I use for my project is Git,
12:56
Speaker A
typically hosted on GitHub. What makes Git so powerful is that it lets you create checkpoints called commits throughout your project's history. At any point, you can go back to a version of your project exactly as it was at that moment. If you've ever been in this situation, you already
13:11
Speaker A
know why this matters. You spend hours fighting a feature. Your code is messy, but eventually it works. Naturally, the next step is to clean things up, and suddenly something breaks. Now you're stuck trying to resolve a problem that you already solved once. Version control completely
13:28
Speaker A
changes that experience. The moment a feature works, you create a commit. From that point on, no matter what happens, you can always go back to the last known good state. Even better, Git lets you compare your current code against the working version, which often makes it trivial to
13:42
Speaker A
spot the one small change that caused the problem. I genuinely cannot overstate how much stress this removes from development. Version control doesn't just protect your code, it gives you confidence to experiment, refactor, and improve things without fear. There's a lot more depth to this topic,
13:58
Speaker A
so let me know if you'd like to see a beginner friendly Git setup or workflow specifically for Godot projects. And with that, let's wrap up with a few quick tips and tricks. You can slow down or speed up the entire game using the global time scale. Great for debugging or special effects. You
14:15
Speaker A
can explicitly control whether the hardware mouse cursor is visible in your game. In the editor, Alt plus right-click lets you choose exactly which object you want to select. Just make sure you're in select mode. If your fonts look blurry, enable MSDF, multi-channel signed distance field,
14:31
Speaker A
under project settings, GUI, theme, font, or directly on the individual font resources. You can right-click scenes in the file system and mark them as favorites to keep frequently used scenes easily accessible. You can also color folders and files to make large projects much easier to navigate.
14:50
Speaker A
If your PC starts making strange noises while running the debug editor, it's often because the engine is rendering at an extremely high frame rate. Limiting the FPS is a good idea and something you should absolutely do before shipping anyways. And finally, you can rearrange the editor
15:05
Speaker A
layout however you like. I prefer giving the file system the full height of the screen and keeping the scene and inspector close together, but tailor it to your workflow. If you found this video helpful, I'd really appreciate a like and a subscription. It helps the channel a lot.
15:19
Speaker A
Let me know in the comments if there are any tips you'd add to this list or which of these topics or any others you'd like to see expanded into a full tutorial next. Thanks for watching.
Topics:Godot Enginegame developmentgame programmingdebuggingSprite2Dpreloadmemory managementphysicsgame optimizationVisibleOnScreenNotifier2D

Get More with the SozAI App

Transcribe recordings, audio files, and YouTube videos — with AI summaries, speaker detection, and unlimited transcriptions.

Or transcribe another YouTube video here →