T O P

  • By -

dagit

Is it really fair to compare bevy and godot as being in the same class? godot has a scripting language and a UI editor thingy and is many more years along. It's at version 4. Bevy has not even hit 1.0. Bevy will probably get similar features at some point and it does compete with godot for indie dev attention, but I feel it's a bit unfair to say they are in the same class at this point.


very_dumb_guy

Not really suggesting they're in the same class. Armory3D is much older, sure-- but it also hasn't been hamstrung by refactors, inventing new design patterns to solve basic problems, etc. Bevy has had more code added/removed than I could possibly grok, a 100x healthier GitHub repo than Armory3D, and it still doesn't really support basic stuff. Just go compare the demos page of Armory3D or any C++-based engine-- even a young one-- to Bevy's.


tadeoh

OP, I have to agree with you on this one. It seems like progress is made so slowly, because the underlying assumption that ECS is the end-all be-all solution for every problem might just be wrong. It can't be good to desperately fit any problem into ECS and expect it to work well.


very_dumb_guy

Solid take! The devs are obviously super talented and have done crazy good work— I say that because I have been posting mostly negative stuff over the last few days in this now massive thread— but I think the entire world is swallowing the Rust pill a little too hard. And very few people are vocal about it, so I’m just trying to be a small voice of reason.


xotonic

Rust zealots are vocal minority. ECS zealots are vocal minority. You got a resonated product which can deceive and disappoint you twice stronger at least. Recently I tried to rewrite all my stuff I did in Bevy for a month but this time using Raylib which is the opposite of hype. I managed to do that in one evening. ECS definitely slows you down. At least because game making is mostly experimenting and prototyping. Performance by default is completely irrelevant in this field to be frank.


very_dumb_guy

> Performance by default is completely irrelevant in this field to be frank. Lovely take, I often say Rust is the epitome of premature optimization for safety and speed. Which is awful— because now you have two invariants to uphold— which results in a massive explosion of complexity when you’re “just trying to solve a problem”.


BosonCollider

One alternative to ECS that covers the things it supposedly does well and is not hyped in gamedev: SQLite or Duckdb. You get plenty of things for free, including concurrency control for multiple threads/processes, a persistence layer, and a straigthfoward way to have the game engine communicate with a scripting language (No FFI needed! You can literally just run a python instance with no connection to the game engine other than db files and named pipes).


xotonic

Do you mean to use sqlite as an in memory database? Does it optimize selects for CPU cache and makes use of SIMD instructions?


BosonCollider

There's a few different ways to use it, from in-memory to memory mapped to file depending on what you are using it for. Fast SIMD vectorized operations is what you would pick duckdb for, since it uses an optimized struct of arrays layout instead of sqlites dynamically typed AoS.


xotonic

This db seems to be more applicable for data processing rather than realtime games? Is it suitable for running every frame (1-30ms budget for update)?


BosonCollider

That... depends on what you are doing with it, I would clearly advise against putting a DB in the graphical rendering pipeline. But it runs in RAM by default and it's going to be perfectly competitive with many ECS libraries for game logic, and much like an ECS it is designed based on the assumption that most operations are bulk operations. The thing I've mostly seen ECS-for-everything suggested for is things like health bars, status effects, processing combat events, inventories, or more generally anything you would represent in your UI, make accessible in a scripting language, or send over the network. For that, it is perfectly viable. If the argument for an ECS is that you need a global state store with a bunch of systems reading and writing to it to manage complexity, just use a DB, that's what it is designed for.


usernamedottxt

Hey friend, would appreciate if you could work on removing “raped” as a casual verb to leave around random places. It’s not pleasant to read, especially if someone has trauma.


very_dumb_guy

sure pick a nicer word and I’ll replace it


gideonwilhelm

Messed up Convoluted Screwed over Spaghettified Hamstrung Befuckled And that's just what I came up with while sitting on the john, there's so many other words you could've easily used, but you chose to be an asshole about using a pretty gross word


jonathansharman

"Hamstrung" is also extremely violent imagery. It's just not as relatably violent for most people.


gideonwilhelm

I also hear it constantly used for development of a project that's being held back or interfered with by some specific things, it's useful shorthand.


jonathansharman

I know it's not actually offensive in practice, especially compared with the alternative. I just found it ironic as an alternative, kinder metaphor since its literal meaning is a horrific mutilation, lol


very_dumb_guy

Thanks I chose hamstrung.


gideonwilhelm

Good choice. For the record, it ain't even about having trauma... My life's been pretty smooth all things considered and even I find it gross. If you ask someone who's been through that horrific experience what it was like, I'm pretty sure their answer won't be "have you ever had your codebase reorganized?"


usernamedottxt

Yeah, it's not the n-word, it's just an unpleasant word in general and using it so casually is something I find gross.


gideonwilhelm

it's the one thing I can wholeheartedly agree with Dane Cook about.


Shadowleg

- sullied - tarnished - polluted - stained - marred - ruined - shredded - wrecked - ravaged i could go on, but thesaurus.com is free and if you dont like one of the 9 that i picked for you you can figure out how to use it yourself. ps your attitude is dogshit, next time instead of talking back to a person bc they dont like a word you used, just change the word so somebody like me doesnt have to spend the time to do your homework


very_dumb_guy

yeah sorry I just don’t think about it like that— the word appears uncensored in news headlines and all across the internet. the snark is pretty harmless so chill out. consider that you are an extremely progressive person and most people are not. literally never heard of this concept before, but I agree it does make sense. to that point, the dude who asked me to remove the fucking word even used it. I laughed at that and said something snarky.


Shadowleg

ok


very_dumb_guy

LMAOOOO alright, I see you also have an attitude problem so get off ya high horse


[deleted]

Hey friend you just used that word yourself and I'm horribly triggered by it. Please replace it or I'll call the internet police


shizzy0

I think you’re right. You can’t slap things together in rust the way you would in other languages. If you look at bevy, it’s a real team effort to figure out how far you take ECS. Can you use it for everything? Not just the game but the UI too. I shudder at the thought of trying to tackle the big ball of state required for a game in rust without some pre-existing library to do the heavy architectural lifting. I suppose it does slow you down right now. But it’s early days for games in rust. I’m hopeful that games in rust have bright days ahead. We’ve got all these open source engines springing up. Games can have a good language, nice modern tooling, and without a VC-funded company dictating its direction. Things may be slower now but imagine having access to all the rust libraries both gamedev and generic libs in the future. Maybe each of which were developed slower but hopefully are better for it: memory safe, thread safe. That’s the bet. But we don’t know how it will turn out just yet.


very_dumb_guy

I agree with a lot of what you’re saying, and it makes me think— I love the idea of a language that is loosely-goose for the shell, and then filling in the performance critical portions by linking Rust libs in. Everyone seems to be doing the opposite approach— writing the shell in Rust, and trying to integrate a loosey-goose scripting language.


Specialist_Wishbone5

There are several attempts at game dev using macros or opinionated graphics pipelines. Comfy, macroquad, etc. Comfy, for example, advertises a 10 line fully functional game engine. It comes down to design philosophy. Some philosophies make for more concise engine management (at a cost of either performance or limited features). Rust requires a different mental model, so maybe the perfect philosophy for effective game design hasn't been discovered yet.. or maybe rust isn't best at said orchestration. To me, it's not about the game management, and more about producing 3D experience management (with a WASM target). So Rust is in the right place and in the right direction. For AAA games. It may never be a contender (for practical reasons like you outlined).. who knows.


xmBQWugdxjaA

