Statsの各項目を指定して出力するサンプル


# 概要
このコードはStatsコマンドで出力される内容を個別に指定してログに出力します。UAutomationBlueprintFunctionLibrary で定義された、`GetStatIncAverage` や `GetStatIncMax`といった関数を利用することもできますが、これらで取得できるは項目は一部に限定されます。このサンプルでは Counters Aggregates の項目も含める内容となります。

# 動作例
![2023-04-03_16h26_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101019/418cc94d-8174-7ac6-fe60-9e3b51208142.png)


**入力**
確認したいstatsのグループおよび項目をBlueprintから指定します。コンソールコマンドからstatsを実行(この場合stat netを実行)して、該当ノードをアプリケーションから呼び出します。
![2023-04-03_16h31_44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101019/31a9aed5-7bac-fb61-a5a9-d280c2e255b2.png)

**出力**
```
LogTemp: Name = STAT_OutBunches, IncAvg = 26.07, IncMin = 2.00, IncMaxMs = 40.00
```

# サンプル例
StatsRender2.cppを元に以下のような例が挙げられます。これを少し変更することで異なる型やExcMaxなどの値も取得できます。

```cpp:MyBlueprintFunctionLibrary.h
#include "MyBlueprintFunctionLibrary.h"

void UMyBlueprintFunctionLibrary::OutputStatsValue(const FName GroupName, const FName StatName)
{
	if (FGameThreadStatsData* StatsData = FLatestGameThreadStatsData::Get().Latest)
	{
		if (!StatsData)
		{
			return;
		}
		for (int32 GroupIndex = 0; GroupIndex < StatsData->ActiveStatGroups.Num(); ++GroupIndex)
		{
			const FActiveStatGroupInfo& StatGroup = StatsData->ActiveStatGroups[GroupIndex];

			if (!StatsData->GroupNames[GroupIndex].IsEqual(GroupName))
			{
				continue;
			}

			const bool bBudget = StatGroup.ThreadBudgetMap.Num() > 0;
			const int32 NumThreadsBreakdown = bBudget ? StatGroup.FlatAggregateThreadBreakdown.Num() : 1;
			TArray<FName> ThreadNames;
			StatGroup.FlatAggregateThreadBreakdown.GetKeys(ThreadNames);

			for (int32 ThreadBreakdownIdx = 0; ThreadBreakdownIdx < NumThreadsBreakdown; ++ThreadBreakdownIdx)
			{
				FName ThreadName;
				if (bBudget)
				{
					ThreadName = ThreadNames[ThreadBreakdownIdx];
				}
				const bool bHasFlat = !!StatGroup.FlatAggregate.Num();
				if (bHasFlat)
				{
					// Flat Aggregates
					TArray<FComplexStatMessage> Aggregates = bBudget ? StatGroup.FlatAggregateThreadBreakdown[ThreadName] : StatGroup.FlatAggregate;
					for (const auto& ComplexStat : Aggregates)
					{
						if (ComplexStat.NameAndInfo.GetShortName().IsEqual(StatName))
						{
							FString IncAveStr = GetStatValueString(ComplexStat, EComplexStatField::IncAve);
							FString IncMinStr = GetStatValueString(ComplexStat, EComplexStatField::IncMin);
							FString IncMaxStr = GetStatValueString(ComplexStat, EComplexStatField::IncMax);
							FString ComplaxStatName = ComplexStat.NameAndInfo.GetShortName().ToString();
							UE_LOG(LogTemp, Log, TEXT("Name = %s, IncAvg = %s, IncMin = %s, IncMaxMs = %s"), *ComplaxStatName, *IncAveStr, *IncMinStr, *IncMaxStr);
							return;
						}
					}
				}
			}

			// Counters Aggregates
			if (StatGroup.CountersAggregate.Num())
			{
				const TArray<FComplexStatMessage> Aggregates = StatGroup.CountersAggregate;
				for (const auto& ComplexStat : Aggregates)
				{
					if (ComplexStat.NameAndInfo.GetShortName().IsEqual(StatName))
					{
						FString IncAveStr = GetStatValueString(ComplexStat, EComplexStatField::IncAve);
						FString IncMinStr = GetStatValueString(ComplexStat, EComplexStatField::IncMin);
						FString IncMaxStr = GetStatValueString(ComplexStat, EComplexStatField::IncMax);
						FString ComplaxStatName = ComplexStat.NameAndInfo.GetShortName().ToString();
						UE_LOG(LogTemp, Log, TEXT("Name = %s, IncAvg = %s, IncMin = %s, IncMaxMs = %s"), *ComplaxStatName, *IncAveStr, *IncMinStr, *IncMaxStr);
						return;
					}				
				}
			}
		}
	}

	UE_LOG(LogTemp, Warning, TEXT("Not found stats name"));
}

FString UMyBlueprintFunctionLibrary::GetStatValueString(const FComplexStatMessage& ComplexStat, const EComplexStatField::Type Type)
{
	FString StatsValueString = "";
	if (ComplexStat.NameAndInfo.GetField<EStatDataType>() == EStatDataType::ST_double)
	{
		StatsValueString += FormatStatValueFloat(ComplexStat.GetValue_double(Type));
	}
	else if (ComplexStat.NameAndInfo.GetField<EStatDataType>() == EStatDataType::ST_int64)
	{
		StatsValueString += FormatStatValueInt64(ComplexStat.GetValue_int64(Type));
	}
	return StatsValueString;
}

FString UMyBlueprintFunctionLibrary::FormatStatValueFloat(const float Value)
{
	const float Frac = FMath::Frac(Value);
	const int32 Integer = FMath::FloorToInt(Value);
	const FString IntString = FString::FormatAsNumber(Integer);
	const FString FracString = FString::Printf(TEXT("%0.2f"), Frac);
	const FString Result = FString::Printf(TEXT("%s.%s"), *IntString, *FracString.Mid(2));
	return Result;
}

FString UMyBlueprintFunctionLibrary::FormatStatValueInt64(const int64 Value)
{
	const FString IntString = FString::FormatAsNumber((int32)Value);
	return IntString;
}
```