Nx10 Logo
Docs

4. SAAQ Prompts

Subjective Action-Anchored Questionnaires for ground-truth model training.

To truly understand human emotion, the Large Feelings Model (LFM) requires "ground truth" labels to calibrate against its kinematic observations. The Nx10 Control Plane actively monitors your incoming telemetry and decides when it is optimal to ask the user a question.

When this happens, the backend sends a trigger down to the Unity SDK. There are two integration paths for handling these triggers: Nx10 Managed UI and Custom Native UI.

Coming Soon

Path A: Managed UI

We provide a fully styleable, drop-in Unity Canvas prefab. The SDK handles rendering, timestamp anchoring, and payload submission with zero UI code required from your team.

Available Now

Path B: Custom UI

You build the UI using your own GameObjects and animations. The SDK simply passes you the question data and a callback function to return the user's answer.

The Art of the Prompt (Best Practices)

  • Smart Interruptions: You shouldn't pop up a UI while a player is mid-jump over a lava pit. If the trigger fires during intense gameplay, cache the request and display it the exact moment they hit a safe state, pause menu, or level-end screen.
  • Pause the Game: When the prompt is on screen, pause your game loop (e.g., Time.timeScale = 0f). You want the user to reflect on their feeling, not panic about dying in the background.
  • Incentivize Answers: Players are much more likely to answer truthfully if they get a micro-reward (e.g., 50 gold coins) upon completion.

Implementing Custom UI (Path B)

The SDK exposes a C# event that provides a SAAQPrompt object (containing the details of what to ask) and a function NX10Manager.Instance.SendPromptAnswer(SAAQAnswer answer, string displayTimestamp, string answerTimestamp, string triggerId). The function requires that you send the answer given by the player, aswell as timestamps for when the prompt was displayed and when it was answered, in ISO 8601 format, lastly the function requires you send the triggerId of the prompt given to you by the event.

The Data Contract

Currently, the system supports basic prompt types (type1 and type2). The data model is designed to expand in the future to support dynamic copy, localized strings, and multi-step forms.

Nx10.Models.SaaQ.cs
[Serializable]
    public class SAAQPrompt
    {
        public string triggerID;
        public string type;
        public string questionText;
        public List<SAAQAnswer> answers;
    }

[Serializable]
    public class SAAQAnswer
    {
        public string displayName;
        public string feelingsType;
        public string id;
        public string suggestedEmoji;
    }

The Implementation

SAAQManager.cs
using Nx10;
using Nx10.Models;
using System;
using UnityEngine;

public class SAAQManager : MonoBehaviour
{
    private bool usingNX10PromptUi;

    void OnEnable()
    {
        // Subscribe to the SDK's trigger event
        Nx10Manager.Instance.OnPromptRequested += Instance_OnPromptRequested;
    }

    void OnDisable()
    {
        Nx10Manager.Instance.OnPromptRequested -= Instance_OnPromptRequested;
    }

    private void Instance_OnPromptRequested(SAAQPrompt prompt)
    {
        // 1. Smart Interruption Check
        if (GameDirector.Instance.IsPlayerInActiveCombat()) 
        {
            // Defer showing the UI until combat ends
            StartCoroutine(WaitForSafeState(() => ShowPrompt(prompt)));
            return; 
        }

        ShowPrompt(prompt);
    }

    private void ShowPrompt(SAAQPrompt prompt)
    {
        // 1. Pause the game
        PauseManager.Instance.Pause();
        
        // 2. Get the timestamp when the prompt is displayed
        string promptDisplayTimestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ");

        if(usingNX10PromptUi)
        {
            // 3. If using the inbuilt solution, call it here.
            NX10Manager.Instance.ShowPrompt(prompt, (answer) =>
            {
                PromptAnswered(prompt, answer, promptDisplayTimestamp);
            });
        }
        else 
        {
            // 4. If using own solution call it here, Its recomended to invoke an event of type SAAQAnswer here but not required. NX10Manager.Instance.SendPromptAnswer() can be called from anywhere
            UIManager.Instance.ShowPromptMenu(prompt, (answer) =>
            {
                PromptAnswered(prompt, answer, promptDisplayTimestamp);
            });
        }
    }

    private void PromptAnswered(SAAQPrompt promptAnswered, SAAQAnswer answerGiven, string promptDisplaytimestamp)
    {
        // 1. Here we can unpause the game
        PauseManager.Instance.UnPause();

        // 2. Get the timestamp when the prompt is answered
        string promptAnswerTimestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ");

        // 3. Call SendPromptAnswer from the NX10Manager passing in the appropriate values
        NX10Manager.Instance.SendPromptAnswer(answerGiven, promptDisplayTimestamp, promptAnswerTimestamp, promptAnswered.triggerID);

        // 4. Reward the player
        GameDirector.Instance.AddCoins(100);
    }
}