For some of us, good just isn't good enough. The best programmers reuse existing functionality that gets externally maintained. But sometimes, that functionality only exists internally. Even though you should be aware that internal code is often experimental and subject to change, it may be safer to reuse if it is complex code that is better maintained by the authors themselves.
There are many reasons why you would want to access Unity's internals. I needed it to replicate Unity's dragging on my TimeSpanField. In this post I will show you exactly how to access Unity's internals safely and cleanly with complete control over where and how you expose it.
Unity's Internal Dragger Behavior
Creating TimeSpanField I tried to replicate Unity's implementation as close as possible: this gives me results that simulate Unity's own behavior. Researching LongField I came across this code:
Replicating Unity's implementation is good because it is likely to remain supported for a long time, however in this case it is locked behind an internal access modifier. Even though we could copy and maintain the code ourselves, that just gives us the problem we are trying to avoid, because at that point it doesn't replicate Unity's behavior anymore.
Unity's Access Handle
If you create an Assembly Definition with the name Unity.InternalAPIEngineBridge.001 up to .024 that assembly is able to access all of Unity's internal code: problem solved!
It is however very important that you never use this approach. If 2 Assembly Definition share the same name, then Unity will not compile. And since Unity uses these numbers 001 to 024 for their in house packages but we don't know which ones, we have to assume all are taken.
Referencing 2D Common Package
This is where we have a trick up our sleeve. Create an Assembly Definition Reference: these can hook into an existing Assembly Definition, and add to it as if it were its own. If you install Unity's 2D Common package (through the package manager), they have a perfectly good assembly sitting under Packages > 2D Common > Runtime / Editor > InternalBridge > InternalBridgeDef for us to use!
With this technique, you can access Unity's internals anywhere you want to! But should you?
Safely Accessing Internals
Much like sex, accessing internals is best done behind closed doors. Put your InternalBridgeDef inside a folder called InternalBridge: you can think of this folder as behind closed doors.
Add a static class InternalEngineBridge:
Make the InternalEngineBridge internal so that it can't just be called willy-nilly (the irony is not lost on me). But seriously: as Chesterton's fence dictates, only access internals if you understand what you're doing. For the safety of our code base, it is only responsible to take every precaution and limit its influence.
Add a script AssemblyInfo:
It's a very short script but it makes our InternalEngineBridge visible only to the assemblies we want. You can keep adding assemblies in this script as you need.
The last thing to do is find our assembly and reference the 2D Common's InternalBridgeDef.
Using Internals
Now that we have our Assembly Definition set up, every script in that assembly can reference our InternalEngineBridge. So we put our TimeSpanField in there...
And presto!
You can keep adding Assembly Definition Reference as you need, exposing only the things you need to. Our only dependency is on the 2D Commons package installed - granted it's a bit of an odd requirement, but not a hard one to swallow to get safe access to internal Unity code!
Did you like this post? Tell us
Leave a comment
Log in with your itch.io account to leave a comment.