Overriding Default GameState
By default, the SDK provides a minimal game state implementation through UGameEngineHandlers:
USTRUCT()
struct FDummyGameState
{
GENERATED_BODY()
UPROPERTY()
bool _can_interact = true;
UPROPERTY()
FString _hint_key;
UPROPERTY()
FString current_scene;
};These fields serve specific purposes:
_can_interact: Tells the AI agent whether it can interact with the game_hint_key: Used for regex-based hint matchingcurrent_scene: The name of the current scene that the agent can actually see
Fields prefixed with underscore (like _can_interact) are used internally by the framework.
Creating a Custom Game State
Let’s look at a practical example of overriding the default game state with Lyra-specific information:
USTRUCT()
struct FLyraNunuGameState
{
GENERATED_BODY()
// Framework fields (required)
UPROPERTY()
bool _can_interact = true;
UPROPERTY()
FString _hint_key;
// Game-specific fields
UPROPERTY()
FString current_map;
UPROPERTY()
FString my_name;
UPROPERTY()
uint8 my_team;
UPROPERTY()
FString my_health;
UPROPERTY()
FString my_kda;
UPROPERTY()
TArray<FLyraWeapon> my_weapons;
UPROPERTY()
FString my_location;
UPROPERTY()
TArray<FLyraOtherPlayerState> other_players;
UPROPERTY()
TArray<FLyraSpawner> spawners;
};Working with Hint Keys
The hint key is crucial for contextual hint matching. You can create sophisticated hint keys by combining different game states:
// Basic hint key using just the map name
GameState._hint_key = CurrentLevelName;
// Advanced hint key combining multiple states
GameState._hint_key = FString::Printf(TEXT("%s_%s_%s_%s"),
*CurrentLevelName,
Character->IsDead() ? TEXT("DEAD") : TEXT("ALIVE"),
InCombat ? TEXT("COMBAT") : TEXT("PEACEFUL"),
*CurrentMenuState
);Example hint key patterns:
ShooterMap_ALIVE_COMBAT: For combat situations in the shooter mapHub_ALIVE_PEACEFUL_INVENTORY: When in the hub with inventory open*_COMBAT_*: Matches any combat situation in any map
Implementation Example
Here’s a full example of overriding the game state:
UCLASS()
class UNunuCoreHandlers : public UFlayerFunctionLibrary
{
GENERATED_BODY()
public:
UNunuCoreHandlers()
{
// Override the default game_state function
REGISTER_FLAYER_FUNCTION(
HandleGameState,
"game_state",
"Returns detailed game state",
".*", // Hint key pattern
false // Not directly exposed to AI
);
}
UFUNCTION()
FString HandleGameState(UPacketLogger* Logger)
{
FLyraNunuGameState GameState;
UWorld* World = GetWorld();
if (!World)
{
return TEXT("{}");
}
// Set basic state
FString CurrentLevelName = World->GetMapName();
GameState.current_map = CurrentLevelName;
// Build a rich hint key
if (APlayerCharacter* Character = GetPlayerCharacter())
{
FString CombatState = Character->IsInCombat() ? TEXT("COMBAT") : TEXT("PEACEFUL");
FString HealthState = Character->IsDead() ? TEXT("DEAD") : TEXT("ALIVE");
GameState._hint_key = FString::Printf(TEXT("%s_%s_%s"),
*CurrentLevelName,
*HealthState,
*CombatState
);
// Set interaction state
GameState._can_interact = !Character->IsDead() &&
!Character->IsInCutscene() &&
!Character->IsStunned();
// Get character stats
if (UHealthComponent* Health = Character->GetHealthComponent())
{
GameState.my_health = FString::Printf(TEXT("%.1f/%.1f"),
Health->GetCurrentHealth(),
Health->GetMaxHealth()
);
}
// Add weapon information
if (UInventoryComponent* Inventory = Character->GetInventoryComponent())
{
for (const auto& Weapon : Inventory->GetWeapons())
{
FLyraWeapon WeaponState;
WeaponState.weapon_name = Weapon->GetDisplayName();
WeaponState.ammo = FString::Printf(TEXT("%d/%d"),
Weapon->GetCurrentAmmo(),
Weapon->GetMaxAmmo()
);
WeaponState.equipped = (Weapon == Inventory->GetActiveWeapon());
GameState.my_weapons.Add(WeaponState);
}
}
}
// Process other players
TArray<AActor*> OtherPlayers;
UGameplayStatics::GetAllActorsOfClass(World, APlayerCharacter::StaticClass(), OtherPlayers);
for (AActor* Actor : OtherPlayers)
{
if (APlayerCharacter* OtherPlayer = Cast<APlayerCharacter>(Actor))
{
// Skip if this is our character
if (OtherPlayer == GetPlayerCharacter())
{
continue;
}
FLyraOtherPlayerState PlayerState;
PlayerState.player_name = OtherPlayer->GetPlayerName();
PlayerState.player_location = UFlayerFunctionUtils::VectorToString(
OtherPlayer->GetActorLocation()
);
// Add additional player info...
GameState.other_players.Add(PlayerState);
}
}
// Convert to JSON
FString JsonOutput;
FJsonObjectConverter::UStructToJsonObjectString(GameState, JsonOutput);
return JsonOutput;
}
};Last updated on