Analytics is the process of gathering and finding patterns within a set of data. This data can be any quantifiable action, such as a mouse click, and its related elements, such as what was clicked. This information allows developers to see how users are using their product. It is incredibly useful when creating games, because there are so many things that can be tracked.
We are going to implement Flurry Analytics, one of the two systems GameMaker: Studio has built-in and is the most robust. While it is possible to track anything and everything, it is generally better to focus on things that are most relevant to the user experience. For our game we are going to track each level's score, equipment used, and times played. We will only send this data out upon the player successfully completing a level. This will allow us to see how often each level is played, what equipment is used the most, the variation in scores, how hard each level is, and where people quit the game on average.
In order to use Flurry Analytics, we need to have an account with the service, an application to send the data to, and have it activated in GameMaker: Studio. Once that has been done, a new build needs to be uploaded to the site and people need to play the game.
Destruct
, and choose an appropriate category, in our case, Games - Simulation seems to fit best. Then click on Create App.Now that we can send out data we just need to implement it into the existing game. We need to add some bits of code into several scripts, plus create some new ones, in order to get useful, trackable information. We want to track the level being played, usage of each piece of equipment, how many times the level has been played, and the score of the level.
TNT: 0, WRECKINGBALL: 1
, MAGNET: 2
) that can be reused for tracking purposes. That leaves us needing some additional constants for the level, the attempts, and the score. Navigate to Resources | Define Constants and add LEVEL: 3, ATTEMPTS: 4, LVLSCORE: 5
.scr_Global_Analytics
, and initialize values for the whole game.globalvar levelData; levelData = ds_grid_create(totalLevels, 6); for (i = 0; i < totalLevels; i++) { ds_grid_set(levelData, i, TNT, 0); ds_grid_set(levelData, i, WRECKINGBALL, 0); ds_grid_set(levelData, i, MAGNET, 0); ds_grid_set(levelData, i, LEVEL, level[i, 0]); ds_grid_set(levelData, i, ATTEMPTS, 0); ds_grid_set(levelData, i, LVLSCORE, 0); }
We start by creating a global data structure that has six values for each level in the game. We run a loop to set the initial values for each piece of equipment, the level by grabbing the level name from the previously created level array, the amount of attempts, and the level score, all set to zero.
scr_Global_GameStart
and execute this script.scr_Button_LevelSelect_MousePressed
, where we need to add an attempt when the player selects a level. In the else
statement, before we change rooms, add the following code:currentLevel = ds_grid_value_x(levelData, 0, LEVEL, totalLevels-1, LEVEL, myLevel); ds_grid_add(levelData, currentLevel, ATTEMPTS, 1);
We search through the levelData grid for the room that has been selected in order to find out what row we need to change. Once we have the row, we add one attempt to that level's data.
scr_Menu_Button_Restart
just before we restart the room.scr_Menu_Button_NextLevel
, except we cannot use myLevel
to find the room. Instead, we need to look ahead to the next room. Just before we change rooms, insert the following code:currentLevel = ds_grid_value_x(levelData, 0, LEVEL, totalLevels-1, LEVEL, level[i+1,0]); ds_grid_add(levelData, currentLevel, ATTEMPTS, 1);
scr_Level_Stats
, and update all the relevant stats.levelCompleted = ds_grid_value_x(levelData, 0, LEVEL, totalLevels-1, LEVEL, room) for (i = 0; i < ds_grid_width(equip); i += 1) { equipUsed = abs(ds_grid_get(obj_Menu.startEquip, i, AMOUNT) - ds_grid_get(equip, i, AMOUNT)); ds_grid_set(levelData, levelCompleted, i, equipUsed); } levelScore = obj_Menu.tempScore - obj_Menu.tempCost ds_grid_set(levelData, levelCompleted, LVLSCORE, levelScore);
We start by finding the row for the level that has just been completed. We then run a loop through the equipment to see how many were used in the level, by subtracting the remaining equipment from how many the player started with. To ensure we get a positive number we use the abs function which returns an absolute value. We also grab the final score of the level and update the grid.
scr_WinCondition
, just before the last line of code where we return a true value.scr_Level_Stats();
The data is now properly updated each time the level is played and successfully completed. All we need to do now is send the data to Flurry. Flurry does not update in real time, but instead compiles the data several times a day. If we send bits and pieces of data individually throughout the entire play session, that data might be separated when it is compiled, resulting in anomalies. To help prevent this, we are going to send all the relevant data of every level each time we want to update. Flurry will recognize the changes and keep the data together.
scr_Analytics_Send
, and run a loop through all the level data and send it out.for (a = 0; a < totalLevels; a++) { levelName = room_get_name(ds_grid_get(levelData, a, LEVEL)); levelScore = ds_grid_get(levelData, a, LVLSCORE); levelAttempt = ds_grid_get(levelData, a, ATTEMPTS); usedTNT = ds_grid_get(levelData, a, TNT); usedWB = ds_grid_get(levelData, a, WRECKINGBALL); usedMagnet = ds_grid_get(levelData, a, MAGNET); analytics_event_ext(levelName, "Level Score", levelScore, "Attempts", levelAttempt, "TNT Used", usedTNT, "WBalls Used", usedWB, "Magnets Used", usedMagnet); }
In this loop, we start by grabbing the name of the room stored in the grid and all the values for each piece of data. Using the function analytics_event_ext
we can send up to 10 different pieces of data to Flurry. The first parameter is the category of data, sent as a string, in this case we are using the name of the levels as categories. All the following parameters are key/value pairs with the name of the data we are tracking and its associated value.
scr_Global_Analytics
and send the data at the end of the script.scr_Analytics_Send();
scr_Level_Stats
and send the data at the end of the script as well.We are tracking several pieces of data, and Flurry will be compiling this information into event logs. We can see when a session has occurred and what happened during that play session. While this is somewhat useful, Flurry breaks things down even further on a global scale that will show us how each level is played on average. Let's take a moment to see what Flurry provides us with. Before we get started, it is important to know that Flurry Analytics are not updated in real time, and it may take a few hours before we see any data appear.
Having this type of information is incredibly valuable. Knowing where players stop playing the game can tell us where improvements can be made. Tracking what the player is using during the game lets us know if the game is balanced properly. The more useful data we can gather, the better we can apply the lessons learned to future games.
18.188.227.4