SlideShare a Scribd company logo
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
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
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
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
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
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
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
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
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
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);
}
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
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
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
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
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!
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;
}
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;
}
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
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
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
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
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/

More Related Content

PPTX
Avoid loss of hair while coding Unity3D plugins for mobile
PDF
Plugin For Unity
PDF
Unity3D Plugins Development Guide
PPTX
Introduction to development of multiplayer HTML5 games (with Socket.io)
PDF
Using the Android Native Development Kit (NDK)
ODP
Kivy - Python UI Library for Any Platform
PPTX
Android ndk
ODP
Developing apps with Kivy
Avoid loss of hair while coding Unity3D plugins for mobile
Plugin For Unity
Unity3D Plugins Development Guide
Introduction to development of multiplayer HTML5 games (with Socket.io)
Using the Android Native Development Kit (NDK)
Kivy - Python UI Library for Any Platform
Android ndk
Developing apps with Kivy

What's hot (20)

PDF
Exploring Your Apple M1 devices with Open Source Tools
PDF
Using the Android Native Development Kit (NDK)
PPTX
NDK Introduction
PDF
One bite and all your dreams will come true: Analyzing and Attacking Apple Ke...
PDF
Android Platform Debugging and Development
PDF
Android Platform Debugging and Development
PDF
Embedded Android Workshop with Marshmallow
PPTX
Kivy report
PDF
Android Platform Debugging and Development
PDF
Standalone Android Apps in Python
PDF
J2me Crash Course
PDF
Android Platform Debugging and Development
PDF
Embedded Android Workshop
PDF
Developing Android Platform Tools
PPTX
PDF
Exploring Thermal Related Stuff in iDevices using Open-Source Tool
PDF
Android Platform Debugging and Development
PDF
Embedded Android Workshop with Marshmallow
PPTX
Going native with less coupling: Dependency Injection in C++
Exploring Your Apple M1 devices with Open Source Tools
Using the Android Native Development Kit (NDK)
NDK Introduction
One bite and all your dreams will come true: Analyzing and Attacking Apple Ke...
Android Platform Debugging and Development
Android Platform Debugging and Development
Embedded Android Workshop with Marshmallow
Kivy report
Android Platform Debugging and Development
Standalone Android Apps in Python
J2me Crash Course
Android Platform Debugging and Development
Embedded Android Workshop
Developing Android Platform Tools
Exploring Thermal Related Stuff in iDevices using Open-Source Tool
Android Platform Debugging and Development
Embedded Android Workshop with Marshmallow
Going native with less coupling: Dependency Injection in C++
Ad

Similar to Avoid loss of hair while coding Unity3D plugin for mobile (20)

PDF
Python Flask app deployed to OPenShift using Wercker CI
PPTX
Cross platform mobile apps using .NET
PDF
NSC #2 - D2 03 - Nicolas Collignon - Google Apps Engine Security
PDF
Web automation with #d8rules (European Drupal Days 2015)
PPTX
PDF
Serial Killer - Silently Pwning your Java Endpoints // OWASP BeNeLux Day 2016
PPTX
Appium Overview - by Daniel Puterman
PPTX
EuroPython 2014 - How we switched our 800+ projects from Apache to uWSGI
ODP
RichFaces - Testing on Mobile Devices
PDF
Porting your favourite cmdline tool to Android
PPTX
Rapid Prototyping Chatter with a PHP/Hack Canvas App on Heroku
PPTX
Setting Apple's UI Automation Free with Appium
PPTX
Custom Buildpacks and Data Services
PDF
Create a-strong-two-factors-authentication-device-for-less-than-chf-100
PDF
Native Javascript apps with PhoneGap 11-04-2014 Codemotion Rome
PDF
Native Javascript apps with Phonegap - De Keijzer
PDF
Appenginejs (old presentation)
PDF
Flutter Forward EXTENDED - Flutter로 앱 개발 입문하기
PDF
Next Generation Memory Forensics
PPTX
APIs for Browser Automation (MoT Meetup 2024)
Python Flask app deployed to OPenShift using Wercker CI
Cross platform mobile apps using .NET
NSC #2 - D2 03 - Nicolas Collignon - Google Apps Engine Security
Web automation with #d8rules (European Drupal Days 2015)
Serial Killer - Silently Pwning your Java Endpoints // OWASP BeNeLux Day 2016
Appium Overview - by Daniel Puterman
EuroPython 2014 - How we switched our 800+ projects from Apache to uWSGI
RichFaces - Testing on Mobile Devices
Porting your favourite cmdline tool to Android
Rapid Prototyping Chatter with a PHP/Hack Canvas App on Heroku
Setting Apple's UI Automation Free with Appium
Custom Buildpacks and Data Services
Create a-strong-two-factors-authentication-device-for-less-than-chf-100
Native Javascript apps with PhoneGap 11-04-2014 Codemotion Rome
Native Javascript apps with Phonegap - De Keijzer
Appenginejs (old presentation)
Flutter Forward EXTENDED - Flutter로 앱 개발 입문하기
Next Generation Memory Forensics
APIs for Browser Automation (MoT Meetup 2024)
Ad

Recently uploaded (20)

PDF
Empathic Computing: Creating Shared Understanding
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PDF
Unlocking AI with Model Context Protocol (MCP)
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PPTX
sap open course for s4hana steps from ECC to s4
PDF
Network Security Unit 5.pdf for BCA BBA.
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
PPTX
20250228 LYD VKU AI Blended-Learning.pptx
PDF
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
PDF
Encapsulation theory and applications.pdf
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PDF
Review of recent advances in non-invasive hemoglobin estimation
PPTX
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
Chapter 3 Spatial Domain Image Processing.pdf
PDF
Approach and Philosophy of On baking technology
PDF
Machine learning based COVID-19 study performance prediction
Empathic Computing: Creating Shared Understanding
The Rise and Fall of 3GPP – Time for a Sabbatical?
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
Unlocking AI with Model Context Protocol (MCP)
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
sap open course for s4hana steps from ECC to s4
Network Security Unit 5.pdf for BCA BBA.
Per capita expenditure prediction using model stacking based on satellite ima...
20250228 LYD VKU AI Blended-Learning.pptx
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
Encapsulation theory and applications.pdf
Mobile App Security Testing_ A Comprehensive Guide.pdf
Review of recent advances in non-invasive hemoglobin estimation
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
Dropbox Q2 2025 Financial Results & Investor Presentation
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
Diabetes mellitus diagnosis method based random forest with bat algorithm
Chapter 3 Spatial Domain Image Processing.pdf
Approach and Philosophy of On baking technology
Machine learning based COVID-19 study performance prediction

Avoid loss of hair while coding Unity3D plugin for mobile

  • 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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/