Home > OS >  Performant way to abstract engine-specific class/struct implementations for C# library?
Performant way to abstract engine-specific class/struct implementations for C# library?

Time:12-21

I'm working on a game reimplementation in Unity, but I want the bulk of the code that deals with the game's resources to be engine agnostic so that tools can be easily made to mod the game by just dropping them into a new C# project, excluding all Unity specific code.

To achieve this, I have a few singletons that handle implementations for certain things. For example, when loading a PNG texture the engine agnostic code calls a texture factory singleton that has the Unity-specific PNG loading code.

The problem now is, I'm about to start working on loading models which normally would involve Unity's Vector3, Mesh, etc. classes, but since I want to be engine agnostic I have to use some kind of abstraction for these or some kind of marshaling.

The obvious way to do it with Vector3, for example, would be to create a new Vector3 class that resembles Unity's Vector3, and simply translate them to Unity's Vector3 by creating the Unity version with the same XYZ values, one by one. The issue is that I'm going to have large arrays of these, so this sounds really inefficient.

I've already tried this with Color32/Color for texture generation code and it was way too slow, so I'm stuck coming up with a solution.

I've thought of just having a factory singleton that creates UnityEngine Vector3 classes and make the engine agnostic code simply expect "object" types rather than any specific type, but I feel this would be way too messy to deal with. Might really be the best solution for performance, though.

Would appreciate any advice!

CodePudding user response:

You won't like the answer: don't do it.

Just write it in Unity-specific code. Don't expect copy-paste and don't create 20 layers to make some sort of magical abstract transformation mechanism. Regardless of the language, this is a serious design vice. First, let's consider you switch from one C# to "Unity C#" (there is some discussion here, but I won't get into it as it's not relevant now). Unity has different code-design and architecture paradigms and concepts. They are slightly different, from say, a web app, server middleware code or even a desktop C# program. Even if you could make wrappers, the work behind it would be hard, not just to "translate", but also to match one paradigm to another. It's like writing C for DOS and then making a layer in Windows 11. Sure, it's the same language, but the concepts are different (I am not bringing up the OS API here).

Now, let's assume you have a C game. If you'll switch to C# (for example using intermediate libraries), then, let's say you want to switch to C again in 10 years. The C standard now is so different than C 99 that those that took a break these 25 years might consider it a new language or extension. And what would happen if you did want to switch to UnrealEngine? Would you wrap over the C# that wraps over the old code?

In the end, it sounds like this is what you'll end up anyway: video classes over the old code and video classes over unity's code, then a translation layer, same for grapics, audio, maths... in the end it will almost be like making a new engine.

Apart from coding errors that might appear, imagine doing maintenance on it. And not by you, but by a team. A new team (if people leave and you get new guys in). There are so many factors in it, that the whole effort isn't justified.

Migration is the best solution, just write code as close-to-native (from the engine's point of view - by this I don't mean asm or whatnot, I mean, as close as what the engine expects it to be like, instead of some magical wrappers or abstraction layer). You'll skip the performance hit of abstraction (because trust me, adding one, two, 500 hundred layers of some magical code will add memory overhead and possibly even CPU overhead) and you might even find some code that can be simplified. Unity has loads of native 5-lines-of-code solutions that can reduce code. Even assets (paid or free) that feature extended utilities, plugins or common code helpers.

P.S. you might hear some crazy workarounds like writing it in C or C , then adding a separate binding library and that library could interface with any language and then you could do this and that... also don't. If this is the situation you're in, imagine you have your own allocator/deallocator, your own memory manager, possibly a thread manager if you have some mutable/immutable/atomic code, maybe some mutex/semaphores for multithreading, all those will clash with both the C# manager AND the Unity manager. While it's true that in Unity games most of the C# "magic" is handled by the framework, in reality, all Unity classes are handled by the engine, which is C and has its own rules. And while you MIGHT find workarounds for this or it MIGHT not be an issue, as the project grows and expands, you might have some surprises. Too many dependencies will add issues.

  • Related