Skip to content

Instantly share code, notes, and snippets.

@kurtdekker
Created February 7, 2022 14:42
Show Gist options
  • Save kurtdekker/74f21975521e5042738018eace2f2e20 to your computer and use it in GitHub Desktop.
Save kurtdekker/74f21975521e5042738018eace2f2e20 to your computer and use it in GitHub Desktop.
Unity3D simple time-and-distance statistics accumulation
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// @kurtdekker
//
// This is the statistics accumulator that runs whenever
// a game is running in Jetpack Kurt.
//
// When your game starts call CommonStatisticsAccumulator.Attach()
//
// STATE.MissionChronometer / SegmentChronometer are just statics
//
// DSM.Statistics.LifetimeShotsFired.iValue are persistent storage
//
// Statistics.AccumulatePlayTime() actually adds and writes totals.
//
// Note that persistent data is only written in OnDisable()
//
public class CommonStatisticsAccumulator : MonoBehaviour
{
static CommonStatisticsAccumulator Instance;
public static CommonStatisticsAccumulator Attach()
{
var csa = new GameObject("CommonStatisticsAccumulator.Attach();").
AddComponent<CommonStatisticsAccumulator>();
Instance = csa;
return csa;
}
float accumulatedTime;
float accumulatedDistance;
Vector3 ?PlayerLastLocation;
// all the pause menu mechanisms call this to start/stop
// statistics accumulation.
public static void SetActive( bool onoff)
{
if (Instance)
{
Instance.enabled = onoff;
}
}
void Update()
{
{
float TimeToAdd = Time.deltaTime;
accumulatedTime += TimeToAdd;
STATE.MissionChronometer += TimeToAdd;
STATE.SegmentChronometer += TimeToAdd;
}
if (STATE.PlayerTransform)
{
Vector3 position = STATE.PlayerTransform.position;
if (PlayerLastLocation != null)
{
Vector3 delta = position - (Vector3)PlayerLastLocation;
delta.y = 0;
float DistanceToAdd = delta.magnitude;
accumulatedDistance += DistanceToAdd;
STATE.MissionOdometer += DistanceToAdd;
STATE.SegmentOdometer += DistanceToAdd;
}
PlayerLastLocation = position;
}
}
void OnDisable()
{
Statistics.AccumulatePlayTime( accumulatedTime);
Statistics.AccumulateTotalDistanceFlown( accumulatedDistance);
DSM.Statistics.LifetimeShotsFired.iValue += STATE.MissionShotsFired;
DSM.Statistics.LifetimeShotsHit.iValue += STATE.MissionShotsHit;
PlayerPrefs.Save ();
}
}
// relevant methods from Statistics.cs, the stuff called from accumulator script above:
private static string s_TotalPlayTime = "TotalPlayTime";
public static float TotalPlayTime
{
get
{
return PlayerPrefs.GetFloat (s_TotalPlayTime, 0);
}
private set
{
PlayerPrefs.SetFloat( s_TotalPlayTime, value);
}
}
public static void AccumulatePlayTime( float accumulatedTime)
{
Statistics.TotalPlayTime += accumulatedTime;
}
public static void AccumulateTotalDistanceFlown( float accumulatedDistance)
{
if (!float.IsNaN( accumulatedDistance))
{
TotalDistanceFlown += accumulatedDistance;
}
}
private static string s_TotalDistanceFlown = "TotalDistanceFlown";
public static float TotalDistanceFlown
{
get
{
var x = PlayerPrefs.GetFloat (s_TotalDistanceFlown, 0);
if (float.IsNaN( x))
{
x = 0;
}
return x;
}
private set
{
PlayerPrefs.SetFloat( s_TotalDistanceFlown, value);
}
}
// formatting and output:
public static string TimeFormatter( float seconds, bool forceHHMMSS = false)
{
float secondsRemainder = Mathf.Floor( (seconds % 60) * 100) / 100.0f;
int minutes = ((int)(seconds / 60)) % 60;
int hours = (int)(seconds / 3600);
if (!forceHHMMSS)
{
if (hours == 0)
{
return System.String.Format ("{0:00}:{1:00.00}", minutes, secondsRemainder);
}
}
return System.String.Format ("{0}:{1:00}:{2:00}", hours, minutes, secondsRemainder);
}
public static string DistanceFormatter( float meters)
{
if (meters < 1000)
{
return System.String.Format ("{0}m", (int)meters);
}
float kilometers = meters / 1000.0f;
return System.String.Format ("{0:0.0}km", kilometers);
}
public static string LandspeedFormatter( float meters, float seconds)
{
float kmh = (meters * 3600) / (seconds * 1000);
return System.String.Format ("{0:0.0}km/h", kmh);
}
public static string TotalPlayTimeFormatted()
{
return TimeFormatter( Statistics.TotalPlayTime, forceHHMMSS: true);
}
public static string TotalDistanceFlownFormatted()
{
return DistanceFormatter( Statistics.TotalDistanceFlown);
}
public static string LifetimeLandspeedFormatted()
{
return LandspeedFormatter (Statistics.TotalDistanceFlown, Statistics.TotalPlayTime);
}
@kurtdekker
Copy link
Author

See notes in first file.

The idea is only in-memory variables are changed at runtime, for obvious performance reasons.

When the game ends and the scene changes, OnDisable() is called and the variables are flushed out to persistent storage.

Variables are reset when the game starts (code not shown).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment