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 Mochimedia - they have many services, and one of them is statistics. Unfortunately, it is very simple and unable to give you such detailed data as in the first paragraph. You could also google and find few other services... and among few smaller ones, Google Analytics for Flash project (later shortened to GAF).
That's true - you can use the well known, very powerfull, 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.
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:
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):
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.
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 :)
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:
- 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.
- 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.
- 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 informative articles 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.
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?".
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 this and this Stack Overflow answer shows, you could make a http request to the GA page - yet the list of parameters is quite long and it would be nice to have a library for that. There is a project UsageAnalytics which tries to close this functionality in one codebase, yet from my quick look, code is quite complicated. Then there's DeskMetrics - looks good from the outside, but the pricing is really steep, and free trial is only 14 days. So the situation for "traditional", C++ desktop games/applications in regard of statistics is not that good. Perhaps your search for "google analytics C++" will be luckier.
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 :)
Article Update Log
1 July 2013: First version