Do you have recommendations for getting Unity advanced coding information? Like assets or open source projects to study the code, youtube channels, articles, books, courses and etc.
I am a professional software developer and work with databases, frontend and backend for 6 years and recently decided to focus on game development. But after trying to go deep on Unity i found really difficult to find advanced content on game architecture and how to properly manage things for scallable games.
For me, beyond knowing the basics of the Unity api, it’s about knowing what your options are in C# and what common design patterns are. Then whenever you are selecting from those many approaches to a problem, you are at least familiar with the approaches and can make an informed decision on which way to go (first.)
(delegates vs interfaces is interesting, I do miss generics and linq here as options)
The unity specific code only goes so far, beyond that, it’s just C# and design patterns, nothing else. In fact, I’d recommend splitting your unity specific code (view) from the general code (model) for larger projects. It’s common practice.
The first thing you will notice is how much slower the unity CLR (mono) is from what you are used to at work (.NET 6 and 7)
Edit: and if you decompile the msil code you will see that mono is type casting floats to doubles before operating on it and than back again for no appernt reason
I’ve got a 3 or 4 page intro to Unity for programmers at taxesforcatses-dot-com/codeNotes/UforP.shtml. One thing from there – everything in Unity wants to run all at once through their own Update()'s. That’s fine, but if you like a traditional top-down structure where one top-level chunk runs (or skips) everything, that works just fine, too.
About jvo3dc’s mentions of “design patterns”, that can be confusing. Some people say that to mean just any cool programming trick. But the official things called “Design Patterns” aren’t useful. Sure, you’ll find lots of old internet stuff about how great they are, but we finally have lots of newer internet stuff about what time-wasters they turned out to be.
I will again point out, and this is the 3rd time this week alone, that the historical reason was that C# didn’t have a dedicated 32-bit floating point math library until .net Core 2.0 which was a) relatively recent, and b) unavailable in Unity. Unity made their own Mathf library (very early on) to support 32-bit math, however they’ve abandoned the idea of actually implementing their own low-level computations, because that’s just ludicrous for many reasons, and this kind of worked for 97% of games out there.
(Edit: Also I think it gets optimized by IL2CPP anyway.)
Because of this UnityEngine.Mathf is mostly just a 32-bit wrapper for the System.Math class which is 64-bit, this is why floats get cast to doubles and back, and this is especially painful for square roots and trigonometric functions. Some other methods such as System.Math.Abs do support 32-bit floats natively. And the rest of it are Unity’s own useful compounds or math functions, so not everything is bad about it.
Nowadays, we have an official 32-bit floating math library since .net 5 because .net core got reintegrated: MathF. According to my simple benchmarks they offer a significantly better performance and everyone and their grandma is encouraged to switch from UnityEngine.Mathf to System.MathF.
That’s true and untrue at the same time. There are “Design Patterns” and there are design patterns. Some of the patterns are so ubiquitous and evergreen, that they work in every environment and in any context, regardless of whether Unity is object- or composition- or data-oriented (as with DOTS), the language itself is vast and open to various approaches. Obviously all of the patterns are situational, but there is nothing black & white about them. Observer pattern, strategy pattern, factory pattern, singleton pattern, composition pattern, and many many more are perfectly valid or even beneficial for Unity projects.
That said, I never actually stress myself with remembering them, let alone applying them to the letter, but there is value in appreciating the reasons why they exist and what they solve.
Try to get a good grasp on what Unity is – it’s a holistic engine, and your code is a mere guest to the party (quote by @Kurt-Dekker ). Pay special attention to how you can build things that are self-contained, but then you want to strategically connect them to the underlying mechanism, to make a cohesive hole. Where juniors would over-connect and lose all control, you can funnel your logic with complex solutions and intermittent machines that you write on top of it. You can also make everything as simple as possible, but the longevity of that approach depends on the scale of your particular project.
Learn about serialization in Unity, that’s a huge topic.
After you learn what makes Unity tick, learn C# as much as possible. This is easier to do than learning Unity. After almost 15 years I can’t manage to fully go through everything that exist, because it constantly evolves, yet I’m pretty much convinced I’ve seen nearly everything C# has to offer (at least on a fundamental level), because its design is much more deliberate and stable. And well-documented.
Spend some time at researching features you’d like to implement. Especially if you don’t have a graphics programming background. Learn the jargon, learn about the GPU and the techniques involved. Learn where the bottlenecks lie. What Unity does on its own, and what is beyond but must be maintained by Unity.
Spend some time in learning how to automate editors and make your own dev tools, this is a large part of being in control over your dev time in Unity. Nearly everything is programmable and extensible, but it’s shifty, sometimes not so well documented, and very different from actually working on a code that will be deployed.
These are the most important things I can think of.
[MethodImpl((MethodImplOptions) 256)]
public static Vector3 operator *(Vector3 a, float d)
{
return new Vector3(a.x * d, a.y * d, a.z * d);
}
The resulting IL is:
.method public hidebysig static specialname valuetype UnityEngine.Vector3
op_Multiply(
valuetype UnityEngine.Vector3 a,
float32 d
) cil managed
{
.maxstack 4
.locals init (
[0] valuetype UnityEngine.Vector3 V_0
)
IL_0000: nop
// [543 7 - 543 53]
IL_0001: ldarg.0 // a
IL_0002: ldfld float32 UnityEngine.Vector3::x
IL_0007: ldarg.1 // d
IL_0008: mul
IL_0009: ldarg.0 // a
IL_000a: ldfld float32 UnityEngine.Vector3::y
IL_000f: ldarg.1 // d
IL_0010: mul
IL_0011: ldarg.0 // a
IL_0012: ldfld float32 UnityEngine.Vector3::z
IL_0017: ldarg.1 // d
IL_0018: mul
IL_0019: newobj instance void UnityEngine.Vector3::.ctor(float32, float32, float32)
IL_001e: stloc.0 // V_0
IL_001f: br.s IL_0021
IL_0021: ldloc.0 // V_0
IL_0022: ret
} // end of method Vector3::op_Multiply
I’m not seeing anything overtly out of place here. You could make an argument about the Vector3 constructor adding an extra stack frame that could be avoided. But I’m not seeing anything about doubles here. Everything is a float32.
Question… how are you getting a peak at the JIT’d code?
The CLR will compile the code in memory… you’d have to either A) read the memory in place, or B) use the debugger api to observe the state of the application at the time. Not exactly a scenario that I have setup ready to just do on a whim. And I have the sneaking suspicion this is not what you’re referring to… and that just like you mistyped about the MSIL, you’re mistyping again here.
Do you mean the compiled code generated by IL2CPP?