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>
126 lines
4.1 KiB
C#
126 lines
4.1 KiB
C#
// 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 (1–3). 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);
|
||
}
|
||
}
|
||
}
|