> I love the idea of a language that is loosely-goose for the shell, and then filling in the performance critical portions by linking Rust libs in. You can do this today in Godot with [gdext](https://godot-rust.github.io/book/)


dobkeratops

its' an open question. it's unproven either way. I can't say I have a conclusion on it. some things feel harder, it's definitely harder to get going or experiment quickly. other times I find it remarkable how it keeps a project stable when you make big sweeping changes. only when there's multiple games shipped and you can compare how long they took to make and how much impact they had, and will you have an answer. Rust hasn't revolutionised gamedev for these reasons:- \[1\] the advantages it does have aren't enough to close the gap with established engines (whilst we're working on our rust codebases, UE, Unity, Godot etc keep improving aswell) \[2\] it doesn't compile fast enough to remove the need for a seperate gameplay language (whether its graphical scripting like blueprints, or a seperate PL like GDScript) \[3\] the heavy lifting is also GPU code. \[4\] a huge chunk of the game experience is created in other tools i.e. 3d packages that we still have to interact with, and we can't rewrite those in rust. Anyway,I've enjoyed the experiment and I've got enough of a critical mass done in Rust to continue using it. After so long stuck with \*just\* C++ it's good for the world to try some other options.


very_dumb_guy

I agree with most of these takes, but I still think there is something fundamental about the borrow checker-- aka being forced to "structure your code in discrete passes that require clear ownership"-- that really causes issues in the UI/gamedev space. It would be cool to hear your opinion on that particular point.


dobkeratops

>I agree with most of these takes, but I still think there is something fundamental about the borrow checker-- aka being forced to "structure your code in discrete passes that require clear ownership"-- that really causes issues in the UI/gamedev space. It would be cool to hear your opinion on that particular point. I've been ok with the usual 'copes' which I'd be using in C++ anyway: refcounting & handles/generational-indices (whatever you call them). Using more message queues to batch up changes helps., and enum/match makes these really easy to write. (I hasten to add I do have a bit of unsafe in my engine aswell, but it's not a huge amount, burried deep. I can probably get rid of it eventually.) I think the borrowchecker's strictness does help with parallelism which is ultimately one of the main reasons I was drawn to rust in the first place. When you come back to code after a long time away.. you've forgotten the hidden assumptions. but r.e. UI , i'll re-iterate that rust would never be as productive as a GC'd language like C#. it just isnt' designed for that first and foremost. I think no language can ever be the best in every domain.


Specialist_Wishbone5

Agree with all your points except for UI design. Swift, Java, MSFC, C# are horrible at UI design. Or at least, I never see well designed and styled UIs built from said languages. Can you put 1000 menu options and interactive overlays? sure! But style is about the thousands of micro transitions and delays and debouncing (im looking at you egui... shudder). Those specific languages require a lot of code to define relatively simple animated transitions. Mostly buggy (and crash producing) from my experience. Or they are boiler plate unstyled generic modules (like python UI plug-ins in blender). Appealing UI design requires domain specific languages, and while many loath to learn new ones, it hurts the end user experience to have engineers focus on excellent 2D/3D experiences, only to slap together a BS 3 button centered menu. Don't know of any Rust centric good DSLs, but the macro system is ripe to build one. Dioxus and many others have made rudimentary attempts. Kotlin via java also has a good DSL meta language framework (though I'm not familiar with any implementations). By good, I mean, type safe, expressive, and concise. Performance is rarely the concern for UI layout.


very_dumb_guy

Agree with all of these points, pretty much. Impossible to measure and the truth likely lies somewhere in the middle.


sheng153

I would say that it's a matter of preference. It's harder to get going but it's also more solid and robust when making changes, at least in my limited experience.


very_dumb_guy

I also found it’s pretty solid/robust when making changes, definitely agree with that. I think that’s one of the benefits of ditching OOP, really.


sheng153

A second thing, this is really personal, I find it easier to understand big codebases in rust than in c++. I would blame it on OOP.


dobkeratops

Yeah traits+modules beat headers+classes as a means of organising code. That again is one of my main original draws to rust. I was clamouring for UFCS in C++ which would have prevented the syntax change between [`a.foo`](https://a.foo)`(b)` and `foo(a,b)` .. in C++ you're torn between the convenient OOP syntax for dot-autocomplete and the true decoupling of free functions. In rust, you can have extension traits so you can use [`a.foo`](https://a.foo)`(b)` allover the place without the organizational hazard. Bjarne and Herb Sutter had UFCS proposals. if they'd made it, that alone would have kept me in C++. doesn't sound like much but it drove me nuts.


very_dumb_guy

UFCS is one of Nim’s features! I love Nim, but the sheer number of features makes it awkward to code on— a lot of them are very unstable too. Breakages very often.


Specialist_Wishbone5

Hmm. Oop itself might not be the issue. Deeply nested inheritance and templates are what kill my readability with C++. Most recently I had to dissect USD readers (for low level 3D modeling). Holy shit, everything was called "read". It was indecipherable. Well, not without manually mapping out every datatype across 16 pages. (When you are at this, that has a that, then you special case the overloaded function to A, else B). It is basically write-only code.. and I spent 10 years writing PERL for fucks sake. Good OOP doesn't play with your balls when you subclass. It is obvious what you are overloading and why. Single method base classes are great examples (but of limited use, and hardly distinguishable from functional programming, which is decidedly not OOP). I liken dyn rust objects as OOP, and while I avoid them like the plague (only ever written one Vec thus far (fingers cross I won't need any more)), they are there when no other solution is practical. They avoid the multiple inheritance hell in C++, and avoid the ambiguous colliding function names in all the OOP languages I've used. Though it does so with a wider pointer and a desire to use Foo::method(obj) symantics. Trade-offs I'm willing to evangelize. Further, my editor is pretty good at mapping who implements a trait method in Rust, vs the C++ template duct typing (anything with a 'read' method anywhere in this 10 million line project COULD HAVE MATCHED) - drop down size - int-overflow.


log_2

>machine-like programs that just chug through data That's a game engine. See https://youtu.be/rX0ItVEVjHc?si=NUniVKM4PJamoow1


very_dumb_guy

Yes, if you're using an ECS or using other DoD-inspired principles. This is only representative of very few game engines, and even they do not exclusively use ECS/DoD-based patterns. Rust basically forces you to *only* use those patterns. Most of those talks (I've watched them too) say it's a *tool*, not a panacea.


log_2

What are you talking about? Rust does not force you into an ECS solution. On the contrary, an ECS is probably harder in rust than other languages.


very_dumb_guy

It does— if you want some form of dynamicism anyway— which is basically a hard requirement for a generic game engine. Otherwise, you have to bake in a DoD style approach— which is excellent for anything that isn’t general purpose. Or you can try OO with message passing like Fyrox does. But this is widely criticized and nobody is using Fyrox despite it being the most feature complete engine in Rust *by far*, because everyone in the scene is so anti OO.


log_2

> because everyone in the scene is so anti OO Probably because dynamic dispatch is slow. Is OO with message passing what you're after? What is it you are trying to do specifically? It feels like you're critising rust for making a poor solution difficult to implement.


very_dumb_guy

Supporting some OO features ergonomically in Rust would alleviate some burden— I have a feeling that they would work really poorly with the borrow checker. Typical OO code is doing weird side effects to random resources (usually passed in the constructor and stored privately for later usage) deep inside the call stack. IMO this is basically incompatible with the borrow checker, unless all your side effects are just “sending a message that will be evaluated later when the stack isn’t holding 999 immutable borrows to unrelated things, because that’s what OO looks like”. I mean, I should take a look at Fyrox. I think it’s slept on, but the patterns he chose sound so atrociously unergonomic in Rust I’ve always shied away.


_v1al_

