Skip to main content

Event Tracking

GameAnalytics supports 7 different types of events: Business, Ad, Impression, Resource, Progression, Error and Design.

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

Business Events

Business events are used to track in-app purchases with real money.

info

Mobile games can be hacked and distributed illegally. Hacking an app will often involve faking/simulating all purchase requests. This will result in several Business events being sent to GameAnalytics for transactions that never occurred.

GameAnalytics provide the option of receipt validation for each purchase sent to GA servers. This process can ensure that revenue metrics reflect the actual spending in your game.

info

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

Receipt validation in Defold is supported for the following stores.

  • App Store (iOS)
  • Google Play Store (Android)
gameanalytics.addBusinessEvent {
currency = "USD",
amount = 100,
itemType = "boost",
itemId = "megaBoost",
cartType = "shop",
customFields = fieldsSting
}

local info = sys.get_sys_info()

if(info.system_name == "Android") then
gameanalytics.addBusinessEvent {
currency = "USD",
amount = 100,
itemType = "boost",
itemId = "megaBoost",
cartType = "shop",
receipt = "<receipt>",
signature = "<signature>"
}
end
if(info.system_name == "iPhone OS") then
gameanalytics.addBusinessEvent {
currency = "USD",
amount = 100,
itemType = "boost",
itemId = "megaBoost",
cartType = "shop",
receipt = "<receipt>"
}
end

Custom Business 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:

local table = require "tableutils.tostring"

local fields = {
test = "hello",
test2 = 1234
}

local fieldsSting = table.tostring(fields)

gameanalytics.addBusinessEvent {
currency = "USD",
amount = 100,
itemType = "boost",
itemId = "megaBoost",
cartType = "shop",
customFields = fieldsSting
}
tip

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

tip

For more information about the business event go here.

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.

Ad Events

info

Only for Android and iOS!!!

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 Videos

Showing a rewarded video

It may be possibile for an ad to not be available, if this happens you can track this as shown below:

if (interstitialAd != null && interstitialAd.isLoaded()) {
interstitialAd.show();
}
else
{
gameanalytics.addAdEvent {
adAction = "FailedShow",
adType = "Interstitial",
adSdkName = "admob",
adPlacement = "[AD_PLACEMENT_OR_UNIT_ID]"
}
}

And if the video is availble, you can track it once it has been completed:

function onRewardedVideoCompleted() {
if(that.get().currentRewardedVideoPlacement != null)
{
gameanalytics.addAdEvent {
adAction = "Show",
adType = "RewardedVideo",
adSdkName = "admob",
adPlacement = "[AD_PLACEMENT_OR_UNIT_ID]"
}
}
}

Interstitials

Showing an interstitial ad

If the ad has failed to load for any particular reason you can track it with the following event:

if (interstitialAd != null && interstitialAd.isLoaded()) {
interstitialAd.show();
}
else
{
gameanalytics.addAdEvent {
adAction = "FailedShow",
adType = "Interstitial",
adSdkName = "admob",
adPlacement = "[AD_PLACEMENT_OR_UNIT_ID]"
}
}

For interstitials we track an event at the time it is being shown. The following example is a method for how you could handle this:

function onAdOpened() {
// send ad event
gameanalytics.addAdEvent {
adAction = "Show",
adType = "Interstitial",
adSdkName = "admob",
adPlacement = "[AD_PLACEMENT_OR_UNIT_ID]"
}
}

Tracking interstitial click

Call this event once the user has clicked the ad and left the application. The following example is a method for how you could handle this.

function onAdLeftApplication() {
// send ad event
gameanalytics.addAdEvent {
adAction = "Clicked",
adType = "Interstitial",
adSdkName = "admob",
adPlacement = "[AD_PLACEMENT_OR_UNIT_ID]"
}
}

Loading a banner ad

function onAdLoaded() {
gameanalytics.addAdEvent {
adAction = "Show",
adType = "Banner",
adSdkName = "admob",
adPlacement = "[AD_PLACEMENT_OR_UNIT_ID]"
}
}

If the ad fails to load we track the latest when onAdFailedToLoad(int errorCode) listener method is called. The following example is a method for how you could handle this.

function onAdFailedToLoad(int errorCode) {
// OR .. if you don't want to track errors
gameanalytics.addAdEvent {
adAction = "FailedShow",
adType = "Banner",
adSdkName = "admob",
adPlacement = "[AD_PLACEMENT_OR_UNIT_ID]"
}
}

Tracking banner clicks

