unity plugin
Thanks to Andy...
Okay, after playing few days with Unity3d on Mac I finally figured it out. All the code in this guide is dummy. I have written this stuff in 15 minutes or so, so don't be bothered by stupid mistakes and typos.
1) Open Unity, create new project (File -> New Project) and save it somewhere
2) When the project is generated it has the following structure:
4) Put your compiled library (.a) file and necessary headers inside of ProjectName/Assets/Plugins/iOS or copy the source code of your library there (.mm, .h, .m, etc..). Remember, normally you can only access C-functions from C#, so you'll have to wrap your Objective-C stuff in C-code somehow, in my case all Objective-C objects were implemented in a form of Singleton so it wasn't hard to make a C-style wrapper around, for instance:
CWrapper.h:
CWrapper.mm
6) Inside of MySDK folder create MySDK.cs file, the dummy example of C# wrapper would look like this:
Don't forget to shutdown Unity Editor when you run this script, otherwise it may fail to build a package.
If you have some issues and package does not show up, this script always prints log to export.log
Next steps make sense only if you want to make a Demo unity project for your library (good for testing at least)
9) You can put created Unity project (ProjectName.unity) to Assets/MySDKDemo so you have a demo inside of your package.
10) Create a simple script for your Demo Unity3d scene at Assets/MySDKDemo/MySDKDemo.cs, for example:
12) Build the XCode project and run on device.
Few notes
1) Plugins don't work in Unity Editor, simply because they're not compiled in the real-time, well, not sure but probably until you use C# in your plugins, probably C# stuff gets linked immidiately and works in Editor environment.
2) Callbacks from C# to C can be passed using C# delegates, on C-side you use standard functions declarations, on C# side you declare delegates with the same signature. It seems that booleans, integers and strings (C: char*) are marshalled flawlessly (I don't talk about memory management policy and who's responsible to release memory or return value policies).
However it will not work on iOS builds out-of-box due to platform limitations, but C#-to-C callbacks still can be implemented using MonoPInvokeCallbackAttribute, useful links on this topic:
http://docs.xamarin.com/guides/ios/advanced_topics/limitations#Reverse_Callbacks http://forums.xamarin.com/discussion/1171/difference-between-bmac-exe-on-xamarin-and-monomac-when-compiling-masshortcutsharp
Actually in Unity 4 there's AOT.MonoPInvokeCallbackAttribute already implemented, it's limited to static delegates that can be passed to unmanaged code, but still better than nothing.
3) There's a way to get Unity RootViewController using UnityGetGLViewController function. Just declare this function in your implementation file, i.e.:
4) There's much more magic and ugly stuff in details, keep your C interfaces as simple as possible otherwise marshalling can become your nightmare and also keep in mind that managed-to-unmanaged is generally expensive.
Good luck!
Okay, after playing few days with Unity3d on Mac I finally figured it out. All the code in this guide is dummy. I have written this stuff in 15 minutes or so, so don't be bothered by stupid mistakes and typos.
1) Open Unity, create new project (File -> New Project) and save it somewhere
2) When the project is generated it has the following structure:
- ProjectName/Assets (That's what you need)
- ProjectName/Library (Nevermind what's there)
- ProjectName/ProjectSettings (You don't care about it)
- ProjectName/ProjectName.sln (MonoDevelop project)
4) Put your compiled library (.a) file and necessary headers inside of ProjectName/Assets/Plugins/iOS or copy the source code of your library there (.mm, .h, .m, etc..). Remember, normally you can only access C-functions from C#, so you'll have to wrap your Objective-C stuff in C-code somehow, in my case all Objective-C objects were implemented in a form of Singleton so it wasn't hard to make a C-style wrapper around, for instance:
CWrapper.h:
extern "C" void MySDKFooBarCFunction();
CWrapper.mm
#import "CWrapper.h"
#import "MyObjectiveCLibrary.h" // your actual iOS library header
void MySDKFooBarCFunction() {
[MyObjectiveCLibrary doSomeStuff];
}
5) Then go to ProjectName/Assets and create a folder for CSharp
wrapper class(es), call it whatever you want, for example:
ProjectName/Assets/MySDK6) Inside of MySDK folder create MySDK.cs file, the dummy example of C# wrapper would look like this:
using UnityEngine;
using System;
using System.Runtime.InteropServices;
public class MySDK
{
// import a single C-function from our plugin
[DllImport ("__Internal")]
private static extern void MySDKFooBarCFunction();
// wrap imported C-function to C# method
public static void FooBarCFunction() {
// it won't work in Editor, so don't run it there
if(Application.platform != RuntimePlatform.OSXEditor) {
MySDKFooBarCFunction();
}
}
}
7) Create a shell script to pack this stuff to .unitypackage and
place it near your project folder (not inside). Adjust EXPORT_PATH and
PROJECT_PATH variables in the script for your needs.#!/bin/sh
WORKDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
UNITY_BIN="/Applications/Unity/Unity.app/Contents/MacOS/Unity"
EXPORT_PATH="${WORKDIR}/ProjectName.unitypackage"
PROJECT_PATH="${WORKDIR}/ProjectName"
ASSETS_PATH="Assets"
$UNITY_BIN -batchmode -quit \
-logFile export.log \
-projectPath $PROJECT_PATH \
-exportPackage $ASSETS_PATH $EXPORT_PATH
8) Run the created bash script to get your package build. All stuff
from Assets will be included in XCode project for your Unity Project
when you generate it via File -> Build Settings in Unity Editor. You
can use generated package to distribute your code to other developers so
they can simply include your library to their Unity projects by double
clicking on the package file.Don't forget to shutdown Unity Editor when you run this script, otherwise it may fail to build a package.
If you have some issues and package does not show up, this script always prints log to export.log
Next steps make sense only if you want to make a Demo unity project for your library (good for testing at least)
9) You can put created Unity project (ProjectName.unity) to Assets/MySDKDemo so you have a demo inside of your package.
10) Create a simple script for your Demo Unity3d scene at Assets/MySDKDemo/MySDKDemo.cs, for example:
using UnityEngine;
using System;
using System.Collections;
public class MySDKDemo : MonoBehaviour
{
private GUIStyle labelStyle = new GUIStyle();
private float centerX = Screen.width / 2;
// Use this for initialization
void Start ()
{
labelStyle.fontSize = 24;
labelStyle.normal.textColor = Color.black;
labelStyle.alignment = TextAnchor.MiddleCenter;
}
void OnGUI ()
{
GUI.Label(new Rect(centerX - 200, 20, 400, 35), "MySDK Demo", labelStyle);
if (GUI.Button(new Rect(centerX - 75, 80, 150, 35), "DoStuff"))
{
MySDK.FooBarCFunction();
}
}
}
11) Go to Unity Editor. Find the "Main Camera" in left sidebar in
Unity Editor, select it and in the bottom of Inspector panel (right
sidebar) click on AddComponent, select Scripts -> MySDKDemo script12) Build the XCode project and run on device.
Few notes
1) Plugins don't work in Unity Editor, simply because they're not compiled in the real-time, well, not sure but probably until you use C# in your plugins, probably C# stuff gets linked immidiately and works in Editor environment.
2) Callbacks from C# to C can be passed using C# delegates, on C-side you use standard functions declarations, on C# side you declare delegates with the same signature. It seems that booleans, integers and strings (C: char*) are marshalled flawlessly (I don't talk about memory management policy and who's responsible to release memory or return value policies).
However it will not work on iOS builds out-of-box due to platform limitations, but C#-to-C callbacks still can be implemented using MonoPInvokeCallbackAttribute, useful links on this topic:
http://docs.xamarin.com/guides/ios/advanced_topics/limitations#Reverse_Callbacks http://forums.xamarin.com/discussion/1171/difference-between-bmac-exe-on-xamarin-and-monomac-when-compiling-masshortcutsharp
Actually in Unity 4 there's AOT.MonoPInvokeCallbackAttribute already implemented, it's limited to static delegates that can be passed to unmanaged code, but still better than nothing.
3) There's a way to get Unity RootViewController using UnityGetGLViewController function. Just declare this function in your implementation file, i.e.:
extern UIViewController *UnityGetGLViewController();
And use UnityGetGLViewController()
whenever you need to get an access to RootViewController.4) There's much more magic and ugly stuff in details, keep your C interfaces as simple as possible otherwise marshalling can become your nightmare and also keep in mind that managed-to-unmanaged is generally expensive.
Good luck!