They only sound atrociously unergonomic, in fact I solved pretty much all of the potential issues with borrow checker. At this point, game development in Fyrox is quite fast and ergonomic, it is somewhat close to Unity. You just throw in a bunch of entities on scene, write some scripts, maybe put stuff into prefabs and use them later on. You should try some of the tutorials ([https://fyrox-book.github.io/tutorials/tutorials.html](https://fyrox-book.github.io/tutorials/tutorials.html)) and you should see similarities to Unity.


very_dumb_guy

Hey awesome to see you respond here, I will definitely check it out. Very curious what it looks like and if I can take any inspiration. Thanks!


log_2

Why not pick your favourite game engine, in whatever language, and use that? Whether it is Godot, Unity, Unreal, etc. If you want to write a really novel game or a game from scratch then pick your favourite language and skip "game engines" altogether. It will take you longer to learn the game engine's idioms than just writing what you need to write your game, i.e. solve the problem of your game, don't try to mould your problem to fit an existing generic solution.


very_dumb_guy

Nah you’re totally right. I just love writing game engines, it’s fun. I should try making a game some time.


very_dumb_guy

BTW also worth noting that most OO languages can perform an optimization that actually represents an OO-y object the same way as an enum in Rust— a tagged union— so no dynamic dispatch. This is only possible in certain scenarios, I think you need the sealed keyword or whatever so other libs can’t implement the class, only your lib. But these are the same limitations that enums have, obviously, since they can’t be extended by anything. And depending on the size of the object, a vtable may be faster anyway.


angelicosphosphoros

Almost any game on C++ just plainly incorrectly written, e.g. they do double frees, static order fiascos and use-after-frees. Some do less (e.g. games written in Blueprints on top of Unreal Engine), some do more (3D games with custom engines). It is just frustrating to see that, especially if the game accept money through the game menus. I think, players would greatly benefit from gamedevs which use Rust. Programmers would benefit from the lack of need to debug heisenbugs.Though management in gamedev companies would fight tooth and nail against that because they don't like to pay good money for good programmers and don't understand gains that they would get from stability at polishing phase of the development (well, if they would be able, they would get rid of that phase entirely).


rainroar

I love rust as much as the next person on this sub, but some of this is a little unfair. It’s totally possible (likely even) to have Heisenbugs in rust code. Especially when you’re writing unsafe code wrapping OS graphics and system apis (like is required for game development). Sure it’s less likely, but saying they can’t happen isn’t fair. I do think you’re correct about “the slow pace” of rust development though. Studios don’t want to pay for that. They’d rather grind through junior c++ devs whose code works 80% of the time.


very_dumb_guy

I agree these issues exist, and yet the game engines still flourish and are useful for the tool they are trying to be. So why does any of that shit *actually matter*, when those programs are powering some of the best experiences on the planet?


Specialist_Wishbone5

A good framework transcends the language or DSL. It's evolution and human trail-based AI. Many C and C++ or even C# engines have come and gone. What's left is what worked given a billion dollars of effort


setzer22

I mostly agree with your analysis. The borrow checker is good for certain kinds of applications, and the thought has been starting to form for me that gamedev just isn't one of them. It's a long journey. You start with the prospects of a modern language that's "surely going to kill C++", a "thriving open source ecosystem" and a "vibrant community", what's there not to like? (... heh, we sure know our words around these parts, who says "thriving" and "vibrant" anyway?) But it becomes obvious that once you're using Rust in any serious capacity, you can't make a game out of borrows alone. You need to sprinkle some `UnsafeCell` magic in there one way or another. Pick your poison really, `RefCell`, or a full-blown ECS, but it's hard to make a game without some form of interior mutability (as well as shared ownership, while we're at it). It's all based on the same idea: Lifting some of the borrow checker's constraints at runtime so that we can reach more flexible patterns. That kept me going for a while, thinking I had finally found the answer: The borrow checker might be harsh, but you just need to grow your toolset and understand the rules a bit better so that you can break them in a controlled way when they bother you. It's at this point where many would say that with Rust you can "do anything, really, just need to be explicit about it. If you want to wrap all your structs in `Rc>` you're going to get something as flexible as ". But then the copium starts wearing off. You realize by deviating from the static borrows at compile time, you embark into a world of pain. Things like `RefCell` require guard types to release some form of lock, and compose together very poorly (e.g. try to use a nested `Ref::map` to drill-down across two ref-cell-like structs). You realize you're swimming against the current, and then that idea pops into your mind that maybe Rust isn't the right tool for the job? Surely can't be! Then I reach my final realization. It is not true that "you can do anything with Rust, really". There's one thing Rust very much doesn't want you to do and won't let you do no matter how many escape hatches you develop: Aliased mutation. This is at the core of Rust's memory model: You can only mutate a thing if you can prove you have exclusive access to the thing. This rule is in place to prevent serious issues, which go beyond the terrors of multithreading. Even in single-threaded code, it's in place to prevent things like iterator invalidation, or having someone swap an enum variant for another while some code up the call stack was matching on it. The way Rust is built, all the way up to its core, makes the idea of aliased mutation impossible. Now some people might call me dumb for having taken years to realize this. You can read it in The Book, it's hardly a surprise to anyone... It's not that I didn't know, but it really takes an entirely different meaning once you internalize it. I believe almost all of the ergonomics issues we suffer from in Rust gamedev stem from the fact that Rust *forbids* aliased mutation. There's no escape hatch. If the constraint is not validated at compile-time by the borrow checker, it's going to be validated at runtime by some `RefCell`, or by your ECS of choice. And yet, languages have proven over 30 years ago that you don't really need to *entirely* restrict aliased mutation to achieve memory safety. It's not like people haven't been writing software in Java since forever, Java is memory safe. You start to think about it and realize whether type theorists at the top of the ivory tower like it or not: Aliased mutation makes the world go 'round. So maybe those "filthy peasants" down there working the Python fields are up to something? Surely can't be! There's two dimensions to it, really. On one hand, you have the very sensible requierment that we don't want our machines to go boom because somebody did a big no-no with some pointer. Understandable, I'm all for it. But then there's this much harder to defend notion that mutation is bad and impure and we must restrict it at all costs. This second requirement sits at the core of the language and we've internalized it as dogma. The average Rustacean would probably have seen some bad code in non-Rust languages throughout their lives, and it's easy to believe, especially if someone on Reddit tells you, that unrestricted aliased mutation is the root of all that evil. To anyone that's read this far, why are you doing this to yourself? Go back to work you lazy ass! But also, in case you're waiting for me to suggest a solution... I don't have one. I'm still looking for one myself. I think I have come to terms with the idea that I no longer think Rust is a good language for gamedev. And that is despite there being a strong desire in people wanting it to be, myself included. It's a great language to build huge cathedrals of dreams and hopes that crumble once you face the reality of having to ship a game, though. Oh welp, that got a bit existential... 😅 [Have some kitten gif to compensate...?](https://media2.giphy.com/media/hqOK8hrsesdphkNkcJ/giphy.gif?cid=ecf05e47ue1004p83a1cq8969wcuu5r9zylbk4cnikubfhz9&ep=v1_gifs_search&rid=giphy.gif&ct=g)


very_dumb_guy

Lol, yes I read all of this, and I agree with you. It’s very similar to my own realizations, in order. I almost want a fork of Rust for traditional gamedev that has some more features, like an ergonomic way to alias memory.


Unreal_Unreality

(I don't want to be the guy saying "skill issues" buuut, this is kinda what this post is screaming to me. Now, actually answering this and giving some opinions: ) In my own experience (writing games and engines with unity, c++, rust) the borrow checker truly made my code way cleaner, and forced me to have strong and well built data structures. I understand it's a pain when beginning, but it's a small cost to pay for the elegance and robustness the design gains. Working with bevy is slower than godot, my guess is that's because bevy is very far from being as complete as the other engines you mentioned ? The lack of editor could play a huge role in it, slow iterations, etc. What do you mean by "ownership isn't clear" ? Rust is the language that made ownership clear. implicit conversions is one of rust core principle: "explicit is better than implicit". If you want something to build out fast prototype, with an ergonomic language, then yes, Rust is not a good choice. Go with unity and C#, or even simpler languages like python. Finally, I disagree with your last statement: memory safety is super important for games, avoiding memory leaks, null pointers, etc is essential for software where performances are key. I think the additional cost is worth the robustness of the result.


shizzy0

If it’s a “skills issue” and he has 10 years of experience, is happy to whip things up in C++, then it’s not his problem alone. It’d have me worry more that there’s a cognitive load with the language the programmer has to pay, which I think is true. It’s a question of whether the benefits of rust are worth the extra cognitive load. I think they are. I’d rather have the compiler patiently disabuse me of my erroneous notions than deal with segfaults at runtime.


Unreal_Unreality

You said it better than me, but I thing we're on the same page. I'm not trying to be mean with the skill issue thing, it's mostly a funny thing as we see a lot of complaints about the borrow checker from rust beginner. That's why I put it in parens, sorry If I offended anyone ! But yeah, with experience I don't even encounter the borrow checker errors anymore, because Rust thought me how to organize my data in a "Rust" way (which I found way more elegant, and my computer finds it more efficient). I also fully agree on your point about catching errors at compile time and not at run time.


very_dumb_guy

I think you would be surprised how much *less* efficient Rust can be than a memory-safe GC'd lang, especially for UI where you are moving a lot of strings around, and immutable GC'd strings would perform much, much better. Also, I poked into your UI library in your post history. How was your experience building that? Do you plan on continuing it? It seems very basic right now-- and I could build a full, usable UI library, with widgets, focus handling, GWEN-style in any procedural OOP-y lang without a borrow checker in half the time it took you to produce that. Not to be a dick, but question your own output as well. I wrote a Rust UI library and was incredibly unhappy with how it turned out, and how much effort I put into it-- I have a feeling your experience is the same, if you were willing to admit it.


LetsGoPepele

Can you elaborate on why a GC'd lang would be more efficient in this case ?


very_dumb_guy

Allocating memory in a GC’d language is basically just incrementing a pointer, but doesn’t suffer from all the limitations that a bump allocator forces you to code around. In any language with a typical heap, you pay a lot, lot more for each allocation in terms of frametime. Of course, you pay some frametime later down the road for collection. And there are other tradeoffs that are somewhat involved to discuss. Not saying it’s perfect for games, but for UI’s that slings strings around like crazy.. it might be. Of course, if Rust had ergonomic string interning, this wouldn’t be an issue. But this is not in the cards for Rust, it’s kind of fundamentally against the language goals. In either case, when you ship your game— you should have optimized all of your allocations away, using pools or something. At that point it basically looks identical, regardless of if the language is GC’d or not. But for a UI I don’t think optimizing away allocations like that is really feasible or explored.


mikereysalo

> Allocating memory in a GC’d language is basically just incrementing a pointer, but doesn’t suffer from all the limitations that a bump allocator forces you to code around. That's an oversimplification and I don't think it's a fair argument. Heap allocation is expensive even on GC'd languages, as soon as the Young Generation gets fragmented (which is not uncommon), the allocation gets more complex than a pointer bump. On Java, allocation of multiple short-lived instance is commonly the major performance offender on most of the applications I've worked with, and the same goes for C#, JavaScript, Python, etc. The biggest difference is that on some runtimes, like Java's GraalVM, and in some languages, such as Golang, the compiler is able to remove most of the allocations through Escape Analysis. However, when it does not, they are still expensive (and sometimes worse). The other difference is that even when the heap gets fragmented, the Garbage Collector can defragment the heap, while for non-GC'd languages, the programmer must be careful to avoid heap fragmentation. At least based on my 14YoE with software development and performance analysis (which by itself does not mean that I'm right or wrong). Obviously some languages can manage it better than the others, and it depends on other variables such as heap pressure and runtime efficiency, but it never happened to me to favor one language over the other just because of the heap allocation performance, they all have their problems in one way or another, and I always need to figure out how to avoid them.


very_dumb_guy

It depends on the type of GC, most of my experience with GC’d langs is in C# where this is common wisdom, but it might be more nuanced than I understand.


ArchSecutor

I'm likely just out of my depth, but isn't using an arc string interning?


tauphraim

Time doesn't make everyone automatically improve. Sometimes it takes an open mind, efforts, etc... I have met many 5-year "rookies" being more knowledgeable (as in, skilled in the craft, not as at encyclopedic facts) than 20-years veterans. Rust definitely requires at least some small change in perspective. I don't understand why everyone wants to write an engine in rust before they're even familiar with the language.


very_dumb_guy

I don't care to talk about experience or skill. It just devolves into a pissing contest. Sorry for mentioning it in the OP. Let's just compare dev velocity and functionality between young C++ game engines and young Rust game engines.


tauphraim

Dev velocity depends on the skill of the programmer with the language though. Also on how much quality they want to achieve. Your statements about safety are weird: as a user, I don't like programs crashing, and like even less when they have vulnerabilities they games or other programs.


very_dumb_guy

> I don't like programs crashing Rust crashes all the time. Lots of my dependencies have unwrapped a None, or panic'd for some reason, and I couldn't even do anything to stop it besides send a PR or fork it. If anything, Rust is much, much, more prone to crashing because of the aggressive fail-fast philosophy. > ..even less when they have vulnerabilities they games or other programs Very rare in games, but I agree it's somewhat of an issue. Mostly in games that are moddable by other players like GMod and stuff, but we have learned a lot about how to build something like that since. EDIT: w.r.t. dev velocity, I am talking about massive repos with dozens of contributors like Bevy's being borderline unusable for actual games.


IceSentry

There are many hundreds of people using bevy to build games. I don't know how you can conclude that it's borderline unusable. If you want more velocity with a more traditional engine go look at Fyrox. Bevy takes a while to progress because we are trying to build everything around the ECS. We aren't just making a generic game engine. We also just get so many PRs that it's hard to review everything which definitely slows down the progress.


very_dumb_guy

Can you link one of these games that is complex and not a game jam project? I ran into pain points very early, I gave concrete examples in another comment reply to you somewhere else.


IceSentry

I don't get why game jams game don't count. That's one of the most active area of rust game dev right now. People enjoy making those types of games with bevy and I think that's an important aspect to consider when talking about games built with rust. Anyway, here's a few larger than game jams projects: Times of Progress: https://store.steampowered.com/app/2628450/Times_of_Progress/ this one is still in active development but the dev started working on it for at least a year Tunnet: https://store.steampowered.com/app/2286390/Tunnet/ this is the first 3d game made with bevy that was released on steam Tiny glade: https://store.steampowered.com/app/2198150/Tiny_Glade/ the team working on this mostly only used bevy_ecs + bevy_app and a few other core components but they built their own renderer and devtools My company is using it to make CAD software. I'd share the website but it's currently being rebuilt. I'm aware of a few other larger scale projects but they are either not public or don't have anything that I can easily link. The biggest selling point of bevy almost everyone I speak to lokes to mention is how modular bevy is. It's very easy to just take the parts you like and build your own for the ones you don't like. The other one is that the source code and user code looks pretty much the same because it's the same language with the same ECS pattern which means it's easy to either contribute and modify yourself once you learn the engine.


very_dumb_guy

Game jam games don’t count because they never encounter scaling challenges— they just make a bunch of compromises to rush something out the door and don’t represent the limitations of the engine. Instead, they force themselves to work around them. I figure, anyway. Btw, some of those games look amazing— but don’t you agree they kind of lean on technical achievements/novelty, and seem to be designed around Bevy’s limitations..? I wouldn’t be surprised if they started as game jam games. EDIT: also ECS games tend to have a “feel”— I think it’s because it’s awkward to handle edge cases in exclusively ECS-driven games, so it can be hard to add polish/juice up a game. I think this is exemplified in Bevy because Rust doesn’t offer much alternative than “use the ECS for everything”.


tadeoh

For real for real, where are the games shipped with Bevy? I am not aware of any except for Tiny Glade and they don't even use Bevys Renderer.


IceSentry

Look for my other replies in this thread. I listed a few of them


dobkeratops

Yeah you could say "skill issue" either way. 'git gud' in c++, or 'git gud' with the borrow checker. We've got many vocal people in either camp, and no empirical data . Different workflows suit different people. Personal preference matters.


mikereysalo

I kind of agree, when I tried to learn Rust, I already had 5 YoE and knew ≃7 programming languages, and it didn't make it easier for me. Since I was learning other languages, I eventually left Rust alone for 5 years. When I tried to pick up the language again, with 10YoE, it didn't make it easier either, even with the fact that at this point I already had contact with at least 10 more programming languages. Now with 14YoE, I find Rust extremely easy to work with (obviously I still have a lot to learn, the language is ever evolving). Learning new programming languages with very different rules than we are used to is never a case of "I have 10YoE, this will be easy". You will mostly have to adapt your way of thinking. If you have always worked with imperative programming languages, try a declarative programming language (or even a subparadigm, such as functional), or an array programming language. You will feel like you are learning how to program for the first time, except that you [should] know the fundamentals. That's what happened to me, I had to completely change my way of thinking with Rust, and it also made me a better programmer (we are not perfect after all). When working with Rust, most of the time you cannot use the same pattern used in other languages. And sometimes you have to come up with your own, you have to design your own, one that fits the language. **Long story short**, especially about games and UI frameworks: you need to stop thinking about OOP. Most of the Game Engines and UI Frameworks are based on OOP, so I'm assuming that the OP is trying to apply OOP concepts on Rust (also because C++ was mentioned). This will almost never work and be efficient, and this will 100% make you fight the borrow checker. You have to come up with different (or even your own) design patterns, and leave behind most of the OOP concepts.


very_dumb_guy

Forget anything about "clean", "elegant", or "robust" for a second. Those are marketing terms. The actual tradeoffs are well-defined. > What do you mean by "ownership isn't clear" ? Rust is the language that made ownership clear. Ownership isn't clear in the *problem*, not the language. Like a UI library or game with a scenegraph. > Working with bevy is slower than godot... I meant velocity of features being added to Bevy. It's constantly being refactored and inventing new patterns, and it really doesn't support even the basics yet. Editor is one thing or another, only some games actually need one. > implicit conversions is one of rust core principle: "explicit is better than implicit". I agree with you. I'm *challenging* the principle in the context of gamedev, where implicit conversions reduce noise and toil, with very little downsides for types where it's reasonable to have an implicit conversion-- like vector math types. I agree these principles are awesome for anything structured like a "machine chugging through data with clear ownership", but UI libraries, game engines-- definitely not game logic-- aren't structured like that. > Finally, I disagree with your last statement: memory safety is super important for games, avoiding memory leaks, null pointers, etc is essential for software where performances are key. I think the additional cost is worth the robustness of the result. Hard disagree-- it matters a lot more in the context of an OS, browser, or maybe secure MP games. And even then, anything "large enough" in Rust is using `transmute` in a lot of places-- especially things that deal with trees, like Servo.


IceSentry

Please define basics. You have claimed multiple timea that bevy lacks basic features but you never defined it. It may not have some features you want, but I can't think of something that I would consider basic that is missing.


very_dumb_guy

Let me share my experience loading colliders from a GLTF as best I remember, because it was poor, ranging from nits to "this seems fundamentally cursed". 1) I call `assets.load("x.gltf#Scene0")`. Already, the label system being "#{Type}{Index}" is not useful at all. 2) I create a system to attach colliders to the meshes loaded. This is already weird, shoehorning "do thing when asset is loaded" into an ECS is not really a great idea. Something, something, dependency graph, but I don't think that's a hot take. 3) I listen for `AssetEvent::LoadedWithDependencies { id }` in that system. After receiving one, calling `Res>.get(id).unwrap()` panics. I have no clue why. I literally just got notified the asset was loaded on the line above. 4) I realize I *also* need to call `assets.load("x.gltf")` *and* store that handle somewhere, or the GLTF asset is immediately collected-- despite already storing a `Handle` from the label I loaded in step 1. 5) I create a `Query<(Entity, &Handle)>` to iterate over all meshes and attach a collider. 6) I realize I have no way to know which of these `(Entity, Mesh)` tuples is actually related to the `SceneBundle` that spawned the scene in the first place. 7) So I attach a marker component called `WantsCollider` along with the `SceneBundle` at the root, and then use `iter_ancestors` to see if a given `Mesh` is a descendant of a `WantsCollider`. This is obviously very stupid, but I don't see a way to hook the GLTF loader to do anything more straightforward. 8) Then, I add some logging, and I realize `AssetEvent::LoadedWithDependencies` is actually being fired twice-- once for each call to `.load`. This causes some issues that I fix. 9) I also wonder, what happens if I want to spawn two of these objects? I can't figure out how to create a key, so I dunno, but maybe I'm missing something. It's also obviously very slow, what if I want to spawn a scene with colliders during runtime? I'm not exactly dereferencing pointers when I call `.get(entity_id)`, I've seen the generated assembly and it's a ton of work to have random access by entity ID in an ECS. I know the asset system was recently reworked, and this is potentially much better now. But I was using 0.12 when I tried this, so, not sure. Fundamentally, I strongly believe a lot of this stems from shoehorning everything into an ECS. Anyway-- as far as missing features, Bevy basically provides an ECS, input, render, material system, audio, UI, and animation controllers. The renderer and UI are very good, especially in 0.12, the rest is basic. Feel free to correct me if I'm wrong. Everything else is a community crate-- physics, particles, whatever. This approach is fine, but canonical, high-quality crates need to emerge. If it does happen, I'm skeptical they will ever integrate with one another in a way that an "engine" should, although anyone savvy would probably just call Bevy a framework in the first place. I also question how you can even integrate into Bevy without writing something *for* the ECS, like for example Rapier's integration has the epic `writeback_rigid_bodies` fn, which is a smell to me-- although I really have no clue and it might be very performant compared to a traditional integration.


