Oooh, this is cool. I always like to see different approaches to game dev (despite never having published a game!). So far I've tried
- Bevy (Rust ECS engine), which is nice at first but has a lot of problems with its implementation and can become rather messy. I think it's heavily dependent on the game. Part of it will be my own incompetence.
- Unity. IMO the system of gameobjects with composed modular components is the most utilitarian - it gets out of the way, and it's easy to avoid spaghetti without requiring a really strict engine-dictated structure.
- Godot. I hated it. All of the awful heirarchy of OOP, a really poor builtin language, and "signals", which are meant to decrease spaghetti but only increased it for me. Maybe I was using it wrong? I very rarely use inheritance to the point of being bad at using it.
- Pygame, back when I first learnt to code. It's quite nice for small projects - it's procedural at heart but you can make your own OOP or functional layers over it. There have been some surprisingly large projects made in it.
I don't know Clojure, but it's interesting to see someone make a functional implementation of something that stereotypically seems like a good fit for OOP.
As a professional game dev who has years of experience producing real products in both Unity and Godot, I am totally not with you about your thoughts on Godot vs unity.
Godots signals are such a huge step up over Unity's built in classes having a lack of modularity.
How do you even make sense of that? Godot vs Unity basically have the same scene/node/component model except Godot does it better imo. Eg) What is the difference between a prefab vs a scene in Unity anyways? Basically nothing, it's just (I'm speculating) a tech debt mistake in their design, probably still going because of how light maps work today.
Unity's advantage over Godot is it's 3D renderer, built in physX, il2cpp backend for C#, profiler, general runtime performance and console support.
Godots design is objectively more cohesive, as Unity has simply splintered into 10 different design directions since ~2018.
I'm not trying to be a hater, I just think signals are a huge advantage for writing modular, simple stuff in Godot. I think there's plenty of great reasons to prefer Unity to Godot but "signals" is not one of em.
I think the original commenter just really likes "plug and play" solutions with a lot of hand holding which is what Unity is excellent at. The problems come down the line.
If you like things like Godot, Godot is the type of thing you will like.
Seriously though, Godot works way better for me using C# than it does with GDScript and the OOP structure means I can refer to classes by their identity.
It's definitely not a "way way better tool" and no serious game developer would say that. Everyone agrees that Godot has a lot of catching up to do but we're still using it because we believe in the foundation and what it will eventually become.
I'm a serious game developer, been in the industry for decades.
I would say that. Godot is a way way better tool.
There, I've falsified your claim.
But more seriously, it's a faster dev experience, it's more ergonomic, it's not cluttered with half baked "new" ways of doing things that don't do everything the old ways do. There's no reason to pick up Unity unless you are being paid to, IMO.
It does have hot reloading at some level. I use GDSCript for a lot of stuff and it hot reloads. And you can use live++ with it to have true top level hot reloading. We implemented a hacky live reloading for native code in shared libraries which works as well.
That doesn't make any sense. Unity may have a pedigree of being for beginners, but in recent years they can barely keep current documentation on their new systems. This smells like a comment from someone who has never used it.
Fully agree, in Godot a Scene and a Node are the same thing which makes it much simpler conceptually.
Also, having a true text based scene format makes merging commits easier than merging unity changes.
The core developers are too enamored with GDScript, but that's fine, C# and C++ (GDExtension) support is excellent. There are only a handful of API functions which are hampered by their need to support GDScript, so it's something you can basically just ignore.
You don't even have to use Nodes much, though they're great for lots of stuff. For real performance, you can drop all the way down to their thin wrapper over Vulkan and do whatever you want.
GDScript makes simple things simple and complicated things complicated. It's pretty good for the simple parts of your game. You can still use C# for its complicated parts as they integrate well.
What "sucks" about it? Since they added type checking it's been perfectly fine, besides the notable omission of nullable types (which is... a strange omission, to be sure).
No tuples. No structs. No real debugger. Extremely limited profiling tools. Autoformatting breaks code more often than not. No interfaces. Extremely weak async story. No refactoring tools, external IDE features like jump to definition. Built in editor is slow and buggy. No destructuring assignment, no enumerate for loops, no looping over key and value together from a dict.
GDScript is, in my opinion, unsuitable for anything more than a couple hundred lines at most.
I agree with this. I suspect it's part of a more general Godot philosophy that involves full in-editor workflows with highly detailed scenes, many nodes, and with small scripts attached to all of them.
Your comments are accurate, but they aren't the whole story.
> unsuitable for anything more than a couple hundred lines at most.
I work profesionally in ~5 code bases with ~10,000 lines of GDScript each. I think GDScript is great for UI widgets. I dont think you can say it has a 'line number' limit. It has a 'complexity' limit.
If your writing a ton of tiny little classes that do one thing (like play a sound when a button is pressed), then GDScript is probably the best language for that. Certainly c++ is not the best, and C# is perhaps worse as well. Note: GDScript doesnt need to be compiled _at all_. In unity, its common to have an annoying 'hang' every time you alt-tab from vscode back to unity, as the c# is compiled. In Godot, I can write a new UI widget directly in the editor, with no compilation hang, with hot reloading. It's simply a smoother experience for writing the mountain of basic plumbing that you write when making a commercial game. It's not all complex algorithms here, it's just a crapload of boring ass code. And GDScript is great for that.
Most of the code in games is tiny little things that manage sequences of animations. There is seriously so much small code to make a UI look and feel nice. Writing this code in GDScript is way faster and ergonomic than any other language I've used.
I agree, GDScript has many flaws, but from where I'm sitting (small studio, been in the industry for 10 years), GDScript is a massive boost for my productivity.
For complex stuff, I like to figure out the algoirithm in GDScript then rewrite it in C++ if it needs it. Works great. Again, massive increase in productivity.
Godot’s UI system is easier when you realize that Control-derived nodes are even more granular than even HTML elements. They’re more like Tailwind classes.
Yes, your UI will be a tree of Control nodes 20 levels deep, and that’s fine.
Your root UI node will probably be an HBoxContainer, or VBoxContainer, each of which simply arranges their children horizontally or vertically, respectively. Between those two nodes you can create 90% of UIs you’d want to.
I recommend trying Zenject in Unity! It provides a pretty good dependency injection framework, but more importantly in reply to your comment it has a very easy to use signal bus implementation!
Now I have not tried Godot so I don't know how the two compare, but for where I work Zenject was basically the a-ha moment that made it possible to develop Unity apps/games that don't devolve into a huge unmaintanable mess.
A lot of people made a lot of noise about it and it hasn’t been long enough to tell for sure, but there is no real competition to Unity in many situations without a lot more work falling on your dev team.
Unity is a bit of a mess but to compare apples to apples when Unity has more features seems not quite fair.
Are signals significantly better than Unity events or is it more that the API uses them heavily?
Honestly, why doesn't Unity retool SendMessage() and public API callbacks to be an exposed Unity event? Unlike merging prefabs and scenes, that's not even a breaking change.
> Godot. I hated it. All of the awful heirarchy of OOP, a really poor builtin language, and "signals", which are meant to decrease spaghetti but only increased it for me. Maybe I was using it wrong? I very rarely use inheritance to the point of being bad at using it.
Not game dev either but I do have to say that I find Godot's design to be one of the best oop desings I worked with.
I tend to not use oop much today (C++ embedded), though I did learn programming with C# so I'm fairly used to inheritance.
Godot is excellent, and I believe it will be the "Blender of game making" in 5 years or so. Version 4 is ready for prime time and is able to tackle most indie projects. Where Unity beats Godot is in the "Triple I" and "Double A" categories, as it has better 3D/performance features, tooling and add-ons. Neither engine is the best choice for AAA projects.
For 2D and simple 3D games, I see no reason to use Unity anymore; I predict a steady decline in Unity's market share as Godot slowly overtakes it.
> Where Unity beats Godot is in the "Triple I" and "Double A" categories, as it has better 3D/performance features, tooling and add-ons
While this is true it does come with a big caveat. Every single AA developer has had access not only to the Unity source code, but in some instances also to Unity employees who became directly embedded in the production cycle.
There’s a GDC presentation from the team behind Ori and
The Blind Forest where they talk about how some Unity devs were flown to Austria to help out with custom tooling. Playdead also has a really nice blog where they talk about all the changes they had to make to the engine in order to achieve good lighting performance/fps for Inside.
https://blog.playdead.com/articles/inside_presentations/insi...
I’m sure that if the Godot foundation had enough resources at hand then you would also see more games like Cuphead and the ones previously mentioned above.
I'm a commercial dev, previously used Unity, now using Godot. This comment rings true of GD Script and signals. I get around this by using C# and its event handling.
Working with nodes and editor bugs (or features? Hard to know really) is probably the most frustrating - you can really feel that this was initially built for 'smaller' projects.
For this reason, I rarely use the editor outside of setting up the scenes and a general hierarchy.
Daily tools: Emacs (with C# LSP) most of the time. VS Code for debugging. Godot editor for tweaking the scene tree.
Perhaps this perspective is useful for other devs thinking about Godot.
I gave Godot a good shot. There's reasons not to like it, but IMO GDScript is not it. It's basically python with slot/emit semantics to integrate with the editor, which is actually rather nice, compared to other more complex ways of achieving such integration - via build systems, or some metadata or external configuration files. In Godot it's integrated into the language.
> Godot. I hated it. All of the awful heirarchy of OOP, a really poor builtin language, and "signals", which are meant to decrease spaghetti but only increased it for me.
Having programmed for over 25 years now, this was my experience. I feel like a little more IDE introspection or tooling to make the signal/listener connections more manageable, is the special sauce that's missing. The event pubsub moel becomes unmanageable to document/control with any non-trivial application. Godot has no encapsulation or tracking support, allowing all modules to hook to any event leads to spaghett, which you learn when working with large projects in various languages (JS et al).
I disagree that GDScript is a poor builtin language. It is somewhere between Python and JavaScript. It gets the job done and there is a lot you can do with it. Signals are great for things like state management. I'm not really sure what you mean by spaghetti code and how signals are supposed to help. I do agree that there are some annoying OOP aspects like providing a path to a scene (module). It's verbose but not unlike library imports in other languages.
When using the optional typing in GDScript, I'm surprised at how many errors are caught before runtime. The optional type syntax is nicer than Python's and the auto complete is good.
Godot could have built on Python, but they would have had to also include a language server (and maybe ipython or jupyter or something) to get the full seamless experience that GDScript gives. Including all that seems a bit much.
So, like you, I appreciate GDScript and have sympathy for why it was created.
Also, GDScript hasn't done anything like implicit type coercions (see JavaScript) and so GDScript can improve without breaking backward compatibility. If needed, breaking backwards compatibility in GDScript won't be as bad as breaking backward compatibility in a real programming language, so they can steer it where it needs to go for the benefit of Godot.
I had similar fun with Don't Starve when I opened up some files in the game directory and noticed they were all Lua, all using an ECS system, and easily extensible. Having never used Lua before, I made a rudimentary multiplayer server for it by opening a socket and sending game events to another instance of the game on another machine. I planned to actually start making it into a multiplayer mod but then they announced Don't Starve Together and I scrapped my implementation for obvious reasons.
I find libGDX to be highly underrated. For me it hit just the sweet spot between being too low level and too fancy with Entity Component System for everything.
I've been a game dev (and services dev) for a two decades and I could not disagree with you regarding Unity and Godot more strongly.
Having gotten into Godot with v4, it was a huge breath of fresh air coming from Unity and Unreal (which isn't really hobby friendly).
Composition of nodes is quick, easy to compartmentalize, and having signals as an interface makes building them very, very quick. Yes, the debugging story on Godot isn't quite there yet, but I genuinely doubt I'll ever touch Unity again unless I have to for a job.
It's a (2D only) game engine I've been putting together for years and obviously not yet at a level comparable to Godot, maybe more like haxe or lowe2d.
The target is simple single player, single developer "weekend" games.
> - Bevy (Rust ECS engine), which is nice at first but has a lot of problems with its implementation and can become rather messy.
Can you expand a bit about why this was messy or comolicated? I found the paradigm leads to pretty well organised code (sometimes you get the odd large system, but it can be broken down into smaller systems, sub systems or composed out of smaller functions).
I started using Bevy for a small game and abandoned it after a little while.
There are two issues:
1. The fundamental issue is that the ECS model has independent subsystems communicating via a relational database. This breaks the connection between function callers and callees, makes control flow incredibly hard to trace, and means the type system can give you very little assistance.
2. Bevy also does not leverage Rust's type system in other ways. E.g. you can use resources that you forget to create and it will only crash at runtime instead of giving a compile time error.
I think you can create a good game in Bevy, and if you come from a C/C++ world you probably won't notice the lack of type safety. It didn't meet my goals, however.
> 2. Bevy also does not leverage Rust's type system in other ways. E.g. you can use resources that you forget to create and it will only crash at runtime instead of giving a compile time error.
Would something else even be possible in Rust? My understanding is that you cannot really add type safety in the way of "This should be a i64 and between 32 and 128, otherwise fail to compile", so not sure how it could be addressed by the language.
Or taken to the extreme "Fail to compile if the user creates an instance of this but doesn't call function F with that newly created instance"
Let me explain a bit more about "you can use resources that you forget to create and it will only crash at runtime instead of giving a compile time error".
Plugins are the main abstraction in Bevy. A plugin has two parts: build and run. Build creates stuff, and run uses stuff created by build and created by the build of other plugins running at the same time.
Build is pure side-effects, so the result type of build tells you nothing about what it builds. Therefore there are no types that can constrain what resources run uses, and therefore failing to create a resource that is used in run is a run-time, not compile-time error.
The alternative is the build returns a type representing the collection of resources it creates, and run's type is a collection of resources it uses. This requires some type level programming (a type level heterogeneous set). This is some of the simplest type level programming, but type level programming itself is quite foreign to most programmers. I assume this is why the Bevy developers went for the easier to write solution that doesn't enforce constraints at compile-time.
More generally, just like in regular programming some things are easier and some are harder to do in common type systems.
Your first example (integer constrained to a range) is harder because you need to solve linear inequations at compile-time, which is not a feature of most type systems (though see refined types).
You second example (must call a method) is relatively easy with linear types, as they express "must do something with this value".
> You second example (must call a method) is relatively easy with linear types, as they express "must do something with this value".
Interesting, could you possibly share an example on how that would look like in Rust? Haven't come across it yet, and would certainly help with some things.
Lets say we want to make sure if "MyStruct" is ever defined + created, we want to make sure the program somewhere calls "register_struct" with an instance of that struct.
Rust does not implement true linear types, only affine, so I believe your parent is mistaken.
You can do a dynamic check to encode this, but that's not super popular. You can also issue a warning, and in theory turn that warning into an error, but it may also trigger on code unrelated to the specific struct you want it to, so I don't think that's a full solution either, and others may use your code without the warning, getting less guarantees.
While any design requires discipline, ECS systems have little or no coupling between them, and they very easily end up all over the place; in addition to the chaotic design, one ends up also not having an idea of what happens when.
For this reason I agree - developing games using an ECS design requires more discipline to manage complexiy, compared to an imperative one.
I've not had much issues understanding what runs when, because for the most part I didn't need to care that much, and when I did it was possible to order/schedule systems in Bevy. That's even a bit easier nowadays too, since the API improved!
With regards to the chaos: I think it can be avoided, but like you said it requires a bit of discipline. I don't feel like it required that much more than normal software engineering (but I'm also the type of person that documents and tests everything even on personal projects lol). The plug-in system makes it easy to help bring order too.
This is all through a bevy-tinted lens, since I've done very little game dev (dabbled with UE and Godot) outside of bevy, though!
I agree with this - ECS lets you write totally decoupled, composable bits of game logic very easily, which is super powerful. But in my experience if you're not careful you end up with a hundred perfectly decoupled little things and it's really difficult to remember how they actually all come together to make the whole game you've built.
Easily grokable filenames and file structure are very important, and bevy specifically has a pretty nice plugin system which lets you really clearly 100% isolate groups of state/logic into more sensible sections instead of ultimately having some root level game loop that's directly setting up your 100 tiny little poorly named things.
Yeah, I'm one of Bevy's maintainers, and I've seen folks get tangled up (or overengineer things wildly) if they go in without a clear plan. ECS (and a strong compiler) makes things much easier to refactor, but I generally agree that the more flexible nature demands more discipline.
I'm not that familiar with Godot but the built in language always felt like a misstep. Maybe someone more familiar with it can make a defense.
I view 3 users
1. Complete Novice
2. Engineer dabbling in games
3. Professional game designer.
For #2, they are more likely to prefer C# as they probably already have experience with it, or otherwise will be familiar with the similar Java language. Also it's a nice resume boost to say you've used C#
For #3, I can't imagine the godot language is better for large scale games than any custom language. C# just has way more resources put behind it
For #1, I kinda see it. But they would probably be better learning a language with more tutorials available. Or if they're struggling, pygame is probably a better place to start.
I wish that was true. I just started rewriting my project from C# to GDScript today so it can run on 32 bit ARM android devices.
Let's say I'm not a big fan of the language. Why the hell they decided to not implement normal for loops, was just googling how to iterate an array backwards and most people were like "just invert the array and iterate over it, bro"
They should have just used Typescript, it's a sweet spot between safety and productivity.
After reading several posts above yours describing the level of effort and pain trying to use this scripting language and how multiple people in this thread avoid trying to use it as much as they can, then reading your small code sample, it's hard to not describe as a level of perception whiplash.
"why god why can't they just have for loops!?!?" and the problem was just ... knowing how to foreach over a range correctly? the idiom and language syntactical preference that python, a language older than java, has done for decades...?
Really trivializes every complaint and the assumed skill level of every person that I see in this thread complaining about gdscript to the absolute lowest level. Thanks for the post!
The doc states that range function returns an Array, so it looks like to iterate backwards you basically allocate array of indices and use those indices to get values from an original array. Not much better than reverting original array, if you will ask me.
Maybe there is some optimization for trivial scenarios, but the referred doc doesn’t mention it.
It really looks like python 2 case when you needed to remember that dict.items() returns copy and most of the time you needed iteritems().
While that's true, the caveat here is that unlike Python, GDScript does not have a garbage collector. The main performance problem with creating an array copy in a soft-real-time application like a video game is usually not the allocation, but the additional GC pressure, which could cause frame jitter. GDScript does not suffer from that latter problem.
This is one of GDScript’s right spots for sure. But if this is the performance bottleneck (in a dynamic scripting language) you’re facing, then maybe that bit of code should just be a GDExtension, written in C++.
I would imagine for most games the performance impact from this won’t matter much.
The problem in this case is not the potential performance bottleneck, but the bad ergonomics for a dev, that leads to the complain. The rest is consequence.
And to me it looks like pure language/stdlib problem. Like for me, the proposed solution looks ugly both from syntax and what is going on behind curtains perspective.
I would rather not to code in a language that makes/tolerates decisions like that.
I agree! I use both GDScript and C# with Godot, and GDScript is excellent. But, I’ve been programming for 40 years, and my baseline is “all programming languages are bad” :) All I need is a Turing machine with nice ergonomics and I’m happy. I’m very easy to please.
I had high hopes when I saw the yield keyword in the reference, but then saw it's basically just a reserved keyword as they don't seem to actually support generators. I would guess the less eye-bleeding version of all those 1, -1 things is just making a ReversedIter and using that: https://docs.godotengine.org/en/stable/tutorials/scripting/g...
I presume they do for loops the same way Python does? Which takes a couple of minutes to get used to and is perfectly fine. I wish C# did this more like Python to be honest.
GDScript is closely tied to the engine, making it incredibly easy to affect any part of your project. It's also very terse and simple to use, and anyone with a passing knowledge of Python should be able to pick it up in minutes.
I've used C# for my day job for years, but never felt the need to stop using GDScript for any of my projects.
I don't use godot and know more about its limitations than its advocates in this thread. Are they being disingenuous? Do they just not know basic information about their own tools?
I'd recommend giving Raylib a look if you haven't. I've been working on a couple small ideas using it lately and it feels very aligned to being a game 'engine' for people who come from a more classic software engineering background.
If you need to customize the behavior of certain interactions (such as modifying physics with a specific type of object), you need to subclass since some behaviors are only available as overrideable functions, not as signals. Also the documentation tends to push this approach.
Godot much prefers composition over inheritance. It's possible to use inheritance in Godot, but no-one recommends it, except maybe for a data structure.
- Bevy (Rust ECS engine), which is nice at first but has a lot of problems with its implementation and can become rather messy. I think it's heavily dependent on the game. Part of it will be my own incompetence.
- Unity. IMO the system of gameobjects with composed modular components is the most utilitarian - it gets out of the way, and it's easy to avoid spaghetti without requiring a really strict engine-dictated structure.
- Godot. I hated it. All of the awful heirarchy of OOP, a really poor builtin language, and "signals", which are meant to decrease spaghetti but only increased it for me. Maybe I was using it wrong? I very rarely use inheritance to the point of being bad at using it.
- Pygame, back when I first learnt to code. It's quite nice for small projects - it's procedural at heart but you can make your own OOP or functional layers over it. There have been some surprisingly large projects made in it.
I don't know Clojure, but it's interesting to see someone make a functional implementation of something that stereotypically seems like a good fit for OOP.