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 gamecurrent_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