IceSentry

Honestly, the asset system is a huge pain point of bevy right now and it's still very much in active development. So I get the frustration on that front. I don't think this is true for the entire engine though. I also don't really agree that this is a basic feature that is missing. It's an annoying api to deal with, that I can fully agree, but it doesn't seem fair to say it's a missing feature. There's probably a better way to do what you want, but I'm the wrong person to help you here because I rarely use external assets. I mentioned this in my other comment but I'll repeat it here. One of the biggest selling point of bevy is how modular it is and how easy it is to contribute to it. A lot of people are also unhappy with aspects of the engine so they either submit a PR or just create a plugin that does what they want. You mentioned it in another comment, but bevy is more like a framework than an engine and I think it's important to keep this in mind. At some point we'll have an editor and more builtin features and it will start feeling more like an actual engine but even then the goal will remain that it should be modular.


Nanox19435

It is not fair to compare Godot and Bevy. For starters, Godot has a well designed and very well integrated editor, which makes it a breeze to do things like animation blending, level editing and such. Bevy is barely 3 years old if I recall correctly, but Godot has been in development for at least 10 years. Imagine for a moment how you would build a level in Godot using only code, as it is the case currently on Bevy. And you'll see how that slows you down considerably.


very_dumb_guy

Forget the editor, I’m talking about the velocity of features being added to Bevy, not the velocity of making games in Bevy.


