Skip to main content

Event Tracking

GameAnalytics features the following event types:

EventDescription
AdAds shown and clicked, fill rate.
BusinessIn-App Purchases supporting receipt validation on GA servers.
DesignSubmit custom event id’s. Useful for tracking metrics specifically needed for your game.
ErrorSubmit exception stack traces or custom error messages.
HealthAutomatically submits health metrics related to your game such as FPS.
ImpressionImpression data from different ad networks
ProgressionLevel attempts with Start, Fail & Complete event.
ResourceManaging the flow of virtual currencies - like gems or lives
tip

Read more about events here

Business Events

Business events are used to track (and validate) real-money transactions.

info

Some configuration is needed before receipt validation will be active. Read information about validation and requirements for different platforms here.

Business Event with receipt

When submitting a Business Event supply the following from the IAP procedure.

  • receipt (INAPP_PURCHASE_DATA)
  • signature (INAPP_DATA_SIGNATURE)
tip

Read more about retrieving these needed fields at the Google documentation here.

Add a business event when an in-app purchase is completed.

GameAnalytics.addBusinessEventWithCurrency("USD", 1000, "item", "id", "cart", "[receipt]", "google_play", "[signature]");
FieldTypeDescriptionExample
currencystringCurrency code in ISO 4217 formatUSD
amountintegerAmount in cents99 is $0.99
itemTypestringThe type/category of the itemGoldPacks
itemIdstringSpecific item bought1000GoldPack
cartTypestringThe game location of the purchase. Max 10 unique valuesEndOfLevel
receiptstringINAPP_PURCHASE_DATA. Null allowed. Read more about Android receipt here
signaturebase64 stringINAPP_DATA_SIGNATURE. Null allowed. Read more about Android signature here.
caution

If the receipt/signature is null (or is an invalid receipt) then it will be submitted to the GA server but will register as not validated.

Receipt JSON Schema

{
"orderId": "",
"packageName": "",
"productId": "",
"purchaseTime": 1484080095335,
"purchaseState": 0,
"purchaseToken": ""
}

Example: Correct receipt information

It is possible to use code frameworks that handle IAP flow and of course every is done a bit differently This code example uses the sample code included in Android SDK found under this directory: android_sdk_folder/extras/google/play_billing/samples/TrivialDrive/src/com/example/android/trivialdrivesample/util, it assumes you have downloaded Google Play Billing.

First of all the SkuDetails class has been modified slightly in this example to expose all information needed:

public class SkuDetails {
String mItemType;
String mSku;
String mType;
String mPrice;
String mTitle;
String mDescription;
String mJson;
String mCurrencyCode;
int mPriceInCents;

public SkuDetails(String jsonSkuDetails) throws JSONException {
this(IabHelper.ITEM_TYPE_INAPP, jsonSkuDetails);
}

public SkuDetails(String itemType, String jsonSkuDetails) throws JSONException {
mItemType = itemType;
mJson = jsonSkuDetails;
JSONObject o = new JSONObject(mJson);
mSku = o.optString("productId");
mType = o.optString("type");
mPrice = o.optString("price");
mTitle = o.optString("title");
mDescription = o.optString("description");
mCurrencyCode = o.optString("price_currency_code");
mPriceInCents = o.optInt("price_amount_micros") / 10000;
}

public String getSku() { return mSku; }
public String getType() { return mType; }
public String getPrice() { return mPrice; }
public String getTitle() { return mTitle; }
public String getDescription() { return mDescription; }
public String getCurrencyCode() { return mCurrencyCode; }
public int getPriceInCents() { return mPriceInCents; }

@Override
public String toString() {
return "SkuDetails:" + mJson;
}
}

public void onIabPurchaseFinished(IabResult result, Purchase purchase)
{
// Code before business event...

SkuDetails skuDetails = currentInventory.getSkuDetails(purchase.getSku());
GameAnalytics.addBusinessEventWithCurrency(skuDetails.getCurrencyCode(), skuDetails.getPriceInCents(), "", purchase.getSku(), "", purchase.getOriginalJson(), "google_play", purchase.getSignature());

// Code after business event...
}

// Current inventory is set after querying inventory
public void onQueryInventoryFinished(IabResult result, Inventory inventory)
{
if (result.isFailure())
{
// Handle failure
}
else
{
// Other code...
currentInventory = inventory;
}
}

