I’m working on an application that will be shipped to both Android (Meta Quest) and WebGL platforms. While much of the functionality is shared, some is platform specific. For example, in the WebGL build, I’m looking to the system keyboard for text input, whereas in the Android build, a virtual, world-space keyboard is used. As I see it, I can use assembly directives to gate platform-specific functionality, or I could use if statements to gate behavior, as I’m already tracking platform with a singleton instance.
Example - I could use if statements…
if (PlatformManager.INSTANCE.platform == PlatformManager.Platform.Desktop)
{
// Get system keyboards input stream.
}
else
{
if (virtualKeyboard != null)
{
virtualKeyboard.gameObject.SetActive(true);
}
}
or I could use assembly directives…
#if !UNITY_WEBGL
if (virtualKeyboard!= null)
{
virtualKeyboard.gameObject.SetActive(true);
}
#else
// Get system keyboards input stream.
#endif
Should I only use assembly directives to gate content that’s otherwise un-buildable for the given platform? Anyone have best practices in this domain? Optimization or readability concerns?
I think you already know your answer. But, the second is, to me, the better choice. While including code for other platforms isn’t always an issue, sometimes it does create problems. but, the end result is, why add code to a build that is never going to be used? Sure, it’s most likely minor and not going to really have much impact, but there isn’t any reason to do so.
Plus, if you aren’t on the correct platform in Unity for the particular code, it greys out that code. Readability is certainly much better than having to deal with a set of if statements for your target platform.
this isnt necesarily applicable for your immediate case, but many devices can run on, lets says, android. for example Quest and Pico, and even a smartphone version of your game. so assembly directives may not necesarily be enough. A good guiding principle i would say is to abstract the logic away, by whatever method/s get the per-device functionality that you need, and only work with an interface to the functionality. so your “ifs” and assembly directives are hidden to most of your program at the “PlatformHandlerFactory” and “PlatformHandler” levels. The PlatformHandlerFactory should get you a IPlatformHandler instance (i.e. QuestPlatformHandler, WebGLPlatformHandler, etc).
disclaimer: i do not have much experience with this particular matter, so i may be missing some nuance, but this is something i will be dealing with soon and this was my anticipated approach. And looking forward to others input on the matter.
Use assembly directives for code that can’t be compiled on a platform and use if statements for code that can be compiled but behaves differently on different platforms. But don’t overuse either of them or your code will be a mess.
The main pain point for me when using preprocessor directives tends to be that the code inside of them does not compile in the editor either unless I’m targeting a particular platform. So for example all UNITY_ANDROID code is completely disabled when I’m targeting the iOS platform.
This can make it awkward to work on code, as you don’t get any help from the compiler unless you temporarily tinker with the preprocessor directives.
It can also cause issues whenever you rename a class or a variable, as that name might not get properly updated for any inactive code wrapped inside preprocessor directives.
Player player; #if UNITY_IOS
player = GetPlayerOnIOS(); #elif UNITY_ANDROID
target = GetPlayerOnAndroid(); // the ‘target’ variable was renamed to ‘player’, but it didn’t update for the inactive code #else
player = GetPlayerDefault(); #endif
The less intertwined your code for all the different platforms is, the easier time you will have working with them. The less variables you share between code for the different platforms, the less chances there are that code on one platform will break when something is changed for another one.
This is why using interfaces / inheritance to handle branching on a higher level is much better than having a lot of if-else branches at the bottom.