Select Git revision
SFGameInstance.cpp

Jonathan Ehret authored
SFGameInstance.cpp 25.01 KiB
// Fill out your copyright notice in the Description page of Project Settings.
#include "SFGameInstance.h"
#include "IUniversalLogging.h"
#include "HUD/SFFadeHandler.h"
#include "HUD/SFMasterHUD.h"
#include "HUD/SFGlobalFadeGameViewportClient.h"
#include "Help/SFUtils.h"
#include "Logging/SFLoggingBPLibrary.h"
#include "Logging/SFLoggingUtils.h"
USFGameInstance* USFGameInstance::Instance = nullptr;
// ****************************************************************** //
// ******* Initialization ******************************************* //
// ****************************************************************** //
void USFGameInstance::Init()
{
Super::Init();
FSFLoggingUtils::Log("USFGameInstance::Init");
GEngine->GameViewportClientClass = USFGlobalFadeGameViewportClient::StaticClass();
IDisplayClusterClusterManager* ClusterManager = IDisplayCluster::Get().GetClusterMgr();
if (ClusterManager && !ClusterEventListenerDelegate.IsBound())
{
ClusterEventListenerDelegate = FOnClusterEventJsonListener::CreateUObject(this, &USFGameInstance::HandleClusterEvent);
ClusterManager->AddClusterEventJsonListener(ClusterEventListenerDelegate);
}
Instance = this;
LogObject = NewObject<USFLogObject>();
if (ConditionToStartAtInit)
{
FSFLoggingUtils::Log(
"Started on a map that was part of the last study, so start the study run for debug reasons from Init()");
RestoreLastParticipantForDebugStart(ConditionToStartAtInit);
}
if (StudySetup)
{
InitFadeHandler(StudySetup->FadeConfig);
}
// Register delegate for ticker callback
TickDelegateHandle = FTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateUObject(this, &USFGameInstance::LogTick));
LogObject->Initialize();
bStudyStarted = false;
bStudyEnded = false;
}
void USFGameInstance::Shutdown()
{
IDisplayClusterClusterManager* ClusterManager = IDisplayCluster::Get().GetClusterMgr();
if (ClusterManager && ClusterEventListenerDelegate.IsBound())
{
ClusterManager->RemoveClusterEventJsonListener(ClusterEventListenerDelegate);
}
if (ExperimenterWindow)
{
ExperimenterWindow->DestroyWindow();
}
Instance = nullptr;
// Unregister ticker delegate
FTicker::GetCoreTicker().RemoveTicker(TickDelegateHandle);
}
void USFGameInstance::OnWorldStart()
{
UWorld* NewWorld = GetWorld();
//when unloading map, NewWorld==nullptr, when loading new map OldWorld==nullptr
if (NewWorld == nullptr)
{
return;
}
if (bStudyStarted)
{
//everything is already up and running, so nothing to do
return;
}
FSFLoggingUtils::Log("USFGameInstance::OnWorldStart for " + NewWorld->GetName());
// so we have loaded a new world and the study is not running, so check whether this is a map in one of the conditions
TArray<USFCondition*> LastConditions = USFParticipant::GetLastParticipantsConditions();
USFCondition* FirstMapCondition = nullptr;
for (USFCondition* Condition : LastConditions)
{
if (FPaths::GetBaseFilename(Condition->Map).Equals(NewWorld->GetName()))
{
FirstMapCondition = Condition;
break;
}
}
if (FirstMapCondition)
{
if (!IsInitialized())
{
//this is potentially called before init was called
ConditionToStartAtInit = FirstMapCondition;
}
else
{
FSFLoggingUtils::Log(
"[USFGameInstance::OnWorldChanged] Started on a map that was part of the last study, so start the study run for debug reasons.");
RestoreLastParticipantForDebugStart(FirstMapCondition);
LogToHUD("Start map " + NewWorld->GetName() + " for debugging!");
}
return;
}
// otherwise check whether a setup is present
TArray<AActor*> StudySetups;
UGameplayStatics::GetAllActorsOfClass(NewWorld, ASFStudySetup::StaticClass(), StudySetups);
FSFLoggingUtils::Log("Found " + FString::FromInt(StudySetups.Num()) + " ASFStudySetup actors on this map.");
if (StudySetups.Num() == 1)
{
PrepareWithStudySetup(Cast<ASFStudySetup>(StudySetups[0]));
return;
}
// else we started on an unrelated map and just disable the HUD, so people can test their stuff
bStartedOnUnrelatedMap = true;
StudySetup = NewObject<ASFStudySetup>(this, TEXT("DefaultStudySetupForUnrelatedMap"));
InitFadeHandler(FFadeConfig());
FadeHandler->FadeIn();
FSFLoggingUtils::Log(
"[USFGameInstance::OnWorldChanged] world started that neither contains exactly one SFStudySetup actor, nor is a level that is part of one of the conditions from the last study run!",
false);
}
void USFGameInstance::RestoreLastParticipantForDebugStart(USFCondition* InStartCondition)
{
bInDebugMode = true;
const int ParticipantSequenceNumber = USFParticipant::GetLastParticipantSequenceNumber();
const FString ParticipantID = USFParticipant::GetLastParticipantID();
Participant = NewObject<USFParticipant>(this,
FName(TEXT("Participant_") + ParticipantID));
StudySetup = USFParticipant::GetLastParticipantSetup();
StartCondition = InStartCondition;
Participant->Initialize(ParticipantSequenceNumber, ParticipantID);
Participant->Conditions = Participant->GetLastParticipantsConditions();
Participant->LoadLastParticipantsIndependentVariables();
Participant->RecoverStudyResultsOfFinishedConditions();
//also delete any data of the condition that is now restarted (StartCondition)
Participant->DeleteStoredDataForConditionFromLongTable(StartCondition);
for (USFDependentVariable* DV : StartCondition->DependentVariables)
{
if (USFMultipleTrialDependentVariable* MTDV = Cast<USFMultipleTrialDependentVariable>(DV))
{
Participant->DeleteStoredTrialDataForCondition(StartCondition, MTDV);
}
}
InitFadeHandler(StudySetup->FadeConfig);
StartStudy();
//cannot directly fade in here, since Init might not be done, so we wait a second for safety
GetTimerManager().SetTimer(StartFadingTimerHandle, this, &USFGameInstance::StartFadingIn, 1.0f);
}
void USFGameInstance::StartFadingIn()
{
FadeHandler->FadeIn();
GetTimerManager().ClearTimer(StartFadingTimerHandle);
}
USFGameInstance* USFGameInstance::Get()
{
if (Instance == nullptr)
{
FSFUtils::OpenMessageBox("GameInstance is not set to USFGameInstance, Study Framework will not work", true);
}
return Instance;
}
bool USFGameInstance::IsInitialized()
{
return Instance != nullptr;
}
void USFGameInstance::InitFadeHandler(FFadeConfig FadeConfig)
{
if (FadeHandler == nullptr)
{
FadeHandler = NewObject<USFFadeHandler>(GetTransientPackage(), "SFFadeHandler");
FadeHandler->AddToRoot();
}
FadeHandler->SetFadeConfig(FadeConfig);
if (bStartedOnUnrelatedMap)
{
StudySetup->ExperimenterViewConfig.bShowHUD = false;
}
if (StudySetup->ExperimenterViewConfig.bShowExperimenterViewInSecondWindow)
{
ExperimenterWindow = NewObject<USFExperimenterWindow>(GetTransientPackage(), "ExperimenterWindow");
ExperimenterWindow->CreateWindow(StudySetup->ExperimenterViewConfig);
}
}
// When starting on the setup map, i.e. not for debugging purposes
void USFGameInstance::PrepareWithStudySetup(ASFStudySetup* Setup)
{
StudySetup = DuplicateObject(Setup, this);
int ParticipantSequenceNumber = USFParticipant::GetLastParticipantSequenceNumber();
FString LastParticipantID = USFParticipant::GetLastParticipantID();
TArray<USFCondition*> Conditions;
bool bRecoverParticipantData = false;
if (USFParticipant::GetLastParticipantFinished())
{
ParticipantSequenceNumber++;
Conditions = StudySetup->GetAllConditionsForRun(ParticipantSequenceNumber);
}
else
{
const FString MessageText = FString("The last participant did not finish the study run. Would you like to:") +
"\n[Continue Participant] Continue last participant (Participant ID: " +
LastParticipantID + ", SequenceNumber: " + FString::FromInt(ParticipantSequenceNumber) + ") where he/she left off in Condition " +
FString::FromInt(USFParticipant::GetLastParticipantLastConditionStarted() + 1) + "/" + FString::FromInt(USFParticipant::GetLastParticipantsConditions().Num()) +
"\n[Next Participant] Continue with the next participant (ParticipantSequenceNumber: " + FString::FromInt(ParticipantSequenceNumber + 1) +
")\n[Restart Study] Restart the entire study anew (ParticipantSequenceNumber: 0)";
const FString MessageTitle = "WARNING: Unfinished study run detected";
TArray<FString> Buttons = {
"Continue Participant",
"Next Participant",
"Restart Study"
};
int Answer = FSFUtils::OpenCustomDialog(MessageTitle, MessageText, Buttons);
switch (Answer)
{
case 2:
FSFLoggingUtils::Log("[USFGameInstance::PrepareWithStudySetup]: Restart entire study");
ParticipantSequenceNumber = 0;
Conditions = StudySetup->GetAllConditionsForRun(ParticipantSequenceNumber);
//clear data
USFParticipant::ClearLogData(Setup);
break;
case 0:
FSFLoggingUtils::Log("[USFGameInstance::PrepareWithStudySetup]: Continue last participant");
Conditions = USFParticipant::GetLastParticipantsConditions();
StartCondition = Conditions[USFParticipant::GetLastParticipantLastConditionStarted()];
bRecoverParticipantData = true;
break;
case 1:
FSFLoggingUtils::Log("[USFGameInstance::PrepareWithStudySetup]: Continue with the next participant");
ParticipantSequenceNumber++;
Conditions = StudySetup->GetAllConditionsForRun(ParticipantSequenceNumber);
break;
default:;
}
}
if (StudySetup->bPreloadAllMapsOnStart)
{
PreloadAllMaps(Conditions);
}
// Participant
FString ParticipantID = "";
if (Setup->bUseCustomParticipantIDs)
{
//if we arerecovering, restore it:
if (bRecoverParticipantData) {
ParticipantID = USFParticipant::GetLastParticipantID();
}
else {
//ask for it:
bool bValidIDEntered = false;
while (!bValidIDEntered)
{
FString IDGiven;
int Result = FSFUtils::OpenCustomDialogText("Participant ID", "Please type in your ID:", "", IDGiven);
if (Result < 0) {
FSFLoggingUtils::Log("[USFGameInstance::PrepareWithStudySetup] The window for entering the participant ID was closed without giving an answer, repeat question!", false);
continue;
}
//check whether it was used before
if (USFParticipant::WasParticipantIdAlreadyUsed(IDGiven))
{
FSFUtils::OpenMessageBox("Participant ID \"" + IDGiven + "\"was already used, you have to choose another one!");
}
else
{
ParticipantID = IDGiven;
bValidIDEntered = true;
}
}
}
}
else
{
//otherwise just use sequence number:
ParticipantID = FString::FromInt(ParticipantSequenceNumber);
}
Participant = NewObject<USFParticipant>(this, FName(TEXT("Participant_") + ParticipantID));
Participant->Initialize(ParticipantSequenceNumber, ParticipantID);
if (bRecoverParticipantData) {
Participant->LoadLastParticipantsIndependentVariables();
}
else {
Participant->SetIndependentVariablesFromStudySetup(Setup);
Participant->StoreInIndependentVarLongTable();
}
Participant->SetStudyConditions(Conditions);
if (bRecoverParticipantData)
{
Participant->RecoverStudyResultsOfFinishedConditions();
//also delete any data of the condition that is now restarted (StartCondition)
Participant->DeleteStoredDataForConditionFromLongTable(StartCondition);
for (USFDependentVariable* DV : StartCondition->DependentVariables)
{
if (USFMultipleTrialDependentVariable* MTDV = Cast<USFMultipleTrialDependentVariable>(DV))
{
Participant->DeleteStoredTrialDataForCondition(StartCondition, MTDV);
}
}
}
if (IsInitialized())
{
InitFadeHandler(Setup->FadeConfig);
}
UpdateHUD("Wait for Start");
}
void USFGameInstance::PreloadAllMaps(const TArray<USFCondition*>& Conditions)
{
TArray<FString> Maps;
for (const USFCondition* Condition : Conditions)
{
if (!Maps.Contains(Condition->Map))
{
Maps.Add(Condition->Map);
}
}
for (FString Map : Maps)
{
LoadPackage(nullptr, *Map, ELoadFlags::LOAD_None);
}
FSFLoggingUtils::Log("Sucessfully preloaded all maps.");
}
// ****************************************************************** //
// ******* Control Study ******************************************** //
// ****************************************************************** //
bool USFGameInstance::StartStudy()
{
if (bStudyStarted)
{
FSFLoggingUtils::Log("[USFGameInstance::StartStudy()]: Study already started.", true);
return false;
}
if (!bInDebugMode)
{
//we are actually doing a real start and not just a "debug-start"
if (!Participant->StartStudy())
{
FSFLoggingUtils::Log("[USFGameInstance::StartStudy()]: unable to start study.", true);
return false;
}
}
if (GetWorld()->GetFirstPlayerController() && Cast<ASFMasterHUD>(GetWorld()->GetFirstPlayerController()->GetHUD()))
{
ASFMasterHUD* HUD = Cast<ASFMasterHUD>(GetWorld()->GetFirstPlayerController()->GetHUD());
HUD->SetStartStudyButtonVisibility(ESlateVisibility::Collapsed);
HUD->SetNextConditionButtonVisibility(ESlateVisibility::Visible);
}
if (!StartCondition)
{
NextCondition();
}
else
{
GoToCondition(StartCondition);
}
//check whether we want to use the eye tracker
if (StudySetup && StudySetup->UseGazeTracker != EGazeTrackerMode::NotTracking)
{
GazeTracker = NewObject<USFGazeTracker>(this, TEXT("GazeTracker"));
GazeTracker->Init(StudySetup->UseGazeTracker, StudySetup->bIgnoreNonGazeTargetActors, StudySetup->EyeTrackingFrameRate);
}
return true;
}
void USFGameInstance::EndStudy()
{
USFCondition* LastCondition = Participant->GetCurrentCondition();
if (!LastCondition || !LastCondition->WasStarted() || LastCondition->IsFinished())
return;
LastCondition->End();
Participant->EndStudy();
UpdateHUD("Study ended");
if (GetHUD())
{
GetHUD()->SetNextConditionButtonVisibility(ESlateVisibility::Collapsed);
}
bStudyEnded = true;
FadeHandler->FadeOut();
}
bool USFGameInstance::NextCondition(bool bForced /*=false*/, EFadeBetweenCondition Fade /*= EFadeBetweenCondition::AsDefault*/)
{
// Check if is already fading
if (FadeHandler->GetIsFading() && !FadeHandler->GetIsFadedOutWaitingForLevel())
{
FSFLoggingUtils::Log("[USFGameInstance::NextCondition()]: Already Fading between levels", true);
return false;
}
USFCondition* NextCondition = Participant->GetNextCondition();
if (!NextCondition)
{
FSFLoggingUtils::Log("[SFGameInstance::NextCondition]: All conditions already ran, no NextCondition", false);
EndStudy();
return false;
}
return GoToCondition(NextCondition, bForced, Fade);
}
bool USFGameInstance::GoToCondition(const USFCondition* Condition, bool bForced /*=false*/, EFadeBetweenCondition Fade /*= EFadeBetweenCondition::AsDefault*/)
{
// Check if is already fading
if (FadeHandler->GetIsFading() && !FadeHandler->GetIsFadedOutWaitingForLevel())
{
FSFLoggingUtils::Log("[USFGameInstance::GoToCondition()]: Already Fading between levels", true);
return false;
}
if (!Condition || Condition->Map.Equals(""))
{
FSFLoggingUtils::Log("[USFGameInstance::GoToCondition()]: Could not load next condition.", true);
return false;
}
if (!bStudyStarted && !bIsStarting) {
StartCondition = Condition; //Start at this condition
bIsStarting = true; //To avoid infinite loops, when StartStudy calls GoToCondition(StartCondition)
return StartStudy();
}
GoToConditionSynced(Condition->UniqueName, bForced, Fade);
return true;
}
void USFGameInstance::GoToConditionSynced(FString ConditionName, bool bForced, EFadeBetweenCondition Fade)
{
const EDisplayClusterOperationMode OperationMode = IDisplayCluster::Get().GetOperationMode();
if (OperationMode != EDisplayClusterOperationMode::Cluster)
{
HandleGoToConditionSynced(ConditionName, bForced, Fade);
}
else
{
IDisplayClusterClusterManager* ClusterManager = IDisplayCluster::Get().GetClusterMgr();
check(ClusterManager != nullptr);
FDisplayClusterClusterEventJson ClusterEvent;
ClusterEvent.Type = "SFGameInstanceEvent";
ClusterEvent.Name = "GoToConditionSynced";
TMap<FString, FString> Params;
Params.Add("ConditionName", ConditionName);
Params.Add("bForced", bForced ? "true" : "false");
Params.Add("Fade", (Fade == EFadeBetweenCondition::AsDefault ? "AsDefault" : (Fade == EFadeBetweenCondition::ForceNoFade ? "ForceNoFade" : "ForceFade")));
ClusterEvent.Parameters = Params;
ClusterEvent.bShouldDiscardOnRepeat = true;
ClusterManager->EmitClusterEventJson(ClusterEvent, true);
}
}
void USFGameInstance::HandleClusterEvent(const FDisplayClusterClusterEventJson& Event) {
if (Event.Type == "SFGameInstanceEvent") {
//now we actually react on all cluster nodes:
if (Event.Name == "GoToConditionSynced")
{
EFadeBetweenCondition Fade = EFadeBetweenCondition::AsDefault;
if (Event.Parameters["Fade"] != "AsDefault")
{
if (Event.Parameters["Fade"] == "ForceNoFade")
{
Fade = EFadeBetweenCondition::ForceNoFade;
}
else if (Event.Parameters["Fade"] == "ForceFade")
{
Fade = EFadeBetweenCondition::ForceFade;
}
else
{
FSFLoggingUtils::Log("[USFGameInstance::HandleClusterEvent] Unknown Fade value: " + Event.Parameters["Fade"], true);
}
}
HandleGoToConditionSynced(Event.Parameters["ConditionName"], Event.Parameters["bForced"] == "true", Fade);
}
}
}
void USFGameInstance::HandleGoToConditionSynced(FString ConditionName, bool bForced, EFadeBetweenCondition Fade)
{
USFCondition* NextCondition = nullptr;
for (USFCondition* Condition : Participant->GetAllConditions())
{
if (Condition->UniqueName == ConditionName)
{
NextCondition = Condition;
}
}
USFCondition* LastCondition = Participant->GetCurrentCondition();
if (NextCondition->WasStarted() && bForced)
{
LogComment("Restarting Condition " + NextCondition->UniqueName + ", so we delete all gathered data for that condition.");
FString BackUpFolderName = "Restart_" + NextCondition->UniqueName + "_BackUp-" + FDateTime::Now().ToString();
Participant->SetCurrentBackUpFolderName(BackUpFolderName);
//so remove already stored data!
Participant->DeleteStoredDataForConditionFromLongTable(NextCondition);
for (USFDependentVariable* DV : NextCondition->DependentVariables)
{
if (USFMultipleTrialDependentVariable* MTDV = Cast<USFMultipleTrialDependentVariable>(DV))
{
Participant->DeleteStoredTrialDataForCondition(NextCondition, MTDV);
}
}
}
else if (LastCondition && LastCondition->WasStarted())
{
TArray<FString> UnfinishedVars = LastCondition->End();
if (UnfinishedVars.Num() != 0)
{
FString Missing = "Missing data for: {";
for (FString UnfinishedVar : UnfinishedVars)
{
Missing += UnfinishedVar + " ";
}
Missing += "}";
if (bForced)
{
FSFLoggingUtils::Log(
"[USFGameInstance::GoToCondition()]: Forced to go to next condition, but current one is not finished! " + Missing,
true);
}
else
{
FSFLoggingUtils::Log(
"[USFGameInstance::GoToCondition()]: Cannot go to next condition, since current one is not finished! " + Missing,
true);
return;
}
}
}
bool bConditionPresent = Participant->SetCondition(NextCondition);
if (!bConditionPresent)
{
FSFLoggingUtils::Log("[USFGameInstance::GoToCondition()]: Requested GoTo Condition seems not to exist.", true);
return;
}
// Fade to next Level
if (IsInitialized())
{
if (FadeHandler->GetIsFadedOutWaitingForLevel())
{
FadeHandler->SetLevelToLoad(NextCondition->Map);
}
else
{
FadeHandler->FadeToLevel(NextCondition->Map, Fade);
}
UpdateHUD("Fading out");
}
bStudyStarted = true;
}
bool USFGameInstance::IsStarted() const
{
return bStudyStarted;
}
bool USFGameInstance::IsInDebugMode() const
{
return bInDebugMode;
}
bool USFGameInstance::IsContinueStudyStart() const
{
return !bInDebugMode && StartCondition;
}
bool USFGameInstance::HasEnded() const
{
return bStudyEnded;
}
float USFGameInstance::GetCurrentTime() const
{
if (GetParticipant() && IsStarted())
{
return GetParticipant()->GetCurrentTime();
}
return 0.0f;
}
FString USFGameInstance::GetFactorLevel(FString FactorName) const
{
if (!Participant)
{
return "ParticipantNotSet";
}
if (Participant->GetCurrentCondition()->FactorLevels.Contains(FactorName))
{
return Participant->GetCurrentCondition()->FactorLevels[FactorName];
}
FSFLoggingUtils::Log("[USFGameInstance::GetFactorLevel()]: Factor " + FactorName + " seems not to exist!", true);
return "FactorNotPresent";
}
bool USFGameInstance::HasFactor(FString FactorName) const
{
if (Participant && Participant->GetCurrentCondition()->FactorLevels.Contains(FactorName))
{
return true;
}
return false;
}
FString USFGameInstance::GetIndependentVariableValue(FString VariableName) const
{
if (!GetParticipant())
{
return "";
}
return GetParticipant()->GetIndependentVariable(VariableName).Value;
}
FString USFGameInstance::GetCurrentPhase() const
{
if (!Participant)
{
return "ParticipantNotSet";
}
if (!Participant->GetCurrentCondition())
{
return "NoConditionActive";
}
return Participant->GetCurrentCondition()->PhaseName;
}
int USFGameInstance::GetCurrentConditionsSequenceNumber() const
{
if (!Participant) return -1;
return Participant->GetCurrentConditionNumber() + 1;
}
void USFGameInstance::LogToHUD(FString Text)
{
if (GetWorld()->GetFirstPlayerController() && Cast<ASFMasterHUD>(GetWorld()->GetFirstPlayerController()->GetHUD()) &&
Cast<ASFMasterHUD>(GetWorld()->GetFirstPlayerController()->GetHUD())->IsWidgetPresent())
{
Cast<ASFMasterHUD>(GetWorld()->GetFirstPlayerController()->GetHUD())->AddLogMessage(Text);
}
else
{
HUDSavedData.LogMessages.Add(Text);
}
//by default we also log this into the debug stream, so you can also check on older messages
FSFLoggingUtils::Log("[Logged to HUD] " + Text);
}
void USFGameInstance::UpdateHUD(FString Status)
{
if (GetHUD())
{
GetHUD()->UpdateHUD(Participant, Status);
}
else
{
HUDSavedData.Status = Status;
if (Participant)
HUDSavedData.Participant = Participant->GetID();
}
}
ASFMasterHUD* USFGameInstance::GetHUD()
{
if (GetWorld() && GetWorld()->GetFirstPlayerController())
{
return Cast<ASFMasterHUD>(GetWorld()->GetFirstPlayerController()->GetHUD());
}
return nullptr;
}
USFGazeTracker* USFGameInstance::GetGazeTracker() const
{
return GazeTracker;
}
FExperimenterViewConfig USFGameInstance::GetExperimenterViewConfig() const
{
if (StudySetup)
return StudySetup->ExperimenterViewConfig;
FSFLoggingUtils::Log("[USFGameInstance::GetExperimenterViewConfig] No StudySetup present, returning default ExperimenterViewConfig.", true);
return FExperimenterViewConfig();
}
USFExperimenterWindow* USFGameInstance::GetExperimenterWindow() const
{
return ExperimenterWindow;
}
void USFGameInstance::SetHUDVisible(bool bVisible)
{
if (GetHUD()) {
GetHUD()->SetHUDVisible(bVisible);
}
}
USFFadeHandler* USFGameInstance::GetFadeHandler()
{
return FadeHandler;
}
ASFStudySetup* USFGameInstance::GetStudySetup()
{
return StudySetup;
}
// ****************************************************************** //
// ******* Executing Study ****************************************** //
// ****************************************************************** //
void USFGameInstance::OnLevelLoaded()
{
//do we need to do something here?
UpdateHUD("Fading In");
}
void USFGameInstance::OnFadedIn()
{
FString NumberStr = FString::FromInt(GetCurrentConditionsSequenceNumber()) + "/" + FString::FromInt(Participant->GetAllConditions().Num());
if (Participant && Participant->GetCurrentCondition())
{
//this should be logged first
if (bInDebugMode) {
USFLoggingBPLibrary::LogComment("Running in DEBUG MODE");
}
USFLoggingBPLibrary::LogComment("Start Condition " + NumberStr + ": " + Participant->GetCurrentCondition()->GetPrettyName());
}
OnFadedInDelegate.Broadcast();
if (bStartedOnUnrelatedMap)
{
return;
}
Participant->GetCurrentCondition()->Begin();
UpdateHUD("Condition " + NumberStr);
}
USFParticipant* USFGameInstance::GetParticipant() const
{
return Participant;
}
// ****************************************************************** //
// ******* Logging ******************************************** //
// ****************************************************************** //
bool USFGameInstance::LogTick(float DeltaSeconds)
{
if (LogObject->GetLoggingLoopsActive()) {
LogObject->WritePositionLogToFile();
LogObject->WriteGazeTrackingLogToFile();
}
return true;
}
USFLogObject* USFGameInstance::GetLogObject()
{
return LogObject;
}
void USFGameInstance::LogComment(const FString& Comment, bool bAlsoLogToHUD /*= true*/)
{
FSFLoggingUtils::Log("Logged Comment: " + Comment);
if (!GetParticipant())
{
return;
}
UniLog.Log("#" + GetParticipant()->GetCurrentTimeAsString() + ": " + Comment, "ParticipantLog");
if (bAlsoLogToHUD)
{
LogToHUD(Comment);
}
}
void USFGameInstance::LogData(const FString& DependentVariableName, const FString& Value)
{
if (!GetParticipant())
{
return;
}
USFCondition* CurrCondition = GetParticipant()->GetCurrentCondition();
if (!CurrCondition->StoreDependentVariableData(DependentVariableName, Value))
{
return;
}
LogComment("Recorded " + DependentVariableName + ": " + Value);
//the data is stored in the phase long table on SetCondition() or EndStudy()
}
void USFGameInstance::LogTrialData(const FString& DependentVariableName, const TArray<FString>& Values)
{
if (!GetParticipant())
{
return;
}
USFCondition* CurrCondition = USFGameInstance::Get()->GetParticipant()->GetCurrentCondition();
if (!CurrCondition->StoreMultipleTrialDependentVariableData(DependentVariableName, Values))
{
return;
}
FString Data = "";
for (const FString& Value : Values)
{
Data += (Data.IsEmpty() ? "{" : ",") + Value;
}
Data += "}";
LogComment("Recorded " + DependentVariableName + ": " + Data);
}