The above example assumes you have setup onIabPurchaseFinished and onQueryInventoryFinished to called on the correct events.

Price and the currency format (ISO 4217)

The amount is an integer with the price cent value. Also the currency has to conform to the ISO 4217 format. Make sure the price is in cents and that the currency strings are returned as required.

Custom Event Fields

It is possible to use a set of key-value pairs to add extra fields but it will only be available through raw data export. Here is an example of how to use it:

HashMap<String, Object> fields = new HashMap<>();
fields.put("test", 100);
fields.put("test_2", "hello_world");

GameAnalytics.addBusinessEventWithCurrency("USD", 1000, "item", "id", "cart", "[receipt]", "google_play", "[signature]", fields);
tip

For more information on custom event fields and raw data export go here.

tip

For more information on Business Events go here.

Ad Events

The GameAnalytics ad event needs to be called when certain events happen for the implemented ad sdk’s. An ad sdk has callback methods activating code when certain things are activated (like ad show or ad clicked).

To use the ad event it is need to call the GameAnalytics SDK when these delegates are called.

The examples below describe how to implement this for the following ad-types:

  • rewarded video
  • interstitial
  • bannner

All code examples use AdMob SDK to showcase example usage. Other ad networks might differ in naming/structure, but the overall process should be applicable to all.

The ad SDK name argument for the ad event needs to be all lower-case with no spaces or underscore used. Here some examples of valid values to use for ad SDK names:

  • admob
  • unityads
  • ironsource
  • applovin

Rewarded Video

Showing a rewarded video

It is possible for an ad to fail to show, either because there's no internet, or the provider has failed to find a suitable ad for you. You can track such errors by sending an AdEvent.:

if (rewardedVideoAd != null && rewardedVideoAd.isLoaded()) {
rewardedVideoAd.show();
}
else
{
GameAnalytics.addAdEvent(GAAdAction.FailedShow, GAAdType.RewardedVideo, "admob", "[AD_PLACEMENT_OR_UNIT_ID]");
}

If you want to track errors then we recommend defining this method to map the error message stored earlier in the latestRewardedVideoError variable:

// TRACKING ERRORS
private GAAdError getLatestAdError(int error)
{
GAAdError result = GAAdError.Unknown;

// implementation

return result;
}

Track time spent

If you want to track how much the user watched then start timer and keep track of current rewarded video ad when onRewardedVideoAdOpened() listener method is called:

@Override
public void onRewardedVideoAdOpened() {
// keep track of current rewarded video ad
currentRewardedVideoPlacement = "[AD_PLACEMENT_OR_UNIT_ID]";
// start timer for this ad identifier
GameAnalytics.startTimer(currentRewardedVideoPlacement);
}

// when application goes to background (during the display of a rewarded video ad) then the timer needs to stop.
// therefore we need to call code in onPause() and onResume().
@Override
public void onPause() {
if(currentRewardedVideoPlacement != null)
{
GameAnalytics.pauseTimer(currentRewardedVideoPlacement);
}
}

@Override
public void onResume() {
if(currentRewardedVideoPlacement != null)
{
GameAnalytics.resumeTimer(currentRewardedVideoPlacement);
}
}

Tracking ad rewards

If you want to track rewards from rewarded videos, then call an ad event in the method where rewards are registered. For example, in admob this method is called onRewarded(RewardItem reward) and the following example is a method for how you could handle this:

@Override
public void onRewarded(RewardItem reward) {
// send ad event - reward recieved
GameAnalytics.addAdEvent(GAAdAction.RewardReceived, GAAdType.RewardedVideo, "admob", "[AD_PLACEMENT_OR_UNIT_ID]");
}

Tracking successful ads

Finally we can track that the rewarded ad has been shown succesfully. For admob this method is called onRewardedVideoAdClosed() and the following example is a method for how you could handle this.

@Override
public void onRewardedVideoAdClosed() {
if(currentRewardedVideoPlacement != null)
{
long elapsedTime = GameAnalytics.stopTimer(currentRewardedVideoPlacement);
// send ad event for tracking elapsedTime
GameAnalytics.addAdEvent(GAAdAction.Show, GAAdType.RewardedVideo, "admob", "[AD_PLACEMENT_OR_UNIT_ID]", elapsedTime);
currentRewardedVideoPlacement = null;

// OR if you do not wish to track time

// send ad event without tracking elapsedTime
GameAnalytics.addAdEvent(GAAdAction.Show, GAAdType.RewardedVideo, "admob", "[AD_PLACEMENT_OR_UNIT_ID]");
}
}

