Unity C# Directives: For improving re-usability of code and enabling cross-platform compatibility.

In most of my projects, I do have to work on VR applications that are needed to be ported to different VR systems. It could be for HTC Vive or Oculus (Go, Rift) or any other third party VR systems that uses its own SDKs. It is true that for each project we can build separate code to run on separate devices, but in the long run, considering if the app needs a particular module that is pretty large and say for example you have it designed to work on SteamVR but you need to port it to Oculus and there are multiple places in the code that you need to change or even put comments to make it compatible with Oculus (button remapping, camera rig assignment and so on. By doing so, you may see that your code is no longer works with SteamVR unless you revert back to your earlier changes. This is extremely hectic when you are doing large scale project that need constant update of the module that you are using (considering you are continuously evaluating the module for improvements). It is time consuming. The concept of C# directive solve the way, where it makes the system ignore a portion of code that will be ignored by specific platform.

C# Directives is all about giving specific if then else conditionals to the existing code base and it uses the following #if, #elif, #else, #endif. To make things a little bit simpler, lets create a Unity project. Say we will create a scene where we just need to place a simple VR Camera Rig for HTC Vive. So first of all we head to Edit > Project Settings > Player. Over there we will find something called Scripting Define Symbols in the Other Settings:

scripting define symbols

If I have wanted I could have directly wrote a script to assign the SteamVR cameraRig as Transform, and so this project is meant to run using SteamVR cameraRig. I am however thinking that I might same scripts in different project using different system other than SteamVR. So keeping that in mind I am adding this:

add STEAM_VR to scripting define symbols

Now lets create a script (maybe AssignCamera.cs). Over there lets declare two variables for the camera rig and set its gameobject to appear when we play the Unity scene.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AssignCamera : MonoBehaviour {

    public Transform cameraRig;
    public OVRCamera cameraRig;

    // Use this for initialization
	void Start () {
	    cameraRig.gameObject.SetActive(true);
	}
	
	// Update is called once per frame
	void Update () {
        
	}
}

You will immediately notice that we do not have the library for OVRCameraRig (the Oculus Unity SDK from the Asset Store) and that’ss why we have red scribbles on OVRCamera and also another one on the second cameraRig. As you know there cannot be duplicate variables so I making a tweek to make the whole code compile properly.

#if (STEAM_VR)
    public Transform cameraRig;
#elif (OCULUS)
    public OVRCamera cameraRig;
#else
    public AnyOtherCameraRig cameraRig;
#endif
    // Use this for initialization
	void Start () {
	    cameraRig.gameObject.SetActive(true);	
	}
	
	// Update is called once per frame
	void Update () {
        
	}
}

Now you will see, the code compiles correctly. So what happened here. Since I assigned STEAM_VR as defined symbols, the system is considering only public Transform cameraRig and ignoring every other assignment for cameraRig. It also removes the conflicts of having duplicate variable. In future, if I wanted to make this code to work with Oculus system, we just removed STEAM_VR from Scripting Define Symbols from the Player Settings and added OCULUS instead. If Unity cannot see either STEAM_VR or OCULUS, it would have gone through public AnyOtherCameraRig cameraRig; . In this case, you make the code more reusable for multiple projects when it comes to cross platform compatibility. Directives could be anything and any names can be assigned by you as long as those terms are mentioned in Scripting Define Symbols. You can put something like say ANDROID;WINDOWS;BUSINESSAPP;PERSONALAPP (note that directives are seperated by semicolons) and then use #if #elif #else #endif and also not conditionals #(!ANYTHING) to organize your code. Hopefully you can use your new found knowledge to make your existing codes more reusable for different projects.