Build A Bridge Swift Library To Unity

So you want to trigger functionality in your Unity3D scene straight from your native Objective-C code? For example you have different scenes, and you want a regular iOS component (e.g. UIButton) to trigger a new scene. It takes some work, but it is doable.

See the top-ranked free assets on the Asset Store. Unity Technologies. Unity Technologies. Standard Assets (for Unity 2018.4) (5164) FREE. Unity Technologies. Apple provides one type of bridge file between Objective-C code & Swift code. Its main functionality is method or variable which is accessible in Objective-C should appear in Swift project. Here is some step you need to follow creating Bridging-Header file.

Thank you so, so much for doing all this work to integrate Unity with Swift. I am trying to integrate the unity view programmatically into a SwiftUI view, and I am having some trouble. In newer versions of xcode or unity, The initial swift integration method stopped working, but there was a workaround using the following code. In this video we take a look at how to build a simple but effective waypoint-based traffic system. With this system, you'll be able to create believable pede.

Read it … bitch 🙂

Unity exposes a way to call code in your Unity3D project (normally this will be javascript or C#). There are 3 components you need to get in place.

  1. Have a Unity3D scene
  2. Have a Unity3D script
  3. Have an Objective-C class

The way it works is that you can bridge your code from Objective-C to C# or Javascript in Unity3D with the ‘UnitySendMessage’ method.

First you’ll need to create a Unity3D project. Open up Unity3D and go to File > New project. Give it a name and place it somewhere on your hard drive. I’ll call my project ‘UnityMethodCalling’.

Unity3D will automatically create a scene for you, go nuts (or just put a box in it) and make sure the camera can see it.

Now create a folder in your ‘Assets’ folder and call it ‘scenes’. Save your scene to this folder and call it ‘scene1’.

Now create another scene via File > New scene. Go nuts again (or put a sphere in it). Just make sure it’s something else visually so you will be able to see the difference when we switch scenes by code. When you’re done, save the scene again in the same folder, but call it ‘scene2’.

Alright, so now we have 2 scenes and the goal of this tutorial is to switch between these 2 scenes at runtime when you press a UIButton.

First we’ll need to create a C# file in which we will declare the API methods we want to expose to our iOS project.

Create a new C# file in the ‘Assets’ folder and call it SceneChanger. Open the file by double-clicking on it. It should open in MonoDevelop.

You’ll see 2 methods here:

  1. Start: this method gets called first (duh…) and in this one you can initialise some properties
  2. Update: this methods get called every frame

We will write our custom method ‘ChangeScene’. This method will accept a level argument of type string. The string to use to load a new scene is the name of the scene. So in our case it will be ‘scene1’ or ‘scene2’.

Now we need to do one more thing and that is import the interop services package. Just add the following at the top of the file.

OK, that’s it for the Unity scripting part.

Now we need to move over to our Xcode project. Go to File > Build Settings and add the two scenes to the build. You can do this by dragging them from the Assets library.

Next choose iOS as your platform and click on ‘Player Settings’ at the bottom of the dialog. This will open the ‘Inspector panel’ and let you set all the settings for the Xcode project. In my case I will change the following settings (but they can -and probably will- be different of yours).

  • Default orientation : Landscape right
  • Bundle identifier: be.thenerd.unityMessaging
  • Target device: iPad
  • Target iOS version: 7.0

Now click on ‘Build & Run’ in the Build Settings dialog. Unity will ask you where to put the Xcode project. I will put it in a folder called ‘ios-builds’ outside of my Unity project and call it ‘unity-native-messaging’.

Click ‘Save’ and let Unity sprinkle its magic and create the Xcode project (this can take some time). If all goes well, your app will boot on your device and you should see the scene you’ve made.

Now we’ll have to add a native UI layer above the scene. I’ve written a blogpost how you can do this. I won’t repeat how you can do this right here, so if you want to know … read up on the other blog post and come back when you have done the necessary steps.

The code to put in the TNAppController (or how you called it) is the following:

If all went well and you run the app. You should see the following.

Alright almost there, now we just have to call the method in our C# file. We can do this by using the UnitySendMessage method. This method expects 3 arguments:

Build A Bridge Swift Library To Unity Maine Nh

  1. the name of the game object in Unity3D to which you have attached the C# script
  2. the name of the method
  3. parameters you would like to send to this method

This is a C method which allows us to bridge the gap between Objective-C++ and C#.

Create the following method.

We can now call this method from our ‘switchScenesTapped’ method.

You will maybe ask yourself … where is that ‘dummy’ string coming from. Well go back to Unity3D and open Scene1. To make this work, your C# script has to be attached to a game object. This is easy to do … just drag and drop the script on for example the cube. You will see in the inspector that a ‘Script component’ has been added to the cube. Also pay attention that we aren’t sending NSStrings, but C strings! (so no @ in front of the quotes)

Ok … but still, where is the dummy string coming from. Well that is actually just the name of the game object on which you have attached the script. You can change the name at the top of the Inspector panel.

In a real world application you would probably give it another name, and probably attach this script also to something else. But this is just for demonstration purposes.

So if we now look back at our code.

Argument 1: ‘dummy’ is the name (identifier) of our game object in Unity3D on which we have attached the C# script

Argument 2: ‘ChangeScene’ is the method name which we wrote in our C# script

Argument 3: ‘scene2’ is the name of the scene file we want to load

So build and run and if you tap on the switch button, you should see the scenes switch.

If you want to return, you will need to add the script also to an object in scene2 and call it dummy and put some logic in the switchScenesTapped method to check which scene is active.

I hope I could help some of you guys/girls out with messaging between Objective-C and Unity3D. I’ve uploaded my Unity3D project and Objective-C code to GitHub, so you can check it out yourself.

If you have any questions or remarks, please put them in the comments!

At the time of writing, there is currently no available example that demonstrates howto integrate Unity as library into an iOS application using SwiftUI. This blog postshares my solution to the problem hoping it can help others.

Introduction#

The solution given in this blog post works with Unity 2020.2.1f1 andXCode 12.3.

This blog post will be less graphics-oriented than the usual ones. I am anywayhoping that this writing can help the community!

When I first tried to integrate Unity into an iOS native application, I had a few goals in mind that needed more information than what I could at the time on StackOverflow and public repositories.

This article will show you how to:

  • Integrate Unity in an application using the SwiftUI lifecycle
  • Add native views overlaying Unity’s rendering
  • Communicate efficiently data from Unity to the native side
  • Communicate efficiently data from the native side to Unity

Spoiler alert: the last bullet point doesn’t use sendMessageToGOWithName()!

Build#

Build a bridge swift library to unity maine

iOS Example App#

Let’s build an iOS application that will host your Unity game. You can eitherstart it from scratch, or skip this step to directly integrate Unity in an existingapp.

If you decide to create a new application, please remember to select theSwiftUI App lifecycle.

You can use any lifecycle you prefer. Obviously, the integration may bedifferent than what is show here, but the overall code should remain identical.

Unity Project Generation#

Let’s build our Unity project. Building for iOS will generate the UnityFramework framework described in thedoc.

If you want to strictly follow my example respository, I exported the projectat unityapp/Build/iOS.

Workspace#

In order to easily share code between those two projects, we are going tocreate an XCode workspace. Create a workspace and reference your Unity projectas well as your example.

For more information about how to create a workspace and add the UnityFrameworkas a dependendcy, please have a look at the example from Unity Technologies.

This step isn’t mandatory but matches the example from the Unity team. You candirectly add the the Unity framework as a dependency to your native application.

Integrate Unity#

Getting into the interesting stuff. The process I will describe here issimilar to the one from the Unity example repository.

Let’s start first by writing a singleton that will manage the Unity instance:

Thanks to Simon Tysland for sharing the original UnityEmbeddedSwift.swiftfile that was wrapping the Unity framework management in Swift

The UnityBridge class is used to:

  • Load the UnityFramework at runtime, i.e., open the UnityFramework.framework file,and get the exported instance
  • Run the Unity instance
  • Show the Unity instance on the phone
Note that only one instance can live in your entire process. If you completelykill this instance, there will be no way to start it again.

Just keep in mind that the unityDidUnload() method is triggered by Unity when theframework is unloaded. This is possible because we first registered our UnityBridgeobject as a delegate with the call:

Let’s try our wrapper to ensure it’s properly working. We are going to displayour game in the background, and overlay some text made with the SwiftUI framework.

ContentView.swift

Let’s run the native app and appreciate the result:

As you can see, Unity doesn’t display anything. However, looking at theconsole we can see it running.

It looks like Unity needs some delay after it’s instanciaed and before itcan show up. I haven’t figure out why yet; maybe the framework re-create a view asynchroneously. I will definitely update this blog post whenever I have the answer.

In the meantime, you can fix this issue by adding a small delay on the main thread:

ContentView.swift

Right now, you may be thinking that this is a gross hack. You are right.

For my use case, this isn’t too much of an issue. I need to expose an API fromUnity that would allow the native iOS app to query vertices, to update some meshes, etc… Such an API shouldn’t be available before the appropriate GameObjects are instanciated and ready to be queried.

Instead of using a made up delay like that, I am going to only assumes Unity is ready when my scene and all the GameObjects are ready. When they are, a Unity scriptwill notify the native app that it can start showing Unity.

Before looking at the code for the solution, let’s look into how we can createa communication system to transfer data from Unity to the native code.

Communication: Unity to Native#

Calling native code from Unity can be done usingForeign Function Interface (FFI). To ensure our functions don’t get mangled, we willneed to annotate them as extern C.

Let’s create two new files in the Unity app, in the folder Assets/Plugins/iOS/.

Each time you update those files, you will need to re-build the Unity app.

For debugging purposes, I would advised to directly modify the XCode generatedproject if you want to iterate faster.

You can edit the files in unityapp/Build/iOS/Libraries/Plugins/iOS. Thosefiles are copied by Unity when generating the XCode project.

Build A Bridge Swift Library To Unity Project

NativeCallProxy.h

NativeCallProxy.mm

Let’s create a new C# script that will demonstrate how to call this function:

API.cs

Don’t forget to add this script component to an empty gameobject.

The line:

Let the compiler knows that this function symbol will be available in the binaryafter linking.

The sendUnityStateUpdate() function will be in charge of forwarding the callto an object that implements the onUnityStateChange() method prototype.

The last thing we haven’t talked about yet is registerAPIforNativeCalls(). Thisfunction saves the reference of an object implementing the NativeCallProtocolprotocol. This allows us to transfer the calls to an object pointed to by auser (i.e., developer integrating the Unity app). Every call performed on theUnity side is forwarded to this delegate object.

The code provided above is taken from the Unity example. This is the way they decided to forward the call.

Alternatively, you could also just export functions that will beavailable on the Unity side, without going through all the FrameworkLibAPI code.

However, I like this implementation so I can add glue code between theC data and the Objective-C one.

Congratulations, you just made your first native call from a Unity script!

All you have to now is to create a delegate that will receives the calls fromUnity. The delegate should implement the NativeCallProtocol, and should beregistered on the native side using:

We will create it together in a few sections, and it will be used to fixthe issue we had with Unity not showing up.

Communication: Native to Unity#

The native side may need to get data from Unity as well.However, the only API exposed by the Unity framework is thesendMessageToGOWithName() method.

This is not good. It only take a string argument. What happens if we needto send a vertex buffer? Something heavy?

For my use case, I have a lot of heavy data I want Unity to accesswithout any copy. I decided to re-use what we did in theUnity to native section to achieve that.

The idea is simple: we call a function declared in the native app with a functionpointer declared in the Unity side. The native app can save this function pointerand call it later.

Obviously, you will only be able to exchange C data structure between Unityscripts and the native app: raw pointers, struct, etc…

Let’s modify the Objective-C code to expose such a function:

NativeCallProxy.h

NativeCallProxy.mm

Same as before! Except here, we forward a function pointer to the native side.The function pointer points to a function declared in your Unity script. Isn’tthat amazing!

Our Unity script needs to be modified accordingly:

And finally, our API class to save the function pointer and expose a nice APIto our developers:

UnityBridge.swift

Build A Bridge Swift Library To Unity Maine N

You can give it a try to ensure everything is working:

Fix Unity Not Showing Up#

Coming back to the issue about how I “fixed” the Unity view not showing up.

I decided to use the sendUnityStateUpdate() function to inform the nativeapp when Unity was ready to show up. This way, I can ensure that there is adelay between the time I instanciate the framework and the time I display it.Moreover, this also ensures that developers will not attempt to use the APIbefore it’s fully initialized.

Let’s update the delegate to react to Unity being ready:

ContentView.swift

When our Unity GameObject API is ready, it will trigger the method onUnityStateChange()of our delegate. It will trigger the onReady callback used to display Unity andappend the Unity view in the hierarchy.

In addition to that, as I said in the beginning of this post, it’s important forme to expose an API to the user only when everything is fully ready on Unity’sside.

Going Further#

You made it! Don’t forget that the entire code presented here is availablein this repository.

I hope this post can help people struggling with integration issues. Some of theideas presented here can still be a bit rough on the edges.

I would recommend readers to make the code their own and improve it. For instance, the UnityBridge class could directly be exported from your Unity app. Anyone integrating your Unity app could directly build it and access the API.

There are also corner cases needs to be taken care of:

  • It’s technically possible for a developer to use the api before it’s available
  • [DllImport ('__Internal')] will not work properly on some platform whereplugins linking is different. For cross-platform implementation, you will needspecific code path (#if)

Build A Bridge Swift Library To Unity Road

If you have any issues, or if you think this post contain mistakes, please either:

  • Open an issue on my GitHub repository;
  • Contact me via Twitter