If onRewardedVideoCompleted() is called send the following ad event:

@Override
public void onRewardedVideoCompleted() {
if(that.get().currentRewardedVideoPlacement != null)
{
long elapsedTime = GameAnalytics.stopTimer(AD_UNIT_ID);
// send ad event for tracking elapsedTime
GameAnalytics.addAdEvent(GAAdAction.Show, GAAdType.RewardedVideo, "admob", "[AD_PLACEMENT_OR_UNIT_ID]", elapsedTime);
that.get().currentRewardedVideoPlacement = null;

// OR if you do not wish to track time

// send ad event without tracking elapsedTime
GameAnalytics.addAdEvent(GAAdAction.Show, GAAdType.RewardedVideo, "admob", "[AD_PLACEMENT_OR_UNIT_ID]");
}
}

Interstitial

Showing an interstitial ad

As for rewarded videos, it is advised to track if the ad has failed to be served. Please take a look at the following example:

if (interstitialAd != null && interstitialAd.isLoaded()) {
interstitialAd.show();
GameAnalytics.addAdEvent(GAAdAction.Show, GAAdType.Interstitial,"admob", "[AD_PLACEMENT_OR_UNIT_ID]");
}
else
{
GameAnalytics.addAdEvent(GAAdAction.FailedShow, GAAdType.Interstitial, "admob", "[AD_PLACEMENT_OR_UNIT_ID]");
}

Track user clicks

You can track if the user has clicked the ad and has been redirected to the product's website. This is done by sending an GAAdAction.Clicked event:

@Override
public void onAdLeftApplication() {
// send ad event
GameAnalytics.addAdEvent(GAAdAction.Clicked, GAAdType.Interstitial,"admob", "[AD_PLACEMENT_OR_UNIT_ID]");
}

Showing a banner

In similiar fashion to rewarded video ads & interstitial ads, you can track if the ad has been shown succesfully or not. The example below will show how you could handle it:

if (bannerAd != null && bannerAd.isLoaded()) {
bannerAd.show();
GameAnalytics.addAdEvent(GAAdAction.Show, GAAdType.Banner,"admob", "[AD_PLACEMENT_OR_UNIT_ID]");
}
else
{
GameAnalytics.addAdEvent(GAAdAction.FailedShow, GAAdType.Banner, "admob", "[AD_PLACEMENT_OR_UNIT_ID]");
}

Track banner clicks

You can track if an user has clicked a banner ad by sending a GAAdAction.Clicked. Please check out the example below:

@Override
public void onAdOpened() {
// send ad event
GameAnalytics.addAdEvent(GAAdAction.Clicked, GAAdType.Banner, "admob", "[AD_PLACEMENT_OR_UNIT_ID]");
}

Custom Event Fields for banners

It is possible to use a set of key-value pairs to add extra fields but it will only be available through raw data export. Here is an example of how to use it:

HashMap<String, Object> fields = new HashMap<>();
fields.put("test", 100);
fields.put("test_2", "hello_world");

GameAnalytics.addAdEvent(GAAdAction.Show, GAAdType.Interstitial,"admob", "[AD_PLACEMENT_OR_UNIT_ID]", fields);

Ad Event Info

In the table below you can find all the values supported for ad events.

| Field | Type | Description | Example | | ----------- | ------ | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------- | | adAction | enum | A defined enum for ad action (for example clicked). | GAAdAction.Clicked
GAAdAction.Show
GAAdAction.FailedShow
GAAdAction.RewardReceived | | adType | enum | A defined enum for ad type (for example interstitial). | GAAdType.Video
GAAdType.RewardedVideo
GAAdType.Playable
GAAdType.Interstitial
GAAdType.OfferWall
GAAdType.Banner | | adSdkName | string | Name of the Ad/Ad mediation SDK. | admob | | adPlacement | string | Identifier of ad in the game or the placement of it. | level_complete_ad | | duration | int | Optional. Only used for video ads to track how long the user watched the video for. | 10 | | noAdReason | enum | Optional. | Used to track the reason for not being able to show an ad when needed (for example no fill). |
GAAdError.Unknown
GAAdError.Offline
GAAdError.NoFill
GAAdError.InternalError
|

tip

