Unity as a Library

Since Unity 2019.3, Unity projects can be exported as a library that can be used in other applications. As such, other applications can harness the full power of Unity to interact and render 2D or 3D realtime content. Taking advantage of this feature, our SDK provides a communication layer to make it simpler to communicate between the native apps and our SDK, with support for asynchronous calls and callbacks. The platforms supported by our SDK are:

  • Android
  • iOS

Unity also supports Windows and the Universal Windows Platform, but the bi-directional communication layer in the Didimo Unity SDK is implemented for iOS and Android only.

661

Adding Unity as a library to a native application

On the official documentation more information can be found on using Unity as a library, and its limitations.

Setup

The application that includes the resulting library, is responsible to manage the Unity runtime life-cycle. So first, there must be a Unity project where the Didimo Unity SDK will be included, and a native application that will include the native Unity Library produced by this SDK.

Android Integration

For a sample that already integrates our SDK with a native android app, checkout our didimo-android-example repository, and follow the instructions on the readme.

For a walkthrough of using our SDK as a Library using Android Studio, follow these steps:

  1. Download and extract the UAAL example: https://github.com/Unity-Technologies/uaal-example.git

  2. Checkout our didimo example Unity project: https://github.com/didimoinc/didimo-digital-human-unity-project.git

  3. Open the didimo example Unity project Using Unity 2020.3.12f1.

  4. From the Unity Build, Android platform settings, enable Export project.

  5. Build and Export to new folder in the UAAL example: uaal-example-master\uaal-example-master\NativeAndroidApp\androidBuild

  6. Using AndroidStudio: Open uaal-example-master\uaal-example-master\NativeAndroidApp\app

  7. Open settings.gradle

  8. Add a new project pointing to unityLibrary module, at the end of the file:

include ':unityLibrary'
project(':unityLibrary').projectDir=new File('..\\androidBuild\\unityLibrary'
  1. Open build.gradle (Module: app)

  2. Add the following in dependencies:

implementation project(':unityLibrary')
implementation fileTree(dir: project(':unityLibrary').getProjectDir().toString() + ('\\libs'), include: ['*.jar'])
  1. Open build.gradle (Project: NativeAndroidApp) file

  2. Add the following in:

...

  allprojects{

    repositories{

      ...

      flatDir {

        dirs "${project(':unityLibrary').projectDir}/libs"

      }

      ...

    }

  }
  1. Copy gradle.properties file from exported Unity project root folder to the native application root folder.

  2. Choose File > Invalidate Caches and restart

  3. In OverrideUnityActivity.java change the package identifier to:
    com.Didimo.DidimoSDKExampleProject.OverrideUnityActivity

  4. Choose Make Project and then Run

References:

  1. Documentation: https://docs.unity3d.com/2020.3/Documentation/Manual/UnityasaLibrary-Android.html
  2. Sample project for using Unity as a library: https://github.com/Unity-Technologies/uaal-example/blob/master/docs/android.md

iOS Intergration

  1. Documentation: https://docs.unity3d.com/2020.3/Documentation/Manual/UnityasaLibrary-iOS.html
  2. Sample project for using Unity as a library: https://github.com/Unity-Technologies/uaal-example/blob/master/docs/ios.md

Bi-Directional communication

The Didimo Unity SDK provides out-of-the-box support for Android and iOS, by implementing bi-directional communication, so the application can communicate directly with the Didimo Unity SDK, and receive callbacks for completion, errors, and other relevant information.

On the Unity (C#) side, the class BiDirectionalNativeInterface is inherited by classes that implement functionality, like loading a didimo in the example below. The Library's DidimoUnityInterface and required callbacks and interfaces, are included as plugins in the generated Library so they can be called natively by the applications. The C# implementation varies a little between Android and iOS due to native language constraints.

1014

Bi-Directional communication for Android

881

Bi-Directional communication for iOS

The class BiDirectionalNativeInterface will automatically call the native plugins to register the callbacks that will handle communication (demonstrated by the arrows labeled "OnStart"). The public interface of DidimoUnityInterface can thus be accessed natively to call all the functionality exposed in the SDK.

An example on how to perform a call to BuildDidimoFromDirectory is demonstrated below, for Android and iOS.

didimoUnityInterface.BuildDidimoFromDirectory(
            "folder/where/didimo/gltf/package/was/extracted",
            "didimoKey",
            object : DidimoUnityInterface.DefaultResponseInterface {
                override fun onSuccess() {
                    // do something
                }

                override fun onError(message: String?) {
                    // do something
                }
            }
        )
// see https://stackoverflow.com/questions/65860970/swift-pass-escaping-closure-to-c-api-callback
// and https://oleb.net/blog/2015/06/c-callbacks-in-swift
// and https://forums.swift.org/t/swift-function-as-a-callback-to-a-c-function/37409/11
final class Weak<T: AnyObject> {
    weak var value: T?
    init(_ value: T) {
        self.value = value
    }

}
// To have a reference to some object when the callback is received, it must be passed down to the C call
let obj = SomeClass()
let weakObj = Weak(obj)
// This will retain weakObj, but won't retain obj
let objReference = Unmanaged.passRetained(weakObj).toOpaque()
DidimoUnityInterface.buildDidimo(fromDirectory: "folder/where/didimo/gltf/package/was/extracted",
                                 didimoKey: "didimoKey",
                                 successCallback: {
                                     reference in
                                     let someClassInstance = Unmanaged<SomeClass>.fromOpaque(reference).takeRetainedValue()
                                     // do something with someClassInstance
                                 }
                                 , errorCallback: {
                                     reference, errorMessage in
                                     let someClassInstance = Unmanaged<SomeClass>.fromOpaque(reference).takeRetainedValue()
                                     // do something with someClassInstance, or errorMessage
                                 }
                                 ,
                                 objectPointer: objReference)
}

For this example of loading a didimo from a folder, you need to first download a didimo package and extract it. You also need to store the didimo key, so you can provide it to this function. For more information on generating didimos and didimo keys, read our API documentation.