Premier commit du cinquième front. Trois parties : - `unity/MyInfoMateVR/` : le projet Unity (6000.0.83f1, URP, Meta XR SDK 205), un APK unique pour tous les clients. Menu flottant à sélection au regard, appairage, chargement de scène GLB, POI, cache de contenu, télémétrie. - `unity-overlay/` : les mêmes scripts à recopier sur un projet Unity neuf, avec les pièges rencontrés consignés dans son README. - `viewer/` : viewer et éditeur de scène web autonome (Vite, TypeScript, three.js), partagé avec les autres fronts. - `docs/` : état des lieux, setup Unity, décisions d'architecture et plan d'exécution en 9 étapes. La scène est décrite par un `scene.json` poussé par `adb push` : l'app le préfère à celui embarqué dans l'APK. Les binaires (GLB, textures de l'échantillon Sponza, DLL Meta XR) passent par Git LFS dès ce premier commit — les y faire entrer après coup demanderait de réécrire l'historique. Les artefacts régénérés par l'éditeur et par CMake (`Library/`, `.utmp/`, Burst debug) sont ignorés. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
84 lines
3.4 KiB
C#
84 lines
3.4 KiB
C#
using UnityEngine;
|
|
|
|
namespace MyInfoMate.Vr.Scene
|
|
{
|
|
/// <summary>
|
|
/// Zone de navigation circulaire, plafonnée à 3 m de rayon (§3.2 du cahier des
|
|
/// charges). Le plafond est appliqué ici <i>et</i> côté serveur : un client qui
|
|
/// fait confiance à la donnée est un client qu'on casse avec une donnée fausse.
|
|
///
|
|
/// Deux rôles : matérialiser la limite comme le garde-fou natif du Quest, et
|
|
/// ramener le visiteur à l'intérieur s'il en sort. On ne téléporte pas — on
|
|
/// repousse : une téléportation surprise dans un casque donne la nausée.
|
|
/// </summary>
|
|
public class NavigationBounds : MonoBehaviour
|
|
{
|
|
public const float MaxRadiusMeters = 3f;
|
|
|
|
[SerializeField] float radiusMeters = 3f;
|
|
[SerializeField] Transform visitor;
|
|
[SerializeField] Transform head;
|
|
[SerializeField] float fadeStartMeters = 0.4f;
|
|
|
|
LineRenderer _ring;
|
|
|
|
public void Configure(Transform visitorRig, Transform visitorHead, float radius, bool showBoundary)
|
|
{
|
|
visitor = visitorRig;
|
|
head = visitorHead;
|
|
radiusMeters = Mathf.Min(radius, MaxRadiusMeters);
|
|
if (showBoundary) DrawRing();
|
|
}
|
|
|
|
void DrawRing()
|
|
{
|
|
const int segments = 96;
|
|
|
|
_ring = gameObject.AddComponent<LineRenderer>();
|
|
_ring.useWorldSpace = false;
|
|
_ring.loop = true;
|
|
_ring.positionCount = segments;
|
|
_ring.widthMultiplier = 0.015f;
|
|
_ring.material = new Material(Shader.Find("Universal Render Pipeline/Unlit"));
|
|
_ring.material.color = new Color(0.4f, 0.8f, 1f, 0.6f);
|
|
|
|
for (var i = 0; i < segments; i++)
|
|
{
|
|
var angle = i * Mathf.PI * 2f / segments;
|
|
_ring.SetPosition(i, new Vector3(
|
|
Mathf.Cos(angle) * radiusMeters, 0.01f, Mathf.Sin(angle) * radiusMeters));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// On mesure la <b>tête</b> et on déplace le <b>rig</b>. En VR le rig ne bouge
|
|
/// pas quand le visiteur marche : le tracking applique le déplacement à la
|
|
/// caméra, à l'intérieur du rig. Mesurer le rig, c'est ne jamais rien mesurer —
|
|
/// la contrainte ne se déclenche alors pour aucune distance parcourue.
|
|
///
|
|
/// On retranche l'excédent au rig plutôt que de poser la tête sur le cercle :
|
|
/// la tête suit le corps, on ne peut que déplacer le repère sous elle.
|
|
/// </summary>
|
|
void LateUpdate()
|
|
{
|
|
if (visitor == null || head == null) return;
|
|
|
|
var local = transform.InverseTransformPoint(head.position);
|
|
var flat = new Vector2(local.x, local.z);
|
|
if (flat.magnitude <= radiusMeters) return;
|
|
|
|
var excess = flat - flat.normalized * radiusMeters;
|
|
visitor.position -= transform.TransformVector(new Vector3(excess.x, 0f, excess.y));
|
|
}
|
|
|
|
/// <summary>Distance à la limite, pour l'assombrissement progressif du bord.</summary>
|
|
public float ProximityToEdge()
|
|
{
|
|
if (head == null) return 0f;
|
|
var local = transform.InverseTransformPoint(head.position);
|
|
var distance = new Vector2(local.x, local.z).magnitude;
|
|
return Mathf.InverseLerp(radiusMeters - fadeStartMeters, radiusMeters, distance);
|
|
}
|
|
}
|
|
}
|