Overview
A Native Android SDK does not exist for Unity Analytics, however the Unity Analytics REST API can be used to send Analytics events from your native game client to Unity Analytics. This will require you implement your own REST API wrapper in your game client, taking into account offline caching, back-off & retry and session management etc..
Alternatively, the source code for the existing deltaDNA Android SDK is publically available on the deltaDNA GitHub repository if you would prefer to download or fork, modify and maintain it yourself. It can be patched to send event data to Unity Analytics instead of the deltaDNA endpoint.
Regardless of whether you intend to implement UGS Services or modify the deltaDNA SDK to work with them, you will need to follow the DELTADNA TO UNITY DASHBOARD MAPPING FLOW before 3rd March 2024 and pay attention to the player’s userID, to ensure you have a contiguous data record for each player that migrates from deltaDNA to Unity Analytics.
Analytics REST API Migration
The native Android SDK can be replaced with the Unity Analytics REST API, but there are a few things you will need to take into consideration when implementing this approach:
-
- Privacy : You will need to implement player consent and data privacy checks to comply with the store and territory legislation relevant for your game.
- userID consistency : To ensure a contiguous data record for the player as they move to Unity Analytics, you will need to use the same userID that the native Android SDK previously used. You can retrieve the userID from the SDK by reading the userID property from it
1DDNA.instance().getUserId() - Session management : The deltaDNA Android SDK generates a new sessionID parameter that it uses on each event for the duration of the current gameplay session. A new sessionID is generated on each App launch or when it returns from being backgrounded for more than 5 minutes. The sessionID is just a string that is unique to that user session, you will need to generate this yourself.
- eventUUID : Each event can contain an optional eventUUID parameter, if this parameter is present the Unity Analytics ingestion pipeline will use it to de-duplicate events. It is just a string that is unique to this user event. It is particularly important if you are using event caching and retries.
- eventTimestamp : Each event should contain an eventTimestamp parameter representing the UTC timestamp when the player performed a particular action. If this is omitted a timestamp will be applied on the server when the event is received, causing batched or cached events to look incorrect in the dataset.
- Offline event caching : The Unity Analytics REST API will respond with a 204 status code if your event POST is received successfully. If you don’t get this response you should consider caching the event(s) and retrying later when connectivity is restored. Be sure to use the eventUUID to avoid duplicate events from retries on bad connections.
- Back off and retry and Timeouts : If you implement event caching you should also implement your own backoff and retry mechanism.
- Standard event and parameter consistency : The Android SDK sends several standard events that are used to populate the Analytics dashboards, you will be required to record these, populating all the relevant parameters correctly, yourself. You can view the event definitions in the Event Manager tool in the deltaDNA dashboard, or in the same tool in the Unity Dashboard after successfully mapping your deltaDNA game to Unity Analytics. Take a look at the Android SDK source code to see how each of the parameter values is populated, searching the repository for the specific event name or parameter name will help you find them.
Additional documentation
- deltaDNA Android SDK GitHUb Repository
- deltaDNA REST API
- deltaDNA Event Management
- deltaDNA Event Browser tool
- Unity Analytics REST API
- Unity Analytics Event Management
- Unity Analytics Event Browser tool
- deltaDNA to UGS project Mapping Guide
- deltaDNA to UGS Analytics SDK Migration Guide
- deltaDNA Migration Guide : Rest API
- Unity Remote Config Documentation
- Unity Remote Config client REST API
- Unity Game Overrides
We recommend using a network proxy tool like Fiddler or Charles to check event posts during development and the Event Browser dashboard tool to check that they are passing event validation.
Remote Config and Game Override Migration
deltaDNA Engage Decision Point campaigns can be replicated to a large extent using UGS Remote Config and Game Overrides
- deltaDNA Decision Point campaigns make a request at time of your choosing and respond with a JSON payload containing parameter keys and values. The content response can be targeted to individual players based on Analytics segmentation and values sent in the request at runtime.
- UGS Remote Config makes a request at game launch by default, but can be refreshed later at a time of your choosing. It retrieves all predefined keys and values for the game
- UGS Game Overrides change the response of a Remote Config requestt at game launch by default, but can be refreshed later at a time of your choosing. It retrieves all predefined keys including any that have been modified based on Analytics Audience or JEXL runtime parameter evaluation criteria.
There are some differences that should be noted though.
deltaDNA Decision Point Campaigns |
Remote Config / Game Overrides |
Analytics Audience Segmentation AND Runtime request parameters can be used to target the response to the player | EITHER Analytics Audience OR Runtime Request parameters can be used to target the response to the player. This may be a sufficient difference to prevent the migration of some DDNA decision point campaign without reworking their player selection criteria. |
Only the parameter keys and values for a specific decision point are returned with each request. | All parameter keys and values for the game are returned with each request. |
Decision Point requests are made whenever you make them in your game client. It is not uncommon for a game to make multiple decision point requests at different points in the game. | Remote Config requests are typically only made when the game is launched, but they can be made at other points in the game if you explicitly make them. |
Decision Point campaign endpoint, parameters and response format are different to Remote Config |
It should also be noted that Event Triggered Campaigns and Image Message Popups rely on significant client side code and JSON response payloads, it would be a major undertaking to try and replicate their functionality with UGS.
deltaDNA Engage Decision Point Request and Response example.
Request Endpoint
https://engage16056nwdmf.deltadna.net
Request Body
1 2 3 4 5 6 7 8 9 10 |
{ "locale": "zz_ZZ", "decisionPoint": "levelData", "userID": "Laurie", "platform": "IOS", "version": "4", "parameters": { "build": 3777 } } |
Response Body
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
{ "transactionID": 3208651093546463000, "eventParams": { "platform": "IOS", "responseTransactionID": 3208651093546463000, "responseDecisionpointName": "levelData", "responseEngagementID": 332911, "responseEngagementName": "Get Level Data", "responseEngagementType": "TARGETING", "responseVariantName": "Default", "responseMessageSequence": 0 }, "parameters": { "adFrequency": 200, "adCooldown": 120, "ddnaIsPersistent": false } } |
UGS Remote Config / Game Override Request and Response example.
Request Endpoint
https://config.unity3d.com/api/v1/settings
Request Body
1 2 3 4 5 6 7 8 9 10 11 12 13 |
{ "projectId": "25088b95-5df2-4352-9d7b-e72d48d9e5fe", "userId": "2A1A81A5-F8A1-4328-A54F-E02AD6DA980B", "attributes": { "unity": {}, "app": {}, "user": { "build": 3777, "decisionPoint": "levelData" } }, "environmentId": "1875d8f6-5c64-42e0-9e18-be437a0a126d" } |
Response Body
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
{ "configs": { "settings": { "adCooldown": 120, "startingBalance": 100, "ExtraHint": "0", "goldCoin": 150, "adFrequency": 200 } }, "metadata": { "configAssignmentHash": "6f6e6e692930f253f048785fa2b0320f3fb2856c", "assignmentId": "7a40474b-c0d6-4745-8749-d8febebbac09", "environmentId": "1875d8f6-5c64-42e0-9e18-be437a0a126d" } } |
deltaDNA SDK Modification
The existing deltaDNA Android SDK is simply a wrapper for the deltaDNA Analytics REST API.
The specifications for the deltaDNA and Unity Analytics REST APIs are very similar. The format of the JSON analytics events is identical, it is just the REST API endpoint that differs.
The following details show how to modify deltaDNA SDK v5.0.2 for Android to make it send events to the Unity Analytics REST API endpoint instead of deltaDNA. These modifications may save you the effort of implementing your own REST API wrapper from scratch.
A version of Android SDK v5.0.2 that has already been modified can also be downloaded from here
android-sdk-5.0.2 – Modified for UGS Analytics and Remote Config.zip
SDK Integration
The deltaDNA Android SDK can be downloaded as a .zip file from the releases page of the deltaDNA repository on GitHub, you will find source code in the .zip file along with some SDK examples. There have been numerous versions of the SDK over the years and a few different ways of integrating it (Gradle, Mavern etc..). This guide has been written using v5.0.2 of the deltaDNA Android SDK, there may be some differences with earlier versions, but the theory is the same. In order to patch the SDK you are going to need to modify a few source files, so we recommend downloading the .zip file in order to get access to the SDK source. Once modified you can then replace the SDK in your game.
- Download the deltaDNA Android SDK v5.0.2 zip file from the Releases page of the deltaDNA GitHub repository and extract it.
- Launch Android Studio then Open the android-sdk-5.0.2 folder
Note – this guide was written with Android Studio Hedgehog 2023.1.1 patch 1
A Gradle error will likely be received at this point. Clicking on the Upgrade Gradle wrapper to 7.2 .. link should resolve this and re-import the project.
Selecting the example-demo project and running it on connected device should now result in a successful build that sends Analytics events to deltaDNA.
SDK Modifications
- Change the version number in the sdk gradle.properties file so we can identify analytics events coming from the modified SDK, then resync the project
VERSION_NAME=5.0.2 (UGS) - We need to provide a couple of additional parameters to Unity Analytics when the SDK is started. These parameters need to be added in a few places, then used by the REST API in the URL endpoints for Unity Analytics and Remote Config to ensure that data is sent to the correct project environment.
Open the sdk\DDNA.java file and replace the Configuration Class with the following code. It adds projectId and environmentName declarations then sets them in the constructor. A couple of precondition checks are also added.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556public static final class Configuration {protected final Application application;final String projectId;final String environmentName;final String environmentKey;final String collectUrl;final String engageUrl;@NullableString hashSecret;@NullableString clientVersion;@NullableString userId;@NullableString platform;protected final Settings settings;public Configuration(Application application,String projectId,String environmentName,String environmentKey,String collectUrl,String engageUrl) {Preconditions.checkArg(application != null,"application cannot be null");Preconditions.checkArg(!TextUtils.isEmpty(projectId),"projectId cannot be null or empty");Preconditions.checkArg(!TextUtils.isEmpty(environmentName),"environmentName cannot be null or empty");Preconditions.checkArg(!TextUtils.isEmpty(environmentKey),"environmentKey cannot be null or empty");Preconditions.checkArg(!TextUtils.isEmpty(collectUrl),"collectUrl cannot be null or empty");Preconditions.checkArg(!TextUtils.isEmpty(engageUrl),"engageUrl cannot be null or empty");this.application = application;this.projectId = projectId;this.environmentName = environmentName;this.environmentKey = environmentKey;this.collectUrl = fixUrl(collectUrl);this.engageUrl = fixUrl(engageUrl);this.settings = new Settings();} - The new
projectId and
environmentName also need to be added to the
DDNAImpl &
DDNANonTracking delegates in the DDNA.java file (Around Line 94 in SDK v5.0.2)
1234567891011121314151617181920212223242526new DDNAImpl(configuration.application,configuration.projectId,configuration.environmentName,configuration.environmentKey,configuration.collectUrl,configuration.engageUrl,configuration.settings,configuration.hashSecret,configuration.clientVersion,configuration.userId,configuration.platform,eventListeners,iEventListeners),new DDNANonTracking(configuration.application,configuration.projectId,configuration.environmentName,configuration.environmentKey,configuration.collectUrl,configuration.engageUrl,configuration.settings,configuration.hashSecret,configuration.platform,eventListeners,iEventListeners)); - Next the
DDNA Constructor can be updated with the two new parameters, in DDNA.java file (around Line 161 in SDKv5.0.2)
1234567891011DDNA( Application application,String projectId,String environmentName,String environmentKey,String collectUrl,String engageUrl,Settings settings,@Nullable String hashSecret,@Nullable String platform,Set eventListeners,Set iEventListeners) { - Then the delegates can be updated. First the
DDNADelegate.java file (Around line 46 in SDK v5.0.2)
123456789101112131415161718DDNADelegate(Configuration configuration,Set eventListeners,Set iEventListeners,DDNA tracking,DDNA nonTracking) {super( configuration.application,configuration.projectId,configuration.environmentName,configuration.environmentKey,configuration.collectUrl,configuration.engageUrl,configuration.settings,configuration.hashSecret,configuration.platform,eventListeners,iEventListeners); - Then the
DDNAImpl.java delegate (Around Line 486 in SDK v5.0.2)
1234567891011121314151617181920212223242526DDNAImpl(Application application,String projectId,String environmentName,String environmentKey,String collectUrl,String engageUrl,Settings settings,@Nullable String hashSecret,@Nullable String clientVersion,@Nullable String userId,@Nullable String platform,Set eventListeners,Set iEventListeners) {super( application,projectId,environmentName,environmentKey,collectUrl,engageUrl,settings,hashSecret,platform,eventListeners,iEventListeners); - Finally the
DDNANonTracking.java delegate (Around Line 51 in SDK v5.0.2)
123456789101112131415161718192021222324DDNANonTracking(Application application,String projectId,String environmentName,String environmentKey,String collectUrl,String engageUrl,Settings settings,@Nullable String hashSecret,@Nullable String platform,Set eventListeners,Set iEventListeners) {super( application,projectId,environmentName,environmentKey,collectUrl,engageUrl,settings,hashSecret,platform,eventListeners,iEventListeners); - Now the
NetworkManager constructor can now be updated to use the two new parameters. First update the
NetworkManager constructor in the
sdk\net\NetworkManager.java file (Around Line 56 in SDK v5.0.2). The two new parameters are added to the constructor as inputs, then the
collectUrl and
engageUrl are updated accordingly
1234567891011121314151617181920212223242526public NetworkManager(String projectId,String envName,String envKey,String collectUrl,String engageUrl,Settings settings,@Nullable String hash) {this.collectUrl = collectUrl + '/' + projectId + "/environments/" + envName;this.engageUrl = engageUrl ;this.settings = settings;this.hash = hash;MessageDigest md = null;if (hash != null && !hash.isEmpty()) {try {md = MessageDigest.getInstance("MD5");} catch (NoSuchAlgorithmException e) {Log.w(TAG, "Events will not be hashed", e);}}md5 = md;dispatcher = new NetworkDispatcher();} - Then the call to create a
NetworkManager in the
sdk\DDNA.java file can be updated to use the new parameters. (Around Line 179 in SDK v5.0.2)
123456789101112network = new NetworkManager(projectId,environmentName,environmentKey,collectUrl,engageUrl,settings,hashSecret);consentTracker = new ConsentTracker(application.getSharedPreferences(DDNA_SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE),new GeoIpNetworkClient(network)); - You can now modify your game code to populate the UGS
projectId and
environmentName parameters when the SDK is started. In the example-demo this is located in the
ExampleApplication.java file. Note, the
EnviornmentKey and API
URls have also been modified to point to the Unity project, rather than deltaDNA.The URL endpoint for the Unity Analytics REST API is
https://collect.analytics.unity3d.com/api/analytics/collect/v1/projectsThe URL endpoint for the Unity Remote Config is
<span class="lang:default decode:true crayon-inline">https://config.unity3d.com/api/v1/settingsThe projectId for your project can be found on the Unity Cloud > Project Settings page for your project.
The environmentKey and environmentName for each environment on your project can be found on the Unity Cloud > Environments page
12345678DDNA.initialise(new DDNA.Configuration(this,"25088b95-5df2-4352-9d7b-e72d48d9e5fe","production","1875d8f6-5c64-42e0-9e18-be437a0a126d","https://collect.analytics.unity3d.com/api/analytics/collect/v1/projects","https://config.unity3d.com/api/v1/settings").clientVersion(BuildConfig.VERSION_NAME)); - Now we can modify the bodies of the Engage Decision Point campaign request and responses to match the formatting expected by Unity Remote Config.
First change the way the request payload is constructed by completely replacing the section of code, inside the try block, that builds the event JSONObject in the EventHandler.java file (Around Line 168 in SDK v5.0.2).
1234567891011121314151617// This is all new, the remote config request body is different from DDNA EngageJSONObject userParams ;if (!engagement.params.isEmpty()) {userParams = engagement.params.json;}else {userParams = new JSONObject();}userParams.put("decisionPoint", engagement.name);event = new JSONObject().put("userId", userId).put("attributes", new JSONObject().put("unity",new JSONObject()).put("app",new JSONObject()).put("user", userParams)); - Now we can make some changes to the
NetworkManager.java file to inject the new
projectId and
environmentId into the request. Four small changes are required to achieve this.Add the JSONException library to the top of the
NetworkManager class if your IDE hasn’t added it automatically already.
1import org.json.JSONException;
12private final String projectId;private final String environmentId;
12this.environmentId = envKey;this.projectId = projectId;
123456789101112// Inject projectId and environmentId into payload for Remote Configtry {if (projectId != null) {payload.put("projectId", projectId);}if (environmentId != null) {payload.put("environmentId", environmentId);}}catch (JSONException e) {// should never happen due to params enforcementthrow new IllegalArgumentException(e);} - Finally, you may need to modify the Engage Decision Point request in the test app to send a request with the decisionPoint and parameters that match something that you have configured in Remote Config.
e.g. in the DemoApplication ExampleActivity.java file (Around Line 173 in SDK v5.0.2)
123456public void onEngage(View view) {DDNA.instance().requestEngagement(new Engagement<>("levelData").putParam("build", 3670),new EngageListenerExample());}
Testing
Use a network proxy like Charles or Fiddler to check that your analytics events are sending as expected and that you are getting a 204 response indicating that they have been received.
You should also check the Unity Analytics > Event Browser tool to confirm that they are arriving and validating correctly.
deltaDNA Engage Decision Point Campaigns can be tested with the deltaDNA > Setup > Engage Trace Tool. It lets you see the Engage request and response for a given decision point campaign.
A network traffic proxy like Charles or Fiddler should be used to check the requests and responses from UGS Remote Config / Game Overrides.
Game Override campaign setup.
The sdk modifications above rely on the decisionPoint campaign name (decisionPoint) being sent to the Remote Config API as a user parameter that can then be evaluated at runtime as JEXL audience targeting criteria.
This does impose a limitation, that Analytics audience selection criteria cannot be used at the same time. But additional parameters can be put into the JEXL targeting criteria to mitigate this.