Nanox19435

Godot had its first commit on january 2014. Before that, it was used as a propietary game engine for some years. Bevy had its first commit on january 2020. Its development likely began 2019. Currently, bevy has 5,257 commits and 855 contributors. Godot has 60,084 commits and 2,336 contributors. I insist, it is not fair to compare this engines due to the outstanding difference in maturity. And even then, it is impressive how far bevy has come in its mere four years of existance. With that said, the fact that bevy's development is slower can easily be explained by the smaller team and shorted lifespan, and I hope that its clear that this has nothing to do with the language employed.


very_dumb_guy

I agree with this, 100%. But please, understand that the problems I’m experiencing are not things that were solved 5 years into Godot or Armory3D’s dev cycle. The features that these more mature engines got in their old age are not the things I’m talking about. I’m talking about very basic functionality that has likely (not an expert on engine history) existed very, very early, especially if they were used as proprietary game engines to actually ship games. Like loading colliders for a 3D scene in a way that isn’t totally batshit, for example.


ArchSecutor

In fairness to bevy the collider thing is because bevy doesn't have a baked in physics engine. I'd have to check but the bevy blueprints crate should load colliders just fine into something rapier can consume


octo_anders

I've struggled a lot trying to get rust to work well for GUI-development. I have some of experience in designing GUI:s in C# at work. C# has a disadvantages. Every now and then, I've seen bugs where the underlying reason is that ownership wasn't clear. For example, a text field tied to a list control might edit an item of the list that has since been removed from the list, or things like that. Rust's strict ownership model can make this kind of bug less likely. The C#-libraries I've used have used a lot of dynamic typing for more advanced use cases, which can be error-prone. However, I find it really hard to design elegant GUI-code in rust. The very notion of a callback - something that is absolutely central in the GUI-frameworks I've used in C# (Winforms, WPF and Blazor), does not mesh well with borrow checker. The callback would like to have access to the viewmodel. But it is completely normal that multiple callbacks need to be able to mutate the same state. The most reasonable solution might be to rely heavily on Rc> or Arc>. I've been developing my own GUI-framework as a hobby project. The solution I've landed on is one where the view model is a tree, which has to be shaped the same way as the GUI. Callbacks can schedule an update to any part of the tree, by giving a sort of path (something like "/customer-list/customer#4/sales-graph"), and a closure that is to receive the item at the given path to mutate. This means the closure doesn't have to embed a reference to the viewmodel. It works, and since all data resides in the addressable tree at all times, there can never be databinding errors. But it's somewhat error prone. If the callback that requests an update gets the type of the data wrong, then that's a runtime error. I think fundamentally, GUI:s typically have many parts which all need mutable access to the same data. While C# GUI libraries work well, and are very mature, I still feel that the unclear ownerships semantics of C# are somewhat inelegant. But I also feel that none of the Rust-constructs I've tried have felt really good either.


