ipin-vr/ondevice/Scripts/SessionController.cs
Oskar Kapala 921a85d30b Add export subcommand and Unity PoC-1 scaffold (ONDEVICE §4)
Python (ipin_vr/export.py):
- generate_bank(): levels 1–2 enumerated exhaustively (120/720 combos),
  level 3 random with dedup; all deduplicated per-level
- run_export(): CLI --per-level / --out / --seed flags per ONDEVICE §4.1
- cli.py routes "export" subcommand before session args (backward-compat)
- 14 new tests: format, dedup for all 3 levels, space caps (L1=120, L2=720),
  seed determinism, file output

Unity scaffold (ondevice/Scripts/):
- CommandBank.cs: loads commands.json from StreamingAssets (WebRequest on Android)
- TtsManager.cs: sherpa-onnx integration with [SHERPA] stubs, StreamingAssets→
  persistentDataPath copy, defensive Speak() with onDone callback
- SessionController.cs: passthrough flow §6, level switching, busy guard on Next
- ondevice/README.md: full manual Editor steps §5 (Unity setup, Meta XR SDK,
  sherpa-onnx install, scene wiring, font, build, metrics)

pytest: 37/37 passed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 16:11:53 +02:00

126 lines
4.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// SessionController.cs — główna logika aplikacji (ONDEVICE §6):
// Start → wczytaj bank → ustaw poziom → pokaż losowe polecenie → TTS
// → trigger "następne" → pokaż kolejne.
//
// Samoistny MonoBehaviour; podepnij do dowolnego GameObject w scenie.
// Wymagane referencje do uzupełnienia w Inspectorze:
// • commandBank — GameObject z CommandBank.cs
// • ttsManager — GameObject z TtsManager.cs
// • commandText — komponent TextMeshProUGUI z tekstem polecenia
// • loadingPanel — panel widoczny podczas ładowania (opcjonalnie)
// • sessionPanel — panel widoczny podczas sesji
// Przyciski poziomu: w Inspectorze podepnij OnClick → SetLevel(1/2/3).
// Trigger "następne": podepnij OnClick → OnNextTrigger().
using System;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace IpinVr
{
public class SessionController : MonoBehaviour
{
[Header("Komponenty (wymagane)")]
[SerializeField] private CommandBank commandBank;
[SerializeField] private TtsManager ttsManager;
[SerializeField] private TMP_Text commandText; // TextMeshPro — wymaga pakietu com.unity.textmeshpro
[Header("Panele UI (opcjonalne)")]
[SerializeField] private GameObject loadingPanel;
[SerializeField] private GameObject sessionPanel;
[Header("Przycisk 'Następne' (opcjonalny — można też wołać OnNextTrigger() z kodu)")]
[SerializeField] private Button nextButton;
[Header("Ustawienia")]
[SerializeField] [Range(1, 3)] private int startLevel = 1;
private int _currentLevel;
private bool _busy; // podczas TTS nie przyjmujemy triggera
private void Start()
{
_currentLevel = startLevel;
SetPanelsVisible(loading: true);
if (nextButton != null)
nextButton.onClick.AddListener(OnNextTrigger);
if (commandBank == null || ttsManager == null)
{
Debug.LogError("[Session] Brakuje referencji CommandBank lub TtsManager w Inspectorze");
return;
}
commandBank.Load(() =>
ttsManager.Initialize(() =>
{
SetPanelsVisible(loading: false);
ShowNext();
})
);
}
private void OnDestroy()
{
if (nextButton != null)
nextButton.onClick.RemoveListener(OnNextTrigger);
}
// --- publiczne API ---
/// <summary>Trigger "następne polecenie" — z przycisku kontrolera lub UI.</summary>
public void OnNextTrigger()
{
if (!_busy)
ShowNext();
}
/// <summary>Zmień poziom (13). Wywoływane przez przyciski poziomu w UI.</summary>
public void SetLevel(int level)
{
if (level < 1 || level > 3)
{
Debug.LogWarning($"[Session] Nieprawidłowy poziom: {level}");
return;
}
_currentLevel = level;
Debug.Log($"[Session] Poziom → {level}");
if (!_busy)
ShowNext();
}
// --- logika wewnętrzna ---
private void ShowNext()
{
if (!commandBank.IsLoaded || !ttsManager.IsReady)
return;
string text = commandBank.GetRandom(_currentLevel);
if (text == null)
{
commandText.text = $"(brak poleceń dla poziomu {_currentLevel})";
return;
}
commandText.text = text;
_busy = true;
if (nextButton != null) nextButton.interactable = false;
ttsManager.Speak(text, onDone: () =>
{
_busy = false;
if (nextButton != null) nextButton.interactable = true;
});
}
private void SetPanelsVisible(bool loading)
{
if (loadingPanel != null) loadingPanel.SetActive(loading);
if (sessionPanel != null) sessionPanel.SetActive(!loading);
}
}
}