Skip to Content

Overriding Default GameState

The SDK provides a default game state implementation through UGameEngineHandlers. The interaction and scene fields give the agent basic context:

USTRUCT() struct FDummyGameState { GENERATED_BODY() UPROPERTY() bool _can_interact = true; UPROPERTY() FString current_scene; };

These fields serve specific purposes:

  • _can_interact: Tells the AI agent whether it can interact with the game
  • 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; // 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; };

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", "", 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; if (APlayerCharacter* Character = GetPlayerCharacter()) { // 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