3 ms·
It's C++, which is rarely good. The language is large and complicated and you generally need to be extra careful when using it. There's a lot of features that i
by tom_ 2y ago
It's C++, which is rarely good. The language is large and complicated and you generally need to be extra careful when using it. There's a lot of features that interact with one another in surprising ways. UE5 does its best not to make things worse, but it's not always successful. (Are you certain you've tagged everything as UPROPERTY/UCLASS/USTRUCT that needs to be? Are you definitely always capturing your UObject pointers correctly? Are you definitely never using UObjects outside the game thread? Is your TSharedPtr structure acyclic? Are you certain they're all getting cleaned up? Have you tagged each one with the correct multithreading mode? Are you keeping an eye on FName creation? Etc., etc. - perhaps some of these are fixed now, this is just some stuff I've had to fix in the past that stuck in my mind.)
C++ build times are often terrible and UE5 does not buck this trend. Editor startup and on-the-fly cooking can also adds to the iteration time. (If you're part of a larger team you'll also spend a lot of time waiting for Perforce and watching it compile shaders and recook. OK, so you can't lay the blame for this bit at C++'s feet, but if you're anything like me then this will give you additional time to spend thinking about how much the iteration time sucks.)
The debugging experience is also subpar, because you pretty much always have to run an optimised build, which makes debugging tedious. Missing variables, wonky single stepping, inlined functions that are impossible to breakpoint, all the usual problems. It's no fun having to work with C++ code using the disassembly.
Having an enormous pile of engine code to carry around also does not help when trying to get the game into a shippable state. Eliminating all the minor hitches and surprising crashes is a huge pain, simply because there's so many places they could be coming from, and even when it's obvious what the problem actually is, actually fixing it is another matter. The build times (which towards the end of the project are as bad as will ever be) really don't help; you'll be working largely with code you've never seen before, and the issue could be anywhere, so header changes aren't uncommon and spending time deep in thought while waiting for builds is not as helpful as it might be if working with your own stuff.
- jncfhnb 2y agoI would be curious to know if you think what you’re saying is niche or not. Not arguing or doubting but it seems foreign against my experience. > Are you certain you've tagged everything as UPROPERTY/UCLASS/USTRUCT that needs to be? Pretty much everything seems to need this? > Are you definitely always capturing your UObject pointers correctly? Idk what this means > Are you definitely never using UObjects outside the game thread? Just… don’t? The engine is not safe for multi threaded operations on game state. Why would this come up? > Is your TSharedPtr structure acyclic? Idk what this means > Are you certain they're all getting cleaned up? Like garbage collected? I haven’t run into anything on that…? > Have you tagged each one with the correct multithreading mode? Why would you be doing significant multi threading?
- tom_ 2y agoUPROPERTY tagging - indeed, pretty much everything does need this, but nothing actually seems to check whether you're getting it right. You can easily add a UObject * , fail to tag it as a UPROPERTY, and now stuff will go wrong. This is what I mean about having to be extra careful. If you get it wrong, you'll never know, until things get GC'd prematurely and it won't be obvious why. Capturing UObject pointers - in a lambda capture list, I mean. These don't participate in the GC mechanism. You have to store a copy of the pointer in a TWeakObjectPtr or TStrongObjectPtr and capture that instead. Again, nothing actually enforces this, so you just have to be very careful to do it. UObjects outside the game thread - had this on a recent project where there was a background thread retrieving stuff using a REST API. The data was related to some thing or other that was configured in the editor, so one reasonable-sounding thing to do is hand the relevant UObject * to the thread and have it handle everything. But this isn't valid, because the GC relies on UObject pointers only being manipulated by the game thread. But - and the reader may sense a theme developing here - nothing actually checks whether you're doing this. TSharedPtr - TSharedPtr is reference counted, so you can't have cycles in your pointer graph. This isn't UE specific (std::shared_ptr suffers from exactly the same problem), it's just another thing that can go wrong that nothing checks for. And it's hard to spot, because there's no built-in debugging features for it. And regarding tagging TSharedPtrs with the correct multithreading mode, UE5 is multithreaded out of the box as it has separate rendering and game threads, and TSharedPtr is an obvious way to share data between them. But if you do that, you need to specify you want the multithreading-friendly TSharedPtr! (This is a particular pitfall for programmers familiar with std::shared_ptr, of which TSharedPtr is supposedly the equivalent, because std::shared_ptr is multithreading-safe by default.) Anyway, we've had this problem on 2 separate projects, and it wasn't easy to track down in either case. And I'm sure I don't need to specify whether anything checks for this situation. Anyway, niche or not, I've no idea. I'm no UE expert, just somebody that's done a lot of this stupid C++ crap and helped out on several UE4 and UE5 projects that needed some bugs fixing. And it was just a bit noticeable to me how much the same old C++ stuff (that I've been dealing with for years) was still much in evidence.