For more information on Ad Events go here.

Impression Events

Impression events are used to get impression data from different ad networks. Currently the following ad networks are supported:

  • Fyber
  • IronSource
  • TopOn
  • MAX
  • Aequus
  • AdMob

Fyber

To use impression data from Fyber add the following code inside the onCreate function of the first activity of your game:

Banner.setBannerListener(new BannerListener() {
@Override
public void onShow(String placementId, ImpressionData impressionData) {
JSONObject impressionDataJson = new JSONObject();
try
{
impressionDataJson.put("advertiserDomain", impressionData.getAdvertiserDomain());
impressionDataJson.put("campaignId", impressionData.getCampaignId());
impressionDataJson.put("creativeId", impressionData.getCreativeId());
impressionDataJson.put("countryCode", impressionData.getCountryCode());
impressionDataJson.put("currency", impressionData.getCurrency());
impressionDataJson.put("impressionDepth", impressionData.getImpressionDepth());
impressionDataJson.put("demandSource", impressionData.getDemandSource());
impressionDataJson.put("impressionId", impressionData.getImpressionId());
impressionDataJson.put("networkInstanceId", impressionData.getNetworkInstanceId());
impressionDataJson.put("priceAccuracy", impressionData.getPriceAccuracy().ordinal());
impressionDataJson.put("placementType", impressionData.getPlacementType().ordinal());
impressionDataJson.put("renderingSDK", impressionData.getRenderingSdk());
impressionDataJson.put("renderingSDKVersion", impressionData.getRenderingSdkVersion());
impressionDataJson.put("netPayout", impressionData.getNetPayout());
}
catch (JSONException e)
{
e.printStackTrace();
}

GameAnalytics.addImpressionEvent("fyber", impressionData.toString());
}

// rest part of BannerListener...
});

Interstitial.setInterstitialListener(new InterstitialListener() {
@Override
public void onShow(String placementId, ImpressionData impressionData) {
// Use same code as in 'BannerListener'
}

// rest part of InterstitialListener...
});

Rewarded.setRewardedListener(new RewardedListener() {
@Override
public void onShow(String placementId, ImpressionData impressionData) {
// Use same code as in 'BannerListener'
}

// rest part of RewardedListener...
});

IronSource

To use impression data from IronSource add the following code inside the onCreate function of the first activity of your game:

protected void onCreate(Bundle savedInstanceState)
{
//...other code
IronSource.setImpressionDataListener(new ImpressionDataListener()
{
@Override
public void onImpressionSuccess(ImpressionData impressionData)
{
GameAnalytics.addImpressionIronSourceEvent(IronSourceUtils.getSDKVersion(), impressionData.getAllData());
}
});
}

TopOn

To use impression data from TopOn add the following code inside the onCreate function of the first activity of your game:

protected void onCreate(Bundle savedInstanceState)
{
//...other code
topOnInterstitial = new ATInterstitial(this, TOPON_INTERSTITIAL_PLACEMENT_ID);
topOnInterstitial.setAdListener(new ATInterstitialListener()
{
// ...rest of listener implementation
@Override
public void onInterstitialAdShow(ATAdInfo atAdInfo)
{
GameAnalytics.addImpressionTopOnEvent( ATSDK.getSDKVersionName().replace("UA_", ""), atAdInfo.toString());
}
});
}

MAX

To use impression data from MAX add the following code inside the onCreate function of the first activity of your game:

protected void onCreate(Bundle savedInstanceState)
{
//...other code
maxInterstitial = new MaxInterstitialAd(MAX_AD_UNIT_ID, this);
maxInterstitial.setAdListener(new MaxAdListener()
{
// ...rest of listener implementation
@Override
public void onAdDisplayed(MaxAd ad)
{
JSONObject json = new JSONObject();
try
{
json.put("country", AppLovinSdk.getInstance(referenceToActivity).getConfiguration().getCountryCode());
json.put("network_name", ad.getNetworkName());
json.put("adunit_id", ad.getAdUnitId());
json.put("adunit_format", ad.getFormat().getLabel());
json.put("placement", ad.getPlacement());
json.put("creative_id", ad.getCreativeId());
json.put("revenue", ad.getRevenue());
}
catch(JSONException e)
{
}
GameAnalytics.addImpressionMaxEvent( AppLovinSdk.VERSION, json);
}
});
}

Aequus

