Introduction

Being able to check out how many players play your game, from what countries, for how long, on which levels they have problems, how much points do they score, even do they ever visit your precious Credits screen or the average FPS number – that sounds incredibly useful, doesn’t it? Fortunately, in web browser games, there’s a way to get such informations. In this post I’m going to describe the process for Flash (ActionScript 3), because I’ve recently implemented it in my released game, and can share some experiences.

Possibilities

And there are even many ways. First, some time ago you could use Playtomic.com – however they had notorious problems with reliability, and are now out of bussiness. Then, the second try could be Google Analytics for Flash project (later shortened to GAF).

That’s true – you can use the well known, very powerful, complex, free, reliable and spied service from Google to process the statistics from your own Flash games. And it’s actually pretty easy to use. Sadly, the documentation is rather cryptic, sparse, ambigous and hard to follow. So, here goes a quick, practical tutorial for you + code samples :)

Let’s dive in

First, download the files from their site (latest version haven’t been updated for a long time) and put it in some directory like lib/gaf, alongside other game libraries. Inside your IDE link to the one of the .swc files: analytics.swc (codebased IDE like FlashDevelop) or analytics_flash.swc (component for Flash CS). Code snippet from Ninja Cat:

package
 
  {
 
    import com.google.analytics.AnalyticsTracker;
 
    import com.google.analytics.GATracker;
 
    import flash.display.DisplayObject;
 
   
 
    public class Analytics
 
    {
 
   
 
      public function Analytics()
 
      {
 
      }
 
   
 
      CONFIG::stats
 
      {
 
        private var tracker:AnalyticsTracker;
 
      }
 
   
 
      public function Init( stage : DisplayObject ) : void
 
      {
 
        CONFIG::stats
 
        {
 
          // UA-google-api-code has to be replaced by your ID, taken from GA page
 
          // fourth parameter is visual_debug - its described later in post
 
          tracker = new GATracker( stage, "UA-google-api-code", "AS3", false );
 
          PageView("Home");
 
        }
 
      }
 
   
 
      public function PageView( URL : String ) : void
 
      {
 
        CONFIG::stats
 
        {
 
          // google wants to have slashes before names of pages
 
          tracker.trackPageview( "/" + URL );
 
        }
 
      }
 
   
 
      public function LinkOpen( name : String, URL : String ) : void
 
      {
 
        PageView( name );
 
   
 
        // could also automatically open link
 
        // Link.Open(URL, name, "Links");
 
      }
 
   
 
      public function TrackEvent( category : String, action : String, label : String, value : int = 0 ) : void
 
      {
 
        CONFIG::stats
 
        {
 
          tracker.trackEvent(category, action, label, value );
 
   
 
          trace("GAF event: " + category + " | " + action + " = " + value + " ( " + label + " )" );
 
        }
 
      }
 
    }
 
  }

Before anything: what is CONFIG::stats? It’s a way of conditionally including code in AS3 (a kind of #ifdef macrodefinitions for you C++ buffs). It’s very useful – by toggling one variable in IDE, you can build a significantly different version of game. So, if CONFIG::stats is not defined, all that is between braces will be ignored. In this case, it might be useful to disable statistics ie. for local testing. Here you can read more about this technique.

So, what I’ve done here, is estabilished interface for using GAF in my game. Create the object of type Analytics somwhere near the start of your game, call the Init method, and you’re ready to go. Then the question arises: how to use it?

GAF gives you two ways of tracking user behaviour: page views and events. Simply speaking, page views are like in the web browser – navigation between different URL locations. User views your /blog subpage, your /about, your /games etc. Events are for user interactions with elements of the page, which don’t result in changing of the page – so for www that would be ie. movie controls, downloading files, clicking on polls etc. With events you can log more informations; pages only log the name of the visited pages.

Note: Google Analytics doesn’t process everything instantly. For more detailed data you will at least need to wait till the next day. There’s a Real time mode which shows pages and events that happend within last 30 minuts, altough it’s with limited functionality. For example, one thing which it doesn’t show is the values of events.

In case of games, you’d want to use this duo like that: pages are for game states and menu screens (MainMenu, Options, Credits, Level1, StatsScreen), while events are used for detailed statistics (I’ll get to that later on). From the code above you can also see that I decided to have LinkOpen be treated as page views, altough it could also work as an event.

Basic Results

So, when you add this kind of code to your game, add the function calls in appropriate places (ie. analytics.PageView(“MainMenu”); ), and turn debug mode on (fourth parameter to GATracker is true), you’ll see some debugging info appear:

gaf-debug
With this you can quickly confirm that things work as expected. Having this, you can go to your Google Analytics dashboard and start peeking at the statistics. Here’s how GA looks with the data from Ninja Cat and Zombie Dinosaurs (I cut out only the interesting bits):

gaf-stats1
What is interesting here, is the incredibly small Bounce rate of 0.03% – it means that 99,97% of users who load the game and see menu, continue to start the first level. Compare that to Bounce rate of anywhere between 40-70% for normal websites. Huge win for me.

right_now_21

Google Analytics has this nice feature of showing some stats in a realtime preview. And so at that thursday afternoon over 20 actual people were playing my game, and from the map below I saw that they were from all around the world. For the creator it’s humbling :)

