Creating a Heat Map to track where players go?

I am trying to create a system that tracks all of the movements taken by a player on a level. This data needs to overlay on a terrain/level layout in the form of a heat map (still image) so that my team can assess the movement after the game has been played.

Here is an example of a Heat Mapped Level:

alt text

The purpose of this is to track user experience throughout the world in order to assess which areas the gamers find interesting and which areas they are avoiding.

Any suggestions on how to approach this is appreciated. I plan on releasing the final system to the community once completed.

Photo from this blog

I had a similar thought as efge did, thinking on a way to create the heatmap in Unity. Here's what I came up with:

First I set up a script to store position data:

var positionTrackingFrequency : int = 2;        // How often to store player position
private var timer : float = 0;                  // The timer

static var posArray : Vector2[];                // Local array storing player position

// debug vars
private var arrayIterator : int = -1;

function Update()
{
    timer += 1 * Time.deltaTime;

    if(timer >= positionTrackingFrequency)
    {
        storePos();
    }
}

function storePos()
{   
    timer = 0;

    var localArray : Array = new Array();

    if(posArray != null)
        localArray = new Array(posArray);

    localArray.Push(Vector2(transform.position.x, transform.position.z) );

    posArray = localArray.ToBuiltin(Vector2);
    arrayIterator++;
    Debug.Log(" " + posArray[arrayIterator] + "  Iteration =  " + arrayIterator);
}

Next on the agenda was to translate the data into a heatmap, so I duplicated the level, set up an orthographic camera and loaded all the positions using cubes as 'marks' for player position.

var heightToOverlay : int = 10;
var theHeat : GameObject;

static var finished : boolean = false;

function Start()
{
    var pass : Vector2[] = storeHeatMapData.posArray;
//  Debug.Log("" + pass.length);
    readData(pass);
}

function readData(curArray : Vector2[])
{
    for(iterator = 0; iterator < curArray.length; iterator++)
    {
        Instantiate(theHeat, Vector3(curArray[iterator].x, heightToOverlay, curArray[iterator].y), transform.rotation);
    }

    finished = true;
}

From here, my next step would be to write a script to cycle through the 'marks' of previous player locations and adjust their Red & Blue material values depending on how many neighbors are within a given radius (Creating the 'heat' for the map). After that, Application.CaptureScreenShot and a heatmap is (hopefully) saved. I didn't have time to write the actual code, but I figure this method could be scaled to allow for multiple playthroughs to be loaded into the heatmap creation level.

Edit : Wrote the code to color the cubes from cool (blue) to hot (red). Attach this to the prefab you're instantiating as the marks.

// The distance allowed to affect 'heat'
var allowableDistance : float = 5;

// How much to adjust the 'heat' by.
var colorAdjuster : float = .2;

// Creates a material from shader&texture references
var texture : Texture;
var color : Color;

// This stops the material from continuously altering color
private var thisCubeDone : boolean = false;

function Start () 
{
    renderer.material = new Material (Shader.Find("Self-Illumin/Diffuse"));
    renderer.material.mainTexture = texture;
    renderer.material.color = color;
    renderer.material.color = Color.blue;
}

function Update()
{
    // Wait till map is finished loading to check nearby marks
    if(readHeatMapData.finished && !thisCubeDone)
        setColor();
}

function setColor()
{
    var heatBoxes : GameObject[] = GameObject.FindGameObjectsWithTag("heatBox");
    for(item in heatBoxes)
    {
        var distCheck = Vector2(item.transform.position.x, item.transform.position.z) - 
                        Vector2(this.transform.position.x, this.transform.position.z);
        dist = distCheck.sqrMagnitude;
        Debug.Log("Distances " + dist);
        if( dist <= allowableDistance )
        {
            // Here's where the color is actually adjusted.
            // It starts blue (cool) by default and moves towards red (hot)
            renderer.material.color.r += colorAdjuster;
            renderer.material.color.b -= colorAdjuster;
        }
    }
    thisCubeDone = true;
}

One last edit: If you were to add a blur effect to the marks (I think that camera effects are a Unity Pro feature only?) it would look much more like a traditional heat map. Here is what I was able to get after a few minutes of adjusting settings:

![screenshot][1]

I programmed something similar. Part of the solution can be found over here.

I didn't use a server, though, but collected the data locally (it was an exhibition project), transformed the XML files with XSLT into simple CSVs, and a friend then analysed the data in R. (Since I made a side scroller, the heatmaps were not exactly telling much, though )

Edit: I created a github project with the code and an explanatory readme: https://github.com/xeophin/UnityGamePlayMetrics Feel free to download it, fork it, improve it all the way, so that I can integrate all the improved goodness back in.

Record transform.position during game play and when replaying spawn polys with low alpha texture and the particle additive shader applied. (just an idea)

(of course you should remove players and enemies, effects, fog etc. and use a ortographic camera)

Assuming you don't need really high accuracy, then why don't you just place triggers in major parts of you level and count the number of times someone enters them. It won't give you quite the precision, but it will give you better performance than tracking each object's position every frame.

var traffic : int;

function OnTriggerEnter (col : Collider) {
    if(col is a player) {
        traffic++;
    }
}

Then have a script that gathers all the info from each when your done and colors boxes accordingly.

Hey guys, I found this post and it was exactly what I needed for my master’s thesis. Especially Kaspar Manz GamePlayMetrics.

With the help of a friend I have expanded the code, to easily generate a heatmap from GamePlayMetrics’ XML-files. The code (and generated .exe) works outside of Unity!

Warnings:
-Quick and dirty coding.
-Only works with German system language(the difference between . and , in numbers is crucial. You should be able to change it in the code.).
-I added rotation to GamePlayMetrics, but it doesn’t work properly on the heatmap (Unity has minus-rotations. I guess the heatmap-code doesn’t understand that. Plus-rotation is fine.).

Here’s the link for the heatmap-code: File-Upload.net - Datei nicht gefunden
And here for GamePlayMetrics with rotation (works exactly like the original):File-Upload.net - Datei nicht gefunden

Instructions (for a level with roughly 30x30 Unity units and the player’s starting point at 0x0):
-Extract into a folder.
-Click to the path Heatmap/bin/Debug.
-Put your log.xml there.
-Rename it to log.xml (GamePlayMetrics has some numbers before the log.xml).
-Start the Heatmap.exe.
-Open heatmap_done.png.

For a level with different dimensions you have to adjust the dimensions in the code (sorry, not sure where and how, but shouldn’t be too hard).
To see your own level underneath the arrows, just exchange the heatmap.png in the Heatmap-folder (not in the Debug-folder) with a top-down view of your level. And recompile your code. Same for the arrow.
There’s also code in there for ellipses instead of arrows and writing the rotation next to them. It’s commented out.

License is Creative Commons Attribution 3.0 Unported (CC BY 3.0), but the programmer actually doesn’t want to be attributed. I guess he’s ashamed. :stuck_out_tongue:
So do with it whatever you want.