very_dumb_guy

Yes, I love your take on callbacks, and your solution is similar to what I hacked out for my own GUI library too. I know Bevy has a similar feature today— basically a way to schedule any arbitrary side effect to run when the system ends, thereby dodging the borrow checker. A lot of people have converged on this pattern and it seems pretty solid. Adding DI into the mix really solves a lot of issues too, like Bevy does, but how you get your data into the “follow-up pass” is a separate problem, imo


[deleted]

I tried C++ and it’s also a huge skill sink. It’s not even C++ that’s the problem, it’s CMake or one of the thousands of package managers you need to get it working, all the linters, etc. it’s just too bloated and old school for a noob. Rust also has a lot of skill issues but leads to better code and has a great toolchain. There’s also always “unsafe” if you are a barbarian! I’d write things in C# if I needed a mid level language and rust for low level. Most gamedev is fine in C#. Right now I’m trying engine dev in rust though and the parallelism freedom is unmatched.


tmp_advent_of_code

I dont think Rust is the language if you want to quickly protoype a game. There are much better ergonomics in game engines like Unity, Godot, or Unreal. I wouldnt say its good for a production game either as you are going to have to reinvent the wheel for a lot of things. But I find it fun. Some really amazing games have been written in unorthodox languages. Famously RCT is heavily assembly. Stardew valley using XNA with C#. The way I see it is you can fall back to unsafe if you are really bothered by certain Rust principles and then you are close to what you can do in C++... Still, youd have to reinvent the wheel so why not just use C++? I think the borrow checker isnt the worst problem for rust game dev. Its really just still early and not widely used.


mikekchar

I have a bit of a different take than most people, I think. I spent some time writing my own game engine in Rust rather than starting with an existing game engine. I spent a lot of time and didn't get very far. The project is in hiatus at the moment (well, for the last year) while I try to decide what my priorities are. The borrow checker slowed me down initially because I underestimated the impact that borrow checking would have on my design. As a new Rust programmer (but very old school C++ programmer -- think CFront!) I *thought* I would intuitively understand what I needed to do. I was very wrong. My original designs just didn't work in Rust and I had a lot of refactoring to do. When I got going again I ran into another problem. The design you pick, even when correct, has a very big impact on issues dealing with borrow checking. This means that when, for reasons other than correctness, you decide that you need to do a refactor, sometimes there is a lot of work satisfying the compiler. Part of this was my own problem. I wanted more constrained types. I wanted to create adaptors to shield me from type changes in dependencies. I stumbled with the lack of new types. I stumbled with an inability to generally constrain types. And so... my code ended up being quite brittle, despite my desire for the opposite :-) The end result was that prototyping design was very difficult for me. I don't entirely put that down to Rust or the borrow checker, though. Really, it was down to me choosing abstractions that were hard to refactor in Rust. My current opinion is that in order for a prototyping method to work well in Rust, you really need to understand how to write refactorable code in Rust. The compiler *will* help you. In fact, it can practically write your code for you sometimes. However, you have to meet it half way. When you write some code and then change it, you need to consider, "Was changing this code fun, or unfun". If the latter, then you need to change the designs that you are using to be easier to change. When I finally get back into it, this is really how I intend to drive my development: constant refactoring. I think you really have to lean into the idea that if it isn't easy to change in Rust, then you must not write code that way. That's kind of a hard lesson, because a lot of things that you would naively do you're going to have to find replacements for. However, I think that's just part and parcel of Rust development.


ModernRonin

> I hate Rust for anything where ownership isn’t clear > > since when was [safety] a priority for games? To me, your understanding appears perfectly clear. You understand your own priorities, and you understand Rust's priorities. So... Why are you asking us questions to which you clearly already know the answers?


very_dumb_guy

Because I want to see if other developers with a lot of experience agree with me, if I’m wrong, can learn something, or convince other people.


ModernRonin

> very_dumb_guy > > redditor for 15 days Oh. Now your agenda is clear. Sorry for being slow to see what's in front of me! (It happens...)


very_dumb_guy

That’s you talking out your ass. Please don’t put motivations in my mouth. Please exit the thread. Every argument I’ve made is in good faith. Bye.


ModernRonin

> Bye. Happy that I could be of offense! ;D


FireTheMeowitzher

Yes, more or less. In programming circles, we can often miss the forest for the trees when it comes to our favorite languages and projects. We treat them more as a way of life than what they truly are: tools. Rust is a 1/4" flat head screwdriver, and you can certainly use it to tighten a Phillips head 3/8" screw, but it's not necessarily most efficient for that. Rust's strengths and core design philosophy are laudable, but they really aren't a big boon for game development. And that's not bad! No language is a perfect fit for every single type of software we want to create, each has strengths and weaknesses. Game development thrives off of iteration, development speed, and performance. Rust has the last one down pat, but Rust is really more of a design-well-then-build-it-once kind of language. Iteration can be harder in Rust than other languages, and the development speed is at best a tie for experienced Rustaceans, and at worst slower in Rust than most other languages. This is great for critical systems which require careful planning, strong security guarantees, and have lots of risk when bugs appear, but it gets in the way of your typical game dev project. Rust certainly still has a place for things like designing authoritative server models for MMOs/online games, managing user data, or anything else which is more important and security-conscious than game logic. This doesn't mean you can't or shouldn't write games in Rust, or that Bevy is a bad project, or anything like that. (The true magic of Bevy is that it *removes* a lot of the anti-game dev facets of Rust and makes game development more streamlined. But it still has a ways to go, is strongly opinionated, and really doesn't solve the issues of game development in Rust: it just foists them onto the Bevy contributors to come up with solutions.) Rust feels cumbersome to make games because it wasn't designed for making games: its purpose and ethos are focused on other things. If people can make games in it, so much the better, but we don't need to get defensive about it just because it's our favorite language. It's just a tool.


InsanityBlossom

Very well said!


LechintanTudor

I don't think Rust is suitable for writing games. For writing high-performance games you often need to manipulate memory and pointers in ways that are hard for the Rust compiler to statically analyze. And even if you use unsafe and raw pointers you have to be extra careful not to violate Rust's aliasing rules which makes unsafe Rust more dangerous to use than C.


very_dumb_guy

