Platform Dependent Code - Best Practice

Hi All,

I have a best practice question here regarding Platform Dependent code.

I’m building a basic plane shooter (think Sky Force or Raiden) and want to play it on my computer or an iPhone.

As such I’ll need 2 control schemes. I was looking at the platform dependent compilation Unity manual (Unity - Manual: Conditional Compilation) which has the #define tools.

An excerpt of my code using this approach:

public class myClass
{
...
        public enum PlatformType
            {
                MacOS,
                iPhone,
                Windows
            }
        
            public static PlatformType PlatformTypeCheck { get; private set; }
        Awake()
    {
    
            #if UNITY_IOS
                    PlatformTypeCheck = PlatformType.iPhone;
            #elif UNITY_STANDALONE_OSX
                    PlatformTypeCheck = PlatformType.MacOS;
            #endif
    ...
    }
...
}

This allows me to determine the platform type and then utilise the appropriate controller scheme in my inputcontroller class.

The problem I have (and currently it’s not a ‘problem’ per se) is that this is not very elegant, and I’m worried that I’m adding in too many per-frame checks during update.

During compile I’ve determined the platform type, yet every frame I’m checking to see which platform type I’m using, before then detecting a keypress or finger touch (see below).

void Update()
{
    if(GameController.PlatformTypeCheck == GameController.PlatformType.iPhone)
    {
        DetectTouchScreenFinger();
    }

    if(GameController.PlatformTypeCheck == GameController.PlatformType.MacOS)
    {
        DetectKeyPress();
    }
}

I thought how about i just put the platform check in the update() method? Would this only compile the required code, therefore it wouldn’t be a performance hit? If so cool, however I know that moving forward there will be other areas of my code (my game is fairly basic at the moment) that will require me to check for platform. Should it be done as required in it’s local class, or should I keep it centralised, check once then do a bool check where I need it?

I’m looking for best practice feedback here, or, if I’m completely off track with regards to platform dependency (first time I’m writing across platform), then some other suggestions would be great!

Thank you in advance for your feedback.

Regards,
Joel

As for this question:

Would this only compile the required code, therefore it wouldn’t be a performance hit?

Correct the if check is done at compile time, not at run time. If you are building for android, it won’t compile things inside #if UNITY_IOS. visual studio makes this clear by making is gray in the editor. Visual studio also doesn’t compile it, so it won’t even show compiler errors in that section until you switch build target.


As for best practice. Due to performance reasons it is in theory better to not use the bool/enum checks, as it just slows down your code and increases your build size (ever so slightly). The downside being that you don’t get compiler errors on parts that are not compiled, so if you have a lot of those #if in a method or class, you might accidentally cause some compiler errors when you rename something, which you might not notice right away and only after switching build targets… Personally I think that is a minor issue, but could be rather annoying during development when you accidentally commit code that doesn’t compile on a different platform.


In some cases in order to reduce the number of platform checking in your code (which might make your code more difficult to read due to huge methods even though half is not compiled), in some situations you can work with interfaces or abstract classes, where each platform has its own implementation of that interface. For Example:

public interface IInputManager
{
    bool WantsToJump();
}

public class TouchInputManager : IInputManager
{
    public bool WantsToJump()
    {
       // ... check whether touches say jump
    }
}

public class StandAloneInputManager : IInputManager
{
    public bool WantsToJump()
    {
        //... check whether w or up arrow is pressed
    }
}

This way, you can choose to put the entire classes inside #if to reduce build size by a tiny (prolly not noticeable) bit. But if you don’t, you only need to check this it on 1 place on startup, the place where you declare which InputManager, like this:

public Example : MonoBehaviour
{
    IInputManager inputManager;

    void Start()
    {
#if UNITY_IOS
        inputManager = new TouchInputManager();
#else
        inputManager = new StandAloneInputManager();
#endif
    }

    void Update()
    {
        if (inputManager.WantsToJump())
        {
            // Do something
        }
    }
}

But ofcourse you should use a pattern that you think makes most sense for your situation. It’s mostly down to preference as the possible performance hit is not noticeable in most cases anyway.
btw. wrote this in the browser, so there might be silly syntax errors in code.

Amazing! Thank you so much for the insight. I don’t fully understand interfaces, but I can now see why you might use them (I can also already imagine a whole lot of other scenarios).

As far as compiling - good to hear that it only compiles what you need so there is only one check. Looks like I have some work ahead of me this weekend!

Thanks again! Appreciated!