Skip to Content

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 matching
  • current_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 map
  • Hub_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