gaf-stats3
Last of screenshots shows the details on which “pages” were viewed the most. We can see ie. that players are not interested in me (Credits) or sponsors (links), and they even very rarely visit Options. Hmmm. When I play new games, first thing I do is look into options and credits. Oh well.

According to Mochimedia, my game so far (beginning of june) had around 28k displays of ads – which is almost the same amount as /Main views in Google. So both systems confirm each others reliablity (or both are wrong ;). As for an online flash game, almost 30k plays (and 1-2k per day) is very small number. I think after maybe 2 months I’m going to write a separate post about how Ninja Cat succeeded in the “internets”.

Apart from the dashboard, you can find useful data a bit burried in Content -> Site content -> Content drilldown and Content -> Events -> Overview. I would really recommend to spend few hours reading Google Analytics help, to get a good understanding of the platform (goal completions, funnels, conversions, intelligence events, how to filter, learning UI) – there’s lots of stuff.

Here I’ll briefly mention three features:

    1. Traffic sources – where you can see the URLs that people are playing your game on… at least theoretically, because I don’t see the URLS of most sites, just some part of it. What works much better for me is the Ads section in developer dashboard on MochiMedia.
    2. Intelligence events – starts working if you have more data, for at least few weeks. Then GA will analyse it and point out for any unusual events ie. sudden increase or decrease of people coming from a certain country, or decrease of avg play time. It’s mostly targeted at website owners, who can then make some adjustments to their site.
    3. Goal completions – on commercial websites they’re used to track how far the user is along the path to goal, which is typically buying something. Landing page -> catalog -> add to cart -> checkout -> payment – you get the idea. In our case, they could be used to track how much has user progressed in game: level 1 -> level 2 -> …, and the goal would be last level of game. In this way GA will show you how many people have finished your game. How cool is that? :) In order to have it, you’ll need to specify a funnel – sequence of page views, which lead to your goal. More on that in GA documentation.

 

Logging detailed statistics

Coming back to the beginning of post – how about original requirement? My game (which is typing game inspired by Typing of the Dead) collects detailed statistics about player progress – they are displayed after finishing a level. Those are things like number of points, enemies killed, katana kills, how much time (in seconds) did it take to finish it, accuracy, number of keystrokes etc. Those are natural things to log. Here’s the code of function in some StatsScreen class that I used:

