Valerio “Lotti” Riva – Interactive Project
valerio.riva@gmail.com
@ValerioRiva
http://it.linkedin.com/in/valerioriva/
Avoi...
Nice to meet you!
• Web / Game developer
• Recent works @ Interactive Project
• World Cup Juggler
• OverVolt: crazy slot c...
Why develop a Unity3D mobile plugin?
• Access to device native features
• Implement third-party SDK (analytics, advertisin...
Extending Unity 4.x
• Unity supports C/C++ libraries. “extern”-alized
functions/methods can be called from C#
• All plugin...
Extending Unity (iOS)
• Call externalized Object-C methods
• Must wraps third-party SDK if their methods are not
externali...
Extending Unity (Android)
• Use JNI (Java Native Interface), Unity provides Helper
classes
• Call native methods directly ...
Extending Unity (WP8)
• Access native code directly from Unity
• Use of callbacks to return data from native code
• Unity’...
Extending Unity (remarks)
• Scripting define symbols are your friends
• Native calls are CPU intensive
• Provide fake resu...
Case study: Flurry plugin
• Wrap Flurry SDK to made it accessible from Unity
• Flurry SDK is simple to use, just call stat...
Flurry plugin (iOS)
• Flurry SDK is not “extern”-alized
• Dictionary<string, string> must be translated somewhat to
NSMuta...
Flurry plugin (iOS)
// FlurryiOS.m - created by Faizan Naqvi on 1/10/13.
#import <stdio.h>
#include "Flurry.h" //Flurry SD...
Flurry plugin (iOS)
void FlurryiOS_logEventWithParameters(const char* eventId,const char *parameters) {
NSString *params =...
Flurry plugin (iOS)
Meanwhile on Unity side…
#region FlurryiOS_Imports
[DllImport("__Internal", CharSet = CharSet.Ansi)]
p...
Flurry plugin (WP8)
• Flurry SDK is compiled with .NET 4.5, import it and Unity will go
mad!
• We need the “fake & real” l...
Flurry plugin (WP8)
using FlurryWP8SDK.Models;
using System;
using System.Collections.Generic;
namespace FlurryWP8SDK.Mode...
Flurry plugin (Android)
• As said before, Flurry doesn’t involve complex logic…
• … so we save time and wrote plugin direc...
Flurry plugin (Android)
ROME 24 June 2014 – Valerio Riva
17
public Flurry LogEvent(string eventId, Dictionary<string,strin...
and for more complex plugins…?
• Do most of logic on native side to minimize native calls from Unity
• Messages examples (...
Run on UI Thread
• iOS
dispatch_async(dispatch_get_main_queue(), ^{
// Your code to run on the main queue/thread
});
[[NSO...
Run on UI Thread (WP8)
• MainPage.xaml.cs
public partial class MainPage : PhoneApplicationPage
{
public void InvokeOnAppTh...
Resources and examples
Resources
• http://docs.unity3d.com/Manual/Plugins.html
• http://docs.unity3d.com/Manual/wp8-plugin...
Thank you!
ROME 24 June 2014 – Valerio Riva
22
Question Time
No animals were harmed in the making of this talk
valerio.riv...
Upcoming SlideShare
Loading in …5
×

Avoid loss of hair while coding Unity3D plugins for mobile

2,553 views

Published on

by Valerio “Lotti” Riva – Interactive Project
Talk held on Codemotion Tech Meetup #4 - Rome Summer Edition

Published in: Technology, Art & Photos
0 Comments
7 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,553
On SlideShare
0
From Embeds
0
Number of Embeds
12
Actions
Shares
0
Downloads
13
Comments
0
Likes
7
Embeds 0
No embeds

No notes for slide

Avoid loss of hair while coding Unity3D plugins for mobile

  1. 1. Valerio “Lotti” Riva – Interactive Project valerio.riva@gmail.com @ValerioRiva http://it.linkedin.com/in/valerioriva/ Avoid loss of hair while coding Unity3D plugins for mobile ROME 24 June 2014 – Valerio Riva Codemotion Tech Meetup #4 – Roma Summer Edition
  2. 2. Nice to meet you! • Web / Game developer • Recent works @ Interactive Project • World Cup Juggler • OverVolt: crazy slot cars ROME 24 June 2014 – Valerio Riva 2
  3. 3. Why develop a Unity3D mobile plugin? • Access to device native features • Implement third-party SDK (analytics, advertising, in-app purchases, game services, etc.) • Save/Earn money • Personal growth ROME 24 June 2014 – Valerio Riva 3
  4. 4. Extending Unity 4.x • Unity supports C/C++ libraries. “extern”-alized functions/methods can be called from C# • All plugins must be placed inside “Assets/Plugins” folder • Platform-dependent plugins must be placed inside specific folders (x86, x86_64, Android, iOS, WP8, Metro, etc.) • Available only on Unity3D Pro/Mobile ROME 24 June 2014 – Valerio Riva 4
  5. 5. Extending Unity (iOS) • Call externalized Object-C methods • Must wraps third-party SDK if their methods are not externalized • Gameobjects can receive messages from native code • Receiver methods declared on GO’s components must have only 1 string parameter as signature ROME 24 June 2014 – Valerio Riva 5
  6. 6. Extending Unity (Android) • Use JNI (Java Native Interface), Unity provides Helper classes • Call native methods directly from Unity • Gameobjects can receive messages from native code • Receiver methods declared on GO’s components must have only 1 string parameter as signature • On specific cases, Unity Player activity must be extended • Android Manifest editing is often required ROME 24 June 2014 – Valerio Riva 6
  7. 7. Extending Unity (WP8) • Access native code directly from Unity • Use of callbacks to return data from native code • Unity’s Mono (v2.6) doesn’t support .NET >= 4.0 • Artefacts are needed to use .NET >= 4.0 libaries • “Always” needs a fake and a real plugin – Unity will overwrite fake one with the real one automatically • In specific cases, write a plugin is a monkey job ROME 24 June 2014 – Valerio Riva 7
  8. 8. Extending Unity (remarks) • Scripting define symbols are your friends • Native calls are CPU intensive • Provide fake results for in-editor usage • Every native UI call must run inside native UI thread • Every callback must run inside Unity thread (WP8) • Save time by testing plugin on a native app • Remember to include Unity library if needed • classes.jar • UnityEngine.dll ROME 24 June 2014 – Valerio Riva 8
  9. 9. Case study: Flurry plugin • Wrap Flurry SDK to made it accessible from Unity • Flurry SDK is simple to use, just call static methods (Advertising, In-App Purchase, …, are more complex plugin) • We have to code wrappers for each platform • Place platforms SKDs on the right directories • FlurryAnalytics-4.0.0.jar -> Plugins/Android/ • libFlurry_5.0.0.a -> Plugins/iOS/ • FlurryWP8SDK.dll -> Plugins/WP8/ • FlurryWP8SDK.dll -> Plugins/ (the WP8 fake one) ROME 24 June 2014 – Valerio Riva 9
  10. 10. Flurry plugin (iOS) • Flurry SDK is not “extern”-alized • Dictionary<string, string> must be translated somewhat to NSMutableDictionary • Each KeyValuePair<string,string> are concatenated to form a single string ROME 24 June 2014 – Valerio Riva 10 //FlurryiOS.h - created by PRADA Hsiung on 13/3/8. extern "C" { void FlurryiOS_startSession(unsigned char* apiKey); void FlurryiOS_setEventLoggingEnabled(BOOL bEnabled); void FlurryiOS_logEventWithParameters(unsigned char* eventId,unsigned char *parameters); }
  11. 11. Flurry plugin (iOS) // FlurryiOS.m - created by Faizan Naqvi on 1/10/13. #import <stdio.h> #include "Flurry.h" //Flurry SDK headers void FlurryiOS_startSession(const char* apiKey) { NSString *str = [NSString stringWithUTF8String:apiKey]; [Flurry startSession:str]; } void FlurryiOS_setEventLoggingEnabled(BOOL bEnabled){ [Flurry setEventLoggingEnabled:bEnabled]; } ROME 24 June 2014 – Valerio Riva 11
  12. 12. Flurry plugin (iOS) void FlurryiOS_logEventWithParameters(const char* eventId,const char *parameters) { NSString *params = [NSString stringWithUTF8String:parameters]; NSArray *arr = [params componentsSeparatedByString: @"n"]; NSMutableDictionary *pdict = [[[NSMutableDictionary alloc] init] autorelease]; for(int i=0;i < [arr count]; i++) { NSString *str1 = [arr objectAtIndex:i]; NSRange range = [str1 rangeOfString:@"="]; if (range.location!=NSNotFound) { NSString *key = [str1 substringToIndex:range.location]; NSString *val = [str1 substringFromIndex:range.location+1]; //NSLog(@"kv %@=%@n",key,val); [pdict setObject:val forKey:key]; } } if([pdict count]>0) { [Flurry logEvent:[NSString stringWithUTF8String:eventId] withParameters:pdict timed:false]; } else FlurryiOS_logEvent(eventId); } ROME 24 June 2014 – Valerio Riva 12
  13. 13. Flurry plugin (iOS) Meanwhile on Unity side… #region FlurryiOS_Imports [DllImport("__Internal", CharSet = CharSet.Ansi)] private static extern void FlurryiOS_startSession([In, MarshalAs(UnmanagedType.LPStr)]string apiKey); [DllImport("__Internal")] private static extern void FlurryiOS_setEventLoggingEnabled(bool bEnabled); [DllImport("__Internal", CharSet = CharSet.Ansi)] private static extern void FlurryiOS_logEventWithParameters([In, MarshalAs(UnmanagedType.LPStr)]string evendId, [In, MarshalAs(UnmanagedType.LPStr)]string parameters); #endregion ROME 24 June 2014 – Valerio Riva 13
  14. 14. Flurry plugin (WP8) • Flurry SDK is compiled with .NET 4.5, import it and Unity will go mad! • We need the “fake & real” library approach, but… • Using Flurry SDK doesn’t involve use of complex logic or complex user defined classes… • … we can use the downloaded FlurryWP8SDK.dll as “real” • and code only the fake dll! Yes, this is the particular case where you can bring in monkeys! ROME 24 June 2014 – Valerio Riva 14
  15. 15. Flurry plugin (WP8) using FlurryWP8SDK.Models; using System; using System.Collections.Generic; namespace FlurryWP8SDK.Models { public enum Gender { Unknown = -1, Female = 0, Male = 1 } public class Parameter { public Parameter(string name, string value) { Name = name; Value = value; } public string Name { get; set; } public string Value { get; set; } } } namespace FlurryWP8SDK { public sealed class Api { private static Api instance = null; public static Api Current { get { return instance; } } public void DummyInitiator() {} public static void EndSession() {} public static void EndTimedEvent(string eventName) {} public static void EndTimedEvent(string eventName, List<Parameter> parameters) {} public static void LogEvent(string eventName) {} public static void LogEvent(string eventName, List<Parameter> parameters, bool timed) {} public static void SetAge(int age) {} public static void SetGender(Gender gender) {} public static void SetLocation(double latitude, double longitude, float accuracy) {} public static void SetSessionContinueSeconds(int seconds) {} public static void StartSession(string apiKey) {} } } ROME 24 June 2014 – Valerio Riva 15 That was easy, now give me peanuts!
  16. 16. Flurry plugin (Android) • As said before, Flurry doesn’t involve complex logic… • … so we save time and wrote plugin directly in Unity using JNI Helper classes ROME 24 June 2014 – Valerio Riva 16 public Flurry StartSession(string apiKey) { #if !UNITY_EDITOR && UNITY_WP8 Api.StartSession(apiKey); #elif !UNITY_EDITOR && UNITY_ANDROID using(AndroidJavaClass cls_UnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) using(AndroidJavaObject obj_Activity = cls_UnityPlayer.GetStatic<AndroidJavaObject>("currentActivity")) using(AndroidJavaClass cls_FlurryAgent = new AndroidJavaClass("com.flurry.android.FlurryAgent")) { cls_FlurryAgent.CallStatic("onStartSession", obj_Activity, apiKey); } #elif !UNITY_EDITOR && UNITY_IPHONE FlurryiOS_startSession(apiKey); #endif return this; }
  17. 17. Flurry plugin (Android) ROME 24 June 2014 – Valerio Riva 17 public Flurry LogEvent(string eventId, Dictionary<string,string> parameters, bool timed) { #if !UNITY_EDITOR && UNITY_WP8 List<Parameter> p = new List<Parameter>(); foreach(KeyValuePair<string,string> i in parameters) { p.Add(new Parameter(i.Key,i.Value)); } Api.LogEvent(eventId, p, timed); #elif !UNITY_EDITOR && UNITY_ANDROID using(AndroidJavaObject obj_HashMap = new AndroidJavaObject("java.util.HashMap")) { // Call 'put' via the JNI instead of using helper classes to avoid: "JNI: Init'd AndroidJavaObject with null ptr!" IntPtr method_Put = AndroidJNIHelper.GetMethodID(obj_HashMap.GetRawClass(), "put“, "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); object[] args = new object[2]; foreach(KeyValuePair<string, string> kvp in parameters) { using(AndroidJavaObject k = new AndroidJavaObject("java.lang.String", kvp.Key)) using(AndroidJavaObject v = new AndroidJavaObject("java.lang.String", kvp.Value)) { args[0] = k; args[1] = v; AndroidJNI.CallObjectMethod(obj_HashMap.GetRawObject(), method_Put, AndroidJNIHelper.CreateJNIArgArray(args)); } } cls_FlurryAgent.CallStatic("logEvent", eventId, obj_HashMap, timed); } #elif !UNITY_EDITOR && UNITY_IPHONE FlurryiOS_logEventWithParametersTimed(eventId, dictionaryToText(parameters)); #endif return this; }
  18. 18. and for more complex plugins…? • Do most of logic on native side to minimize native calls from Unity • Messages examples (Android) UnityPlayer.UnitySendMessage("gameObjectName", "methodName", "message"); • Use “Action<…>” delegates to pass callbacks on WP8, Unity supports Action with max 4 parameters as .NET 3.5 does • Structure plugin as a wrapper (mainly for third-party SDKs) • Hide user defined classes (e.g.: instantiate them with methods) so Unity can’t see them • Wrap API calls with ones that use just primitive or built-in data type and the convert them to user defined classes inside plugin! ROME 24 June 2014 – Valerio Riva 18
  19. 19. Run on UI Thread • iOS dispatch_async(dispatch_get_main_queue(), ^{ // Your code to run on the main queue/thread }); [[NSOperationQueue mainQueue] addOperationWithBlock:^{ // Your code to run on the main queue/thread }]; • Android UnityPlayer.currentActivity.runOnUiThread(new Runnable() { public void run() { //your code to run on the UI thread } }); ROME 24 June 2014 – Valerio Riva 19
  20. 20. Run on UI Thread (WP8) • MainPage.xaml.cs public partial class MainPage : PhoneApplicationPage { public void InvokeOnAppThread ( Action callback ) { UnityApp.BeginInvoke ( () => { callback (); } ); } public void InvokeOnUIThread ( Action callback ) { Dispatcher.BeginInvoke ( () => { callback (); } ); } private void Unity_Loaded() { … MyDispatcher.InvokeOnAppThread = InvokeOnAppThread; MyDispatcher.InvokeOnUIThread = InvokeOnUIThread; … } } • MyPlugin.cs MyDispatcher.InvokeOnAppThread(() => { //your Unity callbacks execution must be placed here }); MyDispatcher.InvokeOnUIThread(() => { //your code to run on UI Thread }); ROME 24 June 2014 – Valerio Riva 20
  21. 21. Resources and examples Resources • http://docs.unity3d.com/Manual/Plugins.html • http://docs.unity3d.com/Manual/wp8-plugins.html • http://docs.unity3d.com/Manual/PluginsForAndroid.html • http://docs.unity3d.com/Manual/PluginsForIOS.html Examples • https://github.com/playgameservices/play-games-plugin-for-unity • https://github.com/googleads/googleads-mobile- plugins/tree/master/unity • https://github.com/guillermocalvo/admob-unity-plugin • https://github.com/mikito/unity-admob-plugin • https://github.com/bearprada/flurry-unity-plugin • https://github.com/mikito/unity-flurry-plugin • https://github.com/faizann/unity3d_flurry ROME 24 June 2014 – Valerio Riva 21
  22. 22. Thank you! ROME 24 June 2014 – Valerio Riva 22 Question Time No animals were harmed in the making of this talk valerio.riva@gmail.com @ValerioRiva http://it.linkedin.com/in/valerioriva/

×