Call methods on Unity3D straight from your Objective-C code

The problem

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.

tl;dr

Read it … bitch 🙂

The solution

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’.

public void ChangeScene( string scene ) {
    Application.LoadLevel(scene);
}
Swift

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.

using System.Runtime.InteropServices;
Swift

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:

#import <UIKit/UIKit.h>
#import "UnityAppController.h"
#import "UI/UnityView.h"
#import "UI/UnityViewControllerBase.h"

@interface TNAppController : UnityAppController

- (void)createViewHierarchyImpl;

@end

@implementation TNAppController
- (void)createViewHierarchyImpl {
    
	_rootController	= [[UIViewController alloc] init];
	_rootView		= [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    _rootController.view = _rootView;
    
    UIButton *btnNext = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    btnNext.backgroundColor = [UIColor whiteColor];
    [btnNext setTitle:@"Switch scenes" forState:UIControlStateNormal];
    btnNext.frame = CGRectMake(0, 0, 200, 44);
    btnNext.center = CGPointMake(_rootView.bounds.size.height / 2, _rootView.bounds.size.width  - 80);
    
    [btnNext addTarget:self action:@selector(switchScenesTapped:) forControlEvents:UIControlEventTouchUpInside];
    
    [_rootView addSubview:_unityView];
    [_rootView addSubview:btnNext];
}

- (void)switchScenesTapped:(id)sender {
	NSLog(@"[TNAppController] Switch scenes tapped");
}

@end

IMPL_APP_CONTROLLER_SUBCLASS(TNAppController)
Swift

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:

  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.

- (void)callUnityObject:(const char*)object Method:(const char*)method Parameter:(const char*)parameter {
    UnitySendMessage(object, method, parameter);
}
Swift

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

- (void)switchScenesTapped:(id)sender {
    NSLog(@"[TNAppController] Switch scenes tapped");
    
    [self callUnityObject:"dummy" Method:"ChangeScene" Parameter:"scene2"];
}
Swift

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.

- (void)switchScenesTapped:(id)sender {
    NSLog(@"[TNAppController] Switch scenes tapped");
    
    [self callUnityObject:"dummy" Method:"ChangeScene" Parameter:"scene2"];
}
Swift

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!

References

http://docs.unity3d.com/ScriptReference/index.html

http://docs.unity3d.com/Manual/CreatingAndUsingScripts.html

http://docs.unity3d.com/Manual/Plugins.html

Share this post

5 Responses

  1. Thank you!
    Can you check on the link to the blog spot about hown to add a native UI layer above the scene please?

  2. Brilliant post, as always! Thanks and keep up the amazing work!
    I used it to change a string in my Unity game, based on the iOS settings:

    //objC:
    – (IBAction)buttonAction:(id)sender {
    NSString *name = @”Pawel”;
    NSString *message = [NSString stringWithFormat:@”Welcome Back %@”,name];
    [self callUnityObject:”UIActionHub” Method:”ChangeLabel” Parameter:[message UTF8String]];
    }
    (UIActionHub is a unity node that I attached all my scripts to)

    //c#:
    public void ChangeLabel (string text){
    TextMesh textObject = GameObject.Find(“Label”).GetComponent();
    textObject.text = text;
    }

  3. Hello,
    Hi! I’ve been following the tutorial but I’m having some issue when it comes to switch scene.
    When i call method : [self callUnityObject:”dummy” Method:”ChangeScene” Parameter:”scene2″];
    My project crashed app, and show error:
    Unloading 2 Unused Serialized files (Serialized files now loaded: 2)
    What can I do? Please help me resolve this issue. Thanks!

  4. As a newbie the “dummy” object I always thought this was the c# script… Thanks to your tutorial now I found out it is not, but the scene object. Thanks mate, you are the best. God bless.

Leave a Reply

Your email address will not be published. Required fields are marked *

Related Posts