public function LevelEnd(
 
          level_index : int, level_time : int,
 
          enemies_killed : int, katana_kills : int, score_points : int,
 
          total_keystrokes : int, accuracy : int,
 
          avg_kill_time : int, avg_kill_score : int,
 
          collected_powerups : int, stars : int, health_loss : int,
 
          player_name : String,
 
          _result : int // 1 for died, 2 for won
 
      ) : void
 
  {
 
      CONFIG::stats
 
      {
 
          var cat : String = "Level_" + level_index; // cat is for category
 
   
 
          analytics.TrackEvent(cat, "time", null, level_time ); // I won't shorten analytics though
 
   
 
          analytics.TrackEvent(cat, "enemies_killed", player_name, enemies_killed );
 
          analytics.TrackEvent(cat, "katana_kills", player_name, katana_kills );
 
          analytics.TrackEvent(cat, "score", player_name, score_points );
 
   
 
          analytics.TrackEvent(cat, "keystrokes", player_name, total_keystrokes );
 
          analytics.TrackEvent(cat, "accuracy", player_name, accuracy );
 
   
 
          analytics.TrackEvent(cat, "avg_kill_time", player_name, avg_kill_time );
 
          analytics.TrackEvent(cat, "avg_kill_score", player_name, avg_kill_score );
 
   
 
          analytics.TrackEvent(cat, "powerups", player_name, collected_powerups );
 
          analytics.TrackEvent(cat, "stars", player_name, stars );
 
          analytics.TrackEvent(cat, "health_loss", player_name, FlxU.abs(health_loss) );
 
   
 
          analytics.TrackEvent(cat, "player_name", player_name );
 
   
 
          analytics.TrackEvent(cat, "music_volume", player_name, int(FlxG.music.volume * 100) );
 
          analytics.TrackEvent(cat, "sound_volume", player_name, int(FlxG.volume * 100) );
 
   
 
          var result : String =(_result == StatsScreen.FINISHED_LEVEL ? "win" : "lost");
 
   
 
          analytics.TrackEvent(cat, "difficulty", result, Game.difficulty );
 
      }
 
  }

As you can see, there’s also music and sound volume – who knows, maybe I’ll see some interesting trend here, ie. most players disable music? I also collect FPS informations (min, max, avg) and player name, because I am curious what players will write there :) You can also log capabilities of players system, just like Valve is doing with steam – I log only flash player version.

The reason behind passing player_name as label values is that then you should be able to drill down and view statistics coupled with specific players. Of course 90% of players won’t change the default “Ninja Cat”, but it will work for those who do. However, I’m not entirely sure whether my category/action/label naming convention is any good, and would seriously advise to read few about the topic.

If you’d like to see the values of those events, here’s the breadcrumb path in GA dashboard jungle: Content -> Events -> Top events, then in the list of categories you choose which level you want, just by clicking the name. On the new screen, under graph click Event action as the Primary dimension. Then you’ll see the detailed stats.

gaf-stats4

The Avg. Value column holds the date we’re interested in. The Event Value will contain sum of all the values… not really useful, unless you want to know how many dinosaurs have killed all your players on level 1. Hmmm, that sounds like a great marketing information, “Ninja Cat and Zombie Dinosaurs players so far have killed one million and 200 thousand zombie dinosaurs… wanna help getting rid of the plague?“.

Warning: For a long time I had this problem of not being able to see the values of events in GA reports. I looked everywhere there, I checked code – GIFs were sent, other things worked. I asked on internet but no one answered. I thought maybe it’s just one thing you can’t do from Flash code, and so I released game without this working. Later, when preparing this article, I wanted to try it one more time, so I made a simple test application and started experimenting. To my positive surprise, it worked!

The thing that was blocking it, was the lack of label value sent in event. Though the documentation says label is optional, appareantly if you want to see the actual values, it’s not optional. Also worth mentioning is that the value (last parameter) has to be positive integer. Because of that, all percents should be multiplied by 100, miliseconds by 1000 etc. It would even be sensible to use proper hungarian notation and postfix the units in action names.

 

Conclusion

So, there you have it: a way to track player behaviour, and to look into some interesting facts about usage of your game. A natural question arises: could it be done with other technologies on other platform, specifically: C++ on desktop games?

Technically yes, and that’s even possible in many ways. First, as writing your own analytics service.

But even if you had that magic tool, you’d steel need to ask user for permision – offline applications are not expected to freely contact internet services. Web games have it easier here – user is obviously connected and in his browser when he’s playing, so there’s little difference to tracking user behaviour in game embedded on page, versus tracking user behaviour on page. The latter is usually taken for granted, since most websites collect statistics, and Google Analytics is one example of such a system. So web games should also be accepted.

If you still have technical questions regarding usage of Google Analytics in Flash/AS3, you may read a similiar, but more thorough (and more oriented towards Flash CS users) tutorial over here, ask question in comment below, or do a search in your favourite search engine :)