function onAdOpened() {
// send ad event
gameanalytics.addAdEvent {
adAction = "Clicked",
adType = "Banner",
adSdkName = "admob",
adPlacement = "[AD_PLACEMENT_OR_UNIT_ID]"
}
}

Custom Ad 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:

local table = require "tableutils.tostring"

local fields = {
test = "hello",
test2 = 1234
}

local fieldsSting = table.tostring(fields)

gameanalytics.addAdEvent {
adAction = "Clicked",
adType = "Banner",
adSdkName = "admob",
adPlacement = "[AD_PLACEMENT_OR_UNIT_ID]",
customFields = fieldsSting
}
tip

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

FieldTypeDescriptionExample
adActionenumA defined enum for ad action (for example clicked).GAAdAction.Clicked
GAAdAction.Show
GAAdAction.FailedShow
GAAdAction.RewardReceived
adTypeenumA defined enum for ad type (for example interstitial).GAAdType.Video
GAAdType.RewardedVideo
GAAdType.Playable
GAAdType.Interstitial
GAAdType.OfferWall
GAAdType.Banner
adSdkNamestringName of the Ad/Ad mediation SDK.admob
adPlacementstringIdentifier of ad in the game or the placement of it.level_complete_ad
adPlacementstringIdentifier of ad in the game or the placement of it.level_complete_ad
durationintOptional. Only used for video ads to track how long the user watched the video for.10
noAdReasonenumOptional. 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
GAAdError.InvalidRequest
GAAdError.UnableToPrecache

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.

info

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.

Source (add) Gem currency from an in-app purchase.

gameanalytics.addResourceEvent {
flowType = "Source",
currency = "Gems",
amount = 400,
itemType = "IAP",
itemId = "Coins400"
}

Sink (subtract) Gem currency to buy an item.

gameanalytics.addResourceEvent {
flowType = "Sink",
currency = "Gems",
amount = 400,
itemType = "Weapons",
itemId = "SwordOfFire"
}

Sink (subtract) Gem currency to Source (buy) some amount of another virtual currency (BeamBooster).

gameanalytics.addResourceEvent {
flowType = "Sink",
currency = "Gems",
amount = 100,
itemType = "Boosters",
itemId = "BeamBooster5Pack"
}
gameanalytics.addResourceEvent {
flowType = "Source",
currency = "BeamBooster",
amount = 5,
itemType = "Gems",
itemId = "BeamBooster5Pack"
}

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

gameanalytics.addResourceEvent {
flowType = "Sink",
currency = "BeamBooster",
amount = 3,
itemType = "Gameplay",
itemId = "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:

local table = require "tableutils.tostring"

local fields = {
test = "hello",
test2 = 1234
}

local fieldsSting = table.tostring(fields)

gameanalytics.addResourceEvent {
flowType = "Sink",
currency = "BeamBooster",
amount = 3,
itemType = "Gameplay",
itemId = "BeamBooster5Pack",
customFields = fieldsSting
}
tip

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

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 about the resource event 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.addProgressionEvent {
progressionStatus = "Start",
progression01 = "world01",
progression02 = "stage01",
progression03 = "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:

local table = require "tableutils.tostring"

local fields = {
test = "hello",
test2 = 1234
}

local fieldsSting = table.tostring(fields)

gameanalytics.addProgressionEvent {
progressionStatus = "Start",
progression01 = "world01",
progression02 = "stage01",
progression03 = "level01",
customFields = fieldsSting
}
tip

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

tip

For more information about the progression event 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.

To add a custom error event call the following function:

gameanalytics.addErrorEvent {
severity = "Debug",
message = "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:

local table = require "tableutils.tostring"

local fields = {
test = "hello",
test2 = 1234
}

local fieldsSting = table.tostring(fields)

gameanalytics.addErrorEvent {
severity = "Debug",
message = "Something went bad in some of the smelly code!",
customFields = fieldsSting
}
tip

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

tip

For more information about the error event go here.

Design Events

Every game is special. 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.

danger

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.addDesignEvent {
eventId = "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.addDesignEvent {
eventId = "BossFights:FireLord:KillTimeUsed",
value = 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:

local table = require "tableutils.tostring"

local fields = {
test = "hello",
test2 = 1234
}

local fieldsSting = table.tostring(fields)

gameanalytics.addDesignEvent {
eventId = "BossFights:FireLord:KillTimeUsed",
value = 234,
customFields = fieldsSting
}
tip

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

danger

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 * 300 * 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.