Skip to Content
SDK Integration
UnityExamplesCustom GameState

Overriding Default GameState

The game_state function is a crucial component that provides AI agents with essential information about the current game environment. This section explains how to customize the GameState to expose additional context to your AI agents.

Understanding the Default GameState

The SDK provides a default GameState implementation. The interaction and scene fields give the agent basic context:

[Serializable] private class DummyGameState { [SerializeField] private bool _can_interact = true; [SerializeField] private string current_scene = SceneManager.GetActiveScene().name; }
  • _can_interact: A boolean value that indicates whether the agent can interact with the game. If false, the agent will poll until the environment is ready for interaction again.
  • current_scene: A string value that stores the current scene name, that the agent can actually see.

Fields prefixed with an underscore (e.g., _can_interact) are private and used internally by the framework. They are NOT exposed directly to the agent.

The default implementation is registered as a volatile function, allowing for easy override:

[FlayerFunction("game_state", "returns minimal game state needed", volatileFunction: true)] [Preserve] public static string HandleGameState() { var gameState = new DummyGameState(); return JsonUtility.ToJson(gameState); }

Creating a Custom GameState

To provide richer context to your AI agent, you can override the default implementation with a custom one that includes game-specific information.

Here’s a comprehensive example of a custom GameState implementation:

#if USE_NUNU_SDK using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Nunu.Flayer; using UnityEngine; using UnityEngine.SceneManagement; namespace NunuFunctions { [FlayerFunction("game_state", "returns the actual game state")] [Preserve] public static string HandleGameState() { // create a new empty json object var gameState = new JObject(); // Set interaction state gameState["_can_interact"] = GameManager.Instance != null; // add custom fields that are exposed to the agent gameState["player_data"] = GetPlayerData(); gameState["inventory"] = GetInventory(); return JsonConvert.SerializeObject(gameState); } } #endif

Additional Fields

Additional fields can be added to reduce repetitive lookups in the agent. For example, you can expose the player’s health, inventory, or other game-specific data to the agent.

They can also be used to expose parameters that are needed to execute some of the action flayer functions, like:

  • If you have a navigate_to(x, y, z) function, expose coordinates of important locations in the game state ("quest_marker": {"x": 120.5, "y": 10.0, "z": 45.8})
  • For an equip_item(item_id) function, include a list of available items with their IDs ("equipment_options": [{"id": "sword_01", "name": "Iron Sword"}, ...])
  • If you have a cast_spell(spell_id, target_id) function, provide both spell IDs and potential target IDs

By including this contextual information, you help the agent make more informed decisions.

Best Practices

To verify your GameState override is working correctly:

  • Use the Flayer Debugger to inspect the returned JSON
  • Check that all expected fields are present and correctly formatted
  • Verify that the interaction state and public fields reflect the current game conditions

Example

#if USE_NUNU_SDK using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Nunu.Flayer; using UnityEngine; using UnityEngine.SceneManagement; namespace NunuFunctions { public static class CustomGameState { [FlayerFunction("game_state", "Returns detailed game state with player and environment information")] [Preserve] public static string HandleGameState() { // Create a JSON object to hold our game state var gameState = new JObject(); // Required framework fields gameState["_can_interact"] = GameManager.Instance != null && !Player.Instance.IsInCutscene; // Visible fields for the agent gameState["current_scene"] = SceneManager.GetActiveScene().name; gameState["current_menu"] = UIManager.Instance.CurrentMenuName ?? "NO_MENU"; gameState["in_combat"] = CombatSystem.Instance.InCombat; gameState["is_dead"] = Player.Instance.IsDead; gameState["player_health"] = Player.Instance.CurrentHealth; gameState["player_max_health"] = Player.Instance.MaxHealth; gameState["player_level"] = Player.Instance.Level; gameState["current_objective"] = QuestManager.Instance.CurrentObjectiveDescription; // Add nearby interactive objects gameState["nearby_interactables"] = GetNearbyInteractables(); // Add inventory information gameState["inventory"] = GetInventoryData(); // Log what we're returning Debug.Log("Providing updated game state to agent"); return JsonConvert.SerializeObject(gameState); } private static JArray GetNearbyInteractables() { var interactables = new JArray(); // Find all interactable objects within range foreach (var interactable in InteractionSystem.GetNearbyInteractables(5.0f)) { interactables.Add(new JObject { ["id"] = interactable.Id, ["type"] = interactable.Type, ["name"] = interactable.DisplayName, ["distance"] = Vector3.Distance(Player.Instance.transform.position, interactable.transform.position) }); } return interactables; } private static JObject GetInventoryData() { var inventory = new JObject(); // Add equipped items var equipped = new JObject(); foreach (var slot in InventorySystem.Instance.EquippedSlots) { if (slot.Value?.Item != null) { equipped[slot.Key] = slot.Value.Item.ItemId; } else { equipped[slot.Key] = null; } } inventory["equipped"] = equipped; // Add key inventory items var items = new JArray(); foreach (var item in InventorySystem.Instance.GetImportantItems()) { items.Add(new JObject { ["id"] = item.ItemId, ["name"] = item.DisplayName, ["count"] = item.Count }); } inventory["key_items"] = items; return inventory; } } } #endif
Last updated on