To use impression data from Aequus add the following code inside the onCreate function of the first activity of your game:

protected void onCreate(Bundle savedInstanceState)
{
//...other code
Aequus.setILRDListener(new AequusILRDListener()
{
// ...rest of listener implementation
@Override
public void onAequusILRDImpressionShow(ImpressionData impressionData)
{
GameAnalytics.addImpressionAequusEvent( mobi.aequus.sdk.BuildConfig.SDK_VERSION_NAME, impressionData.getJsonRepresentation());
}
});
}

AdMob

To use impression data from AdMob add the following code inside the onCreate function of the first activity of your game:

protected void onCreate(Bundle savedInstanceState)
{
//...other code
interstitialAd.setOnPaidEventListener(new OnPaidEventListener()
{
@Override
public void onPaidEvent(AdValue adValue)
{
JSONObject impressionDataJson = new JSONObject();
try
{
impressionDataJson.put("adunit_id", interstitialAd.getAdUnitId());
impressionDataJson.put("currency", adValue.getCurrencyCode());
impressionDataJson.put("precision", adValue.getPrecisionType());
impressionDataJson.put("adunit_format", GameAnalytics.INTERSTITIAL);
impressionDataJson.put("network_class_name", interstitialAd.getResponseInfo().getMediationAdapterClassName());
impressionDataJson.put("revenue", adValue.getValueMicros());
}
catch (JSONException e)
{
e.printStackTrace();
}
GameAnalytics.addImpressionAdMobEvent(MobileAds.getVersionString(), impressionDataJson);
}

Custom Impression Event Fields

It is possible to use a set of key-value pairs to add extra fields but it will only be available through raw data export. Here is an example of how to use it:

HashMap<String, Object> fields = new HashMap<>();
fields.put("test", 100);
fields.put("test_2", "hello_world");

GameAnalytics.addImpressionEvent([adNetworkName], [adNetworkVersion], [impressionData], fields);

tip

For more information on custom event fields and raw data export go here.


tip

For more information on Impression Events go here.

Resource Events

Resource events are used to register the flow of your in-game economy (virtual currencies) – the sink (subtract) and the source (add) for each virtual currency.

caution

Before calling the resource event it is needed to specify what discrete values can be used for currencies and item types in the Configuration phase.

GameAnalytics.addResourceEventWithFlowType(GAResourceFlowType.Source, "Gems", 400, "IAP", "Coins400");

// sink (subtract) Gem currency to buy an item.

GameAnalytics.addResourceEventWithFlowType(GAResourceFlowType.Sink, "Gems", 400, "Weapons", "SwordOfFire");

// sink (subtract) Gem currency to source (buy) some amount of another virtual currency (BeamBooster).

GameAnalytics.addResourceEventWithFlowType(GAResourceFlowType.Sink, "Gems", 100, "Boosters", "BeamBooster5Pack");

GameAnalytics.addResourceEventWithFlowType(GAResourceFlowType.Source, "BeamBooster", 5, "Gems", "BeamBooster5Pack");

// sink (subtract) 3 BeamBooster currency that were used during a level.

GameAnalytics.addResourceEventWithFlowType(GAResourceFlowType.Sink, "BeamBooster", 3, "Gameplay", "BeamBooster5Pack");

Custom Resource Event Fields

It is possible to use a set of key-value pairs to add extra fields but it will only be available through raw data export. Here is an example of how to use it:

HashMap<String, Object> fields = new HashMap<>();
fields.put("test", 100);
fields.put("test_2", "hello_world");

GameAnalytics.addResourceEventWithFlowType(GAResourceFlowType.Sink, "BeamBooster", 3, "Gameplay", "BeamBooster5Pack", fields);
tip

For more information on custom event fields and raw data export go here.


FieldTypeRequiredDescription
flowTypeenumyesAdd (source) or subtract (sink) resource.
currencystringyesOne of the available currencies set in GA_Settings (Setup tab).This string can only contain [A-Za-z] characters.
amountfloatyesAmount sourced or sunk.
itemTypestringyesOne of the available item types set in GA_Settings (Setup tab).
itemIdstringyesItem id (string max length = 32)

danger

Be careful to not call the resource event too often! In a game where the user collect coins fairly fast you should not call a Source event on each pickup. Instead you should count the coins and send a single Source event when the user either complete or fail the level.


tip

For more information on Resource Events go here.

Progression Events

Progression events are used to track attempts at completing some part of a game (level, area). A defined area follow a 3 tier hierarchy structure (could be world:stage:level) to indicate what part of the game the player is trying to complete.

When a player is starting a progression attempt a start event should be added.

When the player then finishes the attempt a fail or complete event should be added along with a score if needed.

Add a progression start event.

GameAnalytics.addProgressionEventWithProgressionStatus(GAProgressionStatus.Start, "world01", "stage01", "level01");

It is not required to use all 3 if your game does not have them:

  • progression01
  • progression01 and progression02
  • progression01 and progression02 and progression03

Custom Progression Event Fields

It is possible to use a set of key-value pairs to add extra fields but it will only be available through raw data export. Here is an example of how to use it:

HashMap<String, Object> fields = new HashMap<>();
fields.put("test", 100);
fields.put("test_2", "hello_world");

GameAnalytics.addResourceEventWithFlowType(GAResourceFlowType.Sink, "BeamBooster", 3, "Gameplay", "BeamBooster5Pack", fields);

tip

For more information on custom event fields and raw data export go here.


FieldTypeRequiredDescription
progressionStatusenumyesStatus of added progression (start, complete, fail).
progression01stringyes1st progression (e.g. world01).
progression02stringno2nd progression (e.g. level01).
progression03stringno3rd progression (e.g. phase01).
scoreintnoThe player’s score.

tip

For more information Progression Events go here.

Error Events

Used to track custom error events in the game. You can group the events by severity level and attach a message. By default the SDK will automatically send error events for uncaught exceptions for more information on this and how to disable it click here.

To add a custom error event call the following function:

GameAnalytics.addErrorEventWithSeverity(GAErrorSeverity.GAErrorSeverityDebug, "Something went bad in some of the smelly code!");

Custom Error Event Fields

It is possible to use a set of key-value pairs to add extra fields but it will only be available through raw data export. Here is an example of how to use it:

HashMap<String, Object> fields = new HashMap<>();
fields.put("test", 100);
fields.put("test_2", "hello_world");

GameAnalytics.addErrorEventWithSeverity(GAErrorSeverity.GAErrorSeverityDebug, "Something went bad in some of the smelly code!", fields);
tip

For more information on custom event fields and raw data export go here.

Design Events

Every game is unique. Therefore some needed events might not be covered by our other event types. The design event is available for you to add your own event-id hierarchy.

caution

Please note that custom dimensions and progression filters will not be added on design and error events. Therefore you cannot (at the moment) filter by these when viewing design or error metrics.

To add a design event call the following method.

GameAnalytics.addDesignEventWithEventId("Kill:Sword:Robot");

It is also possible to add a float value to the event. This will (in addition to count) make the mean and sum aggregation available in the tool.

GameAnalytics.addDesignEventWithEventId("BossFights:FireLord:KillTimeUsed", 234);

Custom Design Event Fields

It is possible to use a set of key-value pairs to add extra fields but it will only be available through raw data export. Here is an example of how to use it:

HashMap<String, Object> fields = new HashMap<>();
fields.put("test", 100);
fields.put("test_2", "hello_world");

GameAnalytics.addDesignEventWithEventId("BossFights:FireLord:KillTimeUsed", 234, fields);
tip

For more information on custom event fields and raw data export go here.

FieldTypeRequiredDescription
eventNamestringyesThe event string can have 1 to 5 parts. The parts are separated by ‘:’ with a max length of 64 each. e.g. “world1:kill:robot:laser”. The parts can be written only with a-zA-Z, 0-9, -_.,:()!? characters.
eventValuefloatnoNumber value of event.
caution

It is important to not generate an excessive amount of unique nodes possible in the event hierarchy tree. A bad implementation example. [level_name]:[weapon_used]:[damage_done]. level_name could be 100 values, weapon_used could be 300 values and damage_done could be 1-5000 perhaps. This will generate an event hierarchy with: 100 x 300 x 5000 = 1.5M possible nodes. This is far too many. Also the damage should be put as a value and not in the event string. The processing will perhaps be blocked for a game doing this and cause other problems when browsing our tool. The maximum amount of unique nodes generated should be around 10k.

tip

Read more about event types here. You will get the most benefit of GameAnalytics when understanding what and how to track.

tip

You will get the most out of GameAnalytics when understanding what and how to track.

tip

For more information on Design Events go here.