using System.Threading.Tasks; using MyInfoMate.Vr.Scene; using UnityEngine; namespace MyInfoMate.Vr.Boot { /// /// Orchestration de l'étape S1 : décor, calibration, personnage, zone de navigation. /// /// Provisoire par construction. Les chemins sont ici en dur, parce que S1 /// n'a ni manifeste ni réseau — c'est exactement son intérêt : prouver la chaîne /// technique sans backend. S2 remplace ce script par SceneBuilder, qui lit /// les mêmes assets depuis un manifeste. Ne rien construire au-dessus de celui-ci. /// public class S1Bootstrap : MonoBehaviour { [Header("Fichiers dans Assets/StreamingAssets")] [SerializeField] string worldFile = "world.glb"; [SerializeField] string personaFile = "persona.glb"; [Header("Placement du personnage — coordonnées glTF, en mètres")] [SerializeField] float[] personaPosition = { -1.5f, 0f, -1f }; [Header("Navigation")] [SerializeField] float navigationRadiusMeters = 3f; [SerializeField] bool showBoundary = true; [SerializeField] bool runCalibration = true; Transform _sceneRoot; async void Start() { _sceneRoot = new GameObject("SceneRoot").transform; _sceneRoot.SetParent(transform, false); var head = Camera.main != null ? Camera.main.transform : null; if (head == null) Debug.LogError("[S1] Aucune caméra active : le rig VR n'est pas dans la scène. " + "Meta → Tools → Building Blocks → Camera Rig."); await LoadWorld(); if (runCalibration) await gameObject.AddComponent().RunAsync(_sceneRoot); await LoadPersona(head); SetUpBounds(head); } async Task LoadWorld() { var world = await GltfLoader.LoadAsync( GltfLoader.StreamingAssetsUrl(worldFile), _sceneRoot, "World"); if (world == null) Debug.LogError($"[S1] {worldFile} absent de StreamingAssets — " + "exporte un monde Marble en mesh GLB et dépose-le là."); } async Task LoadPersona(Transform head) { var holder = new GameObject("Persona"); holder.transform.SetParent(_sceneRoot, false); holder.transform.localPosition = GltfSpace.Position(personaPosition); var persona = holder.AddComponent(); if (!await persona.LoadAsync(GltfLoader.StreamingAssetsUrl(personaFile), head)) Debug.LogError($"[S1] {personaFile} absent de StreamingAssets."); } void SetUpBounds(Transform head) { // La zone suit le rig, pas la tête : c'est la position au sol du visiteur // qui compte, et la tête bouge de 60 cm quand on se penche. var rig = head != null ? head.root : null; var bounds = new GameObject("NavigationBounds").AddComponent(); bounds.transform.SetParent(_sceneRoot, false); bounds.Configure(rig, head, navigationRadiusMeters, showBoundary); } } }