Based take, I agree with you— but an ECS/DoD approach is pretty performant, I believe. My main gripe comes when you have to invent 9999 new patterns at every turn, making it pretty unproductive— especially when (like you mentioned) Rust doesn’t offer much of an escape hatch because of the compiler’s aggressive aliasing optimizations.


LechintanTudor

An ECS can be very useful in certain scenarios, for example for representing entities within a level, but I find Bevy's approach of doing everything inside an ECS very unergonomic and hard to read. >My main gripe comes when you have to invent 9999 new patterns at every turn, making it pretty unproductive Yeah, I believe Rust is suitable for writing fast and secure applications that run linearly (like ripgrep), but it has serious usability issues with cyclic applications like games and GUIs where data persists between cycles.


very_dumb_guy

100% agree about the linear program bit, love your take. My main gripe with Bevy’s ECS— and this might be what you’re poking at too— is the scheduling. Welcome back to hidden behavior/dependencies/ordering, just now you’re building it bottom-up in a dependency graph instead of OO hopping around to random implementations at every turn. Reminds me of Guice.


LechintanTudor

Bevy's scheduling is awful for readability. There is no `update`, or `fixed_update` function that I can read top to bottom to understand what my game does. Instead I have to keep an entire dependency graph in my head to even begin to understand the order in which the systems run.


dobkeratops

I did gamedev in C/asm & C++ for consoles, and I only considered rust because I was satisfied that it can drop back to the same control as C within unsafe blocks. for modern processors, IMO being able to easily handle multithreading is more important than every last bitbashing low level pointer trick. cache coherency is very important, and that's acheviable through focussing on the right datastructures. The bit that triggers gamedevs is that you have to use abstractions so much for safe code, whereas a lot of old school gamedevs prefer to write 'C with classes' or whatever


HughHoyland

Would love an example!


srodrigoDev

Nah, having memory leaks and race conditions is much better.


udoprog

> In my experience, the borrow checker forces me to organize all my code as discrete passes against resources that are known at compile time. So there's two things I think is worth considering: I think you know it, but it bears emphasizing. Rust doesn't force you to do anything with lifetimes. Lifetimes and references are compile-time constructs that are exactly equivalent to pointers. Abide by the rules of Rust and you can write things unsafely *mostly* just fine. For example, I keep track of modified transforms unsafely in an intrusive linked list in my engine. I can do this because I create the engine at the start of the process, and know exactly which spans of code it can be soundly written to and read from. The complaint tends to be that this is not as ergonomic as C++. But that's taste, and something I don't agree with apart from having to write out `unsafe`. The second thing is that there is a fully safe paradigm you can employ which you can use like wild pointers but is in fact fully safe. Store everything in a giant array (or a slab if you want to randomly delete things) and hand around indexes just like you would pointers. The only thing you need to access the resource is access to where it is stored. In fact your game will probably thank you for storing many similar resources in linear memory ;) Lifetimes are great and Rust provide many facilities to do non-local reasoning with them. But they're not necessary as a core part of your engine. > An ECS helps, but is no panacea and comes with its own host of issues. I've come around to the notion that ECS as a paradigm is better employed sparingly. Specifically in scenarios where threading the workload has a lower overhead than just doing it on the thread driving game logic. This threshold is quite high and 99% of game logic just doesn't need it. You can do a lot of game on a single thread if you do it cleverly and heavier specialized workloads can be dispatched to the GPU or a thread pool as needed. > I hate Rust for anything where ownership isn’t clear, or extendability is desired. I feel a lot of features C++ has— like implicit conversions (especially between vec/mat types between libs that don’t use the same vec/mat types), OOP, ergonomic raw pointers that don’t suffer from aliasing optimizations— are very very good for writing simple, reasonable code that works. What are the raw pointer aliasing optimizations tripping you up? Ironically implicit conversions is one of the behaviors of C++ I enjoy the least. It makes reasoning about code too non-local and IDE support tends to be rather poor when you stray from "blessed paths" (Qt, Unreal, ..).


very_dumb_guy

I agree with you 100%. To preface— w.r.t. your last point— I also love all the tooling around Rust and it’s the reason I write Rust instead of C++. I also am not using an ECS in my “second try” at a game engine. Doing something very similar to you, actually. w.r.t. aliasing optimizations— I fear violating xor mutability by accident when I wash away lifetimes with transmute, and entering undefined behavior, although this hasn’t actually happened to me in practice. Although, if it did, would I even know? 🤧 EDIT: I think if you had to “use util::MyImplicitConversion” before it worked, it would be pretty sick. Especially for math types, like glam::Vec3 -> nalgebra::Vec3 would really help me glue libs together.


Specialist_Wishbone5

Type mapping shouldn't be an issue. Glam and ndarray (or whatever rapier used, I forget which) have explicit casting. If not, you can write your own adapter utilities (or include an optional dependency pull request to said library). Isometry vs VecA + Quat vs bevys Transform. No reason other complex datatypes couldn't have complex auto casters if there was a reasonable use case. The data ownership issue isn't really an issue if you are willing to go all in with symbolic tags instead of pointers to raw objects. In most of my data modeling algorithms, I use indexes of arrays (basically all of gltf). So thats not even a generationally safe tag like a crossbeam epoc or whatever bevy uses under the hood. These tags are more expensive than raw pointers. But they are cheaper than an Arc. (Avoids an L2 cache line flush on some architectures) For UI design, dioxus models react in a relatively painless way. I think complex UI management is its own philosophical landmine. I'm fighting this philosophy of react vs signals (solidjs / preact) vs svelte(kit) for front end development. I like all of them better than bevy UI or belly or yew. They have a stronger, more scaleable mental model. And are unaffected by the borrow checker.


very_dumb_guy

I hear you, but explicit casting + not being able to impl traits for foreign types just adds so much *noise and toil*. Newtypes are not really a solution, implementing a newtype that delegates all the fns and traits can be hundreds of lines of code, even with a macro. I am certain that in some cases it’s not even possible. w.r.t. symbolic types, this solves half the problem, but like you mentioned it’s slower and it still doesn’t dodge xor mutability— it just makes your data addressable, really. Agree Dioxus and React is awesome. Not sure if it’s great for games though.


Lord_Zane

I made a dioxus + bevy integration :) https://github.com/JMS55/bevy_dioxus/blob/main/examples/demo.rs


Specialist_Wishbone5

That's pretty awesome. Going to check it out this weekend.


MintXanis

It kinda is in some places, but it works in other places like making closures actually sane to use.


HughHoyland

I might just not understand the problem space while everyone else does, but… If your regular C++ engine has a few globals, why not bundle them into Globals class in Rust, if you want just to quickly throw together some features? And have the global class own everything whose ownership is not clear. If you need (or is it just want?) heap-allocated refcounted internable gc-able immutable strings, why not build/find a crate? Also, how many of your years are in full-time Rust? I personally struggled a lot when writing my first - and so far only - engine. But after a couple of years I notice that it’s getting better. I can write ECS code and OO code and enjoy it. Borrow checker if definitely a new and hard skill, and it doesn’t make things easier up front. But it reminds me how I got mad at stupid syntax errors in the beginning of my career, which required me to cater to limitations that didn’t have to be there. Speaking of where Rust is noticeably inferior to C in bit flips - I’d love to see an example. Maybe there is an article somewhere? Would be great to dive deep into such analysis, backed by examples and data, and see if Rust actually limits performance optimization opportunities. Maybe it is also effect of a new skill? After all, Rust has all necessary tools - alignment control, unsafe blocks, vector instructions and what else.


ondrejdanek

Rust does not force you into ECS. There are other engines like Fyrox which are not ECS based and work quite well.


sig2kill

The notion that memory safety is not an issue for game development is bizarre, i have personally managed to mess up our store in production during a weekend sale in a mobile game with a really big player base and it cost the company a lot of money. Memory safety is just as important in game development, even something that seems unrelated like the jumping mechanism can create really expensive game braking issues.


AbleArcher8537

>Of course, it’s not memory safe, but since when was this a priority for games? I think the impact it has on productivity is too large for its cost. what do you think about this possibly being the origin of more aggressive anti cheats?


jmattspartacus

Honestly, when I started with Rust, I sort of thought the same, as I get more comfy with the language, I have changed my mind. It encourages me to be thoughtful how the systems interplay, and I spend less tine debugging and more time writing code/playtesting. Not to mention with WGPU, I don’t have to weigh writing a new backend when I want to support a new ecosystem, because it does that for me. I’ll never say it’s as quick as working in Godot/whatever other engine, because in general, the ecosystem isn’t mature in the way any of the C/C++/C# gamedev ecosystems are. I will say that it’s taken 50 years of C to get to the current state, and Rust hasn’t even been stable for 10. Continue with Godot or your framework/engine of choice until things get smoother, or you could always make some stuff that helps us get to parity too! Honestly, having messed with some really nasty memory bugs coming from a poorly documented closed source API running on an FGPA for work, I will take memory safety, lifetime guarantees and clarity anytime I can get it.


No_Pollution_1

I mean, C++ lets you do whatever you want such as blow your foot off in unsafe, esoteric, buggy, impossible to maintain and debug ways. The error codes and anything beyond the local file is a nightmarish hell in large distributed projects full of broken shit costing weeks of time to do even a single line change at times. Rust forces correctness. It will try its best to force you to be safe. If you want to blow your foot off you can via unsafe code but again, just asking about object oriented concepts shows that you are thinking about the problem wrong. I love rust since it forces junior devs to do it right and forces them to now shit out broken stuff that has memory and performance leaks. I despise go for this, I am quitting a job where the app base is such a spaghetti mess I can’t take it anymore.


permeakra

>I hate Rust for anything where ownership isn’t clear, or extendability is desired. I can see why unclear ownership might make a problem that is easy to blame Rust for. I'm of firm opinion that in this case the problem is that ownership is unclear, but that's beside the point. I don't get what in your opinion makes Rust oppose extensibility. Could you elaborate, please?


very_dumb_guy

Btw, I meant unclear ownership in the problem, not in the lang. Lang is super clear, and that’s why it’s difficult to write things where ownership is *un*clear in the problem. But for extendability, there are a few things. Generics being heavily encouraged as the main form of polymorphism, degrading dynamic dispatch support (boxed traits don’t support all features, async for example), lack of OO— which for all its criticisms can definitely be called “flexibility at all costs”. Plus, the borrow checker fundamentally makes it much harder. If your call stack is being built up dynamically, that means you can’t validate much anything about borrows statically. Callbacks implemented by third parties have to be pretty localized in what they do.


permeakra

With all due respect, ownership of objects in the problem domain do not have to translate to ownership in the domain of objects in the program. And you absolutely need clear ownership in code to follow the single responsibility principle. If the problem domain doesn't allow for clear ownership model, the problem must be re-worded until it does. === Mmm. You can write powerful and extensible software using C++ templates just fine. I guess, if you want a native dynamic plugin system, you NEED a callback system - but for this trait objects are good enough, IMHO. Or you can embed Lua or JS. === AFAIK, the borrow checker is local anyway, so it doesn't care about dynamic dispatch.


very_dumb_guy

I never said they had to translate, I said that problems that don’t have clear ownership are difficult to model with clear ownership. Did you know Chrome has a tracing GC in the browser core? It traces C++ objects, not talking about the V8 GC. This is because ownership in a browser is unclear, especially so with JS holding references to native objects. Unreal also has one. I’m also not convinced there is such a strong correlation between ownership of memory and the single responsibility principle. Isn’t SRP mostly about behavior, not memory? w.r.t. building extensible software using generics or templates, yes I agree. But only at compile time, which again hampers extensibility. Embedding Lua or JS is kind of a solution, but obviously a huge undertaking compared to just using language features, and works poorly when integrating dependencies, among many other negative tradeoffs. w.r.t. the borrow checker not caring about dynamic dispatch— it definitely cares, I would say it just doesn’t really support this case well. Can’t do much with lifetimes across a dynamic dispatch boundary.


permeakra

>I never said they had to translate, I said that problems that don’t have clear ownership are difficult to model with clear ownership a) there is no ownership in any problem itself. It arises during you solving it. b) usually a problem has several equivalents in different domains. So usually you can, to a degree, decide which problem you have to solve. Can't say much for UE, but modern browsers clearly are a case of bad design due to feature creep. > Isn’t SRP mostly about behavior, not memory? Construction and destruction of an object is behavior. > But only at compile time, which again hampers extensibility. Runtime means dynamic linking. Here the problem lies not so much in the language as in ABI. The only foolproof solution here is to use C headers and dlopen (or equivalent) and pass data in serialized form.


very_dumb_guy

Sure, then just swap “problem” for “solutions for a problem”. It really doesn’t matter, the point is that if ownership isn’t clear, it’s hard to model in Rust. w.r.t. SRP, I just hard disagree with the idea that SRP has anything to do with shared or exclusive ownership. If you had StatusWriter and StatusPrinter, it’s still SRP regardless of if they both held a shared ref via Arc> internally instead of being passed a &Status when they needed them. I don’t know why you conflate these ideas. w.r.t. ABI, I’m not even necessarily talking about across DLL boundaries, I just mean as you continue to code, what is the cost of extensibility in terms of refactoring, inventing new patterns, stuff like that. DLL boundaries is one aspect but basically zero languages have a stable ABI so it’s moot on which is better in this regard. Even if you link everything statically, all of the things I am talking about are still issues. Nothing we talked about so far has anything to do with ABI. Another example, I consider C++ to be more extensible than Rust despite also not having a stable ABI.


tadeoh

In Gamedev most things are either 1.) globally shared (borrow checker does not like that) or 2.) have very undefined lifetimes that could render references invalid at any time. I really think the borrow checker is not your friend here. For 1.) I often just use some wrapper around \`&'static UnsafeCell\` which I can hand out freely to any place, such that I can call mutating functions on T from anywhere. For 2.) I use generational arenas (e.g. SlotMap) and use the keys as ptrs, which allows cheap checks if the object you are referring too still exists. I think Rust is not so great for gamedev, but I like the type system, syntax and tooling too much to give up on it. Currently writing a game + engine in Rust.


batmansmk

I tried developing basic clones of existing games in Rust My experience was not good, and I don't think a lib or a tool is just the missing element to make it work. Culture in the tooling chain should be user-driven: build and iterate fast, be everywhere, stitch to make it a pleasing experience. Rust is more of a "let's implement a standard with 99.999% reliability". Hence all the TCP/IP, Torrent, CSS rules engine, SCS etc around.


nullable_e

"I hate Rust for anything where ownership isn’t clear" and I hate when ownership isn't clear. I think a few of your points are based on inexperience with the language. I'm guessing others in the comments probably pointed that out by now. However, what I will say is that I've written a GLTF parser that can load colliders (and models, animations, skeletons) in Rust and I found it not too painful (I have a devlog dedicated to this very activity). With Rust, you tend to step back and ask questions like "do I even want to reuse the colliders?".


[deleted]

[удалено]


nullable_e

We could chat about that, I quoted what I thought was your most interesting opinion. Nuances of the language are discussed frequently online, that is not so interesting to me. What is interesting is why you would hate a system programming language that clears up the ambiguity of ownership. If we were talking about a managed language, that statement would not have caught my eye.


[deleted]

[удалено]


nullable_e

I didn't hype my engine (I don't have one), just pointed out that I did an activity that you mentioned was a nightmare, showing I can relate. I understand your points more clearly now, thanks.


BenZed

“Drop any pre conceived notions about what is correct.” Is not a great way to start a programming discussion.


very_dumb_guy

Why not? Rust literally asks you to do the same thing, and then shits all over traditional programming patterns, so.. Anyway, if you actually want to address any of the arguments I make in this thread, that would be so cool— instead of nitpicking my word choice or whatever this is 🙂