using System.Collections; using UnityEngine; using UnityEngine.UI; using UnityEngine.Networking; // Rendus du design affiches dans la page VIEWER (kitchengo). // Les images sont publiees sur https://ukitchenit.com/go/viewer/.png, // n allant de 1 a 4 : seule la premiere est attendue, les autres sont optionnelles // (le serveur repond 404 tant qu'elles n'existent pas, on passe simplement a la suivante). public static class ViewerImages { const string BaseUrl = "https://ukitchenit.com/go/viewer/"; const int MaxImages = 4; const float MaxWidth = 900f;// largeur max d'une vignette dans le Scroll View const float StartDelay = 4f;// animation seule avant la toute premiere verification const float RetryDelay = 10f;// nouvel essai sur l'image 1 toutes les 10 s // Adresse forcee pour la demo : les images du serveur sont nommees d'apres elle. // Mettre "" pour reprendre le courriel saisi par le client. const string DemoEmail = "hb3d@gmail.com"; const string SpinnerName = "ViewerWait"; const float SpinnerY = 75f;// hauteur du sablier dans le panneau // Chaque appel a Load() ouvre une "generation" : une relance par le bouton Resent doit // faire abandonner la precedente, sinon deux boucles interrogeraient le serveur en // parallele et ajouteraient les images en double. static int RunId; public static void Load() { RunId++; StaticCoroutine.Start(LoadRoutine(RunId)); } static IEnumerator LoadRoutine(int Id) { // Le sablier est pose avant tout le reste. ShowWait(true); Transform Content = ContentTransform(); if (Content == null) { Debug.LogError("[Viewer] VIEWER/Scroll View/Viewport/Content introuvable."); ShowWait(false); yield break; } // On repart d'un contenu vide : le panneau peut etre rouvert plusieurs fois. for (int i = Content.childCount - 1; i >= 0; i--) UnityEngine.Object.Destroy(Content.GetChild(i).gameObject); string Email = ClientEmail(); if (Email == "") { Debug.LogWarning("[Viewer] Aucun courriel client : rien a charger."); ShowWait(false); yield break; } // L'animation tourne d'abord StartDelay secondes : le rendu vient a peine d'etre // demande au serveur, inutile d'interroger l'adresse tout de suite. yield return new WaitForSeconds(StartDelay); // Puis on patiente sur l'image 1, sablier toujours a l'ecran, en reessayant // toutes les RetryDelay secondes jusqu'a ce qu'elle existe. Texture2D First = null; while (First == null) { if (Id != RunId) yield break;// une relance a pris le relais, elle gere l'affichage if (!ViewerIsOpen())// l'utilisateur a quitte la page : on n'interroge plus le serveur { ShowWait(false); yield break; } yield return Download(Email, 1, Tex => First = Tex); if (First != null) break; yield return new WaitForSeconds(RetryDelay); } if (Id != RunId) yield break; ShowWait(false);// l'image 1 est la : fin de l'animation AddImage(Content, First, 1); int Found = 1; // Les suivantes sont optionnelles : une seule tentative chacune. for (int n = 2; n <= MaxImages; n++) { Texture2D Tex = null; yield return Download(Email, n, T => Tex = T); if (Tex == null) continue; AddImage(Content, Tex, n); Found++; } Debug.Log("[Viewer] " + Found + " image(s) chargee(s) pour " + Email); } // Telecharge .png. Renvoie null via OnDone si le serveur ne l'a pas (404). static IEnumerator Download(string Email, int n, System.Action OnDone) { string Url = BaseUrl + Email + n + ".png"; using UnityWebRequest Req = UnityWebRequestTexture.GetTexture(Url); yield return Req.SendWebRequest(); if (Req.result != UnityWebRequest.Result.Success) { Debug.Log("[Viewer] " + Url + " => " + Req.responseCode); OnDone(null); yield break; } OnDone(DownloadHandlerTexture.GetContent(Req)); } // Vrai tant que la page VIEWER est celle affichee. static bool ViewerIsOpen() { GameObject Viewer = Get.o2("Canvas", "VIEWER"); return Viewer != null && Viewer.activeInHierarchy; } // ---------- Animation d'attente ---------- // Affiche/masque le sablier et le message "design en preparation". static void ShowWait(bool On) { GameObject Viewer = Get.o2("Canvas", "VIEWER"); if (Viewer == null) return; Transform Spinner = Viewer.transform.Find(SpinnerName); if (Spinner == null) { if (!On) return; Spinner = BuildSpinner(Viewer.transform); } Spinner.gameObject.SetActive(On); // Rien a faire tant que le design n'est pas affiche : les boutons reviennent avec lui. Transform Buttons = Viewer.transform.Find("BUTTONS"); if (Buttons != null) Buttons.gameObject.SetActive(!On); Transform Message = Viewer.transform.Find("Message"); if (Message != null && Message.TryGetComponent(out Text Label)) { Label.color = Color.red; Label.text = On ? (_G.L == 1 ? "Your design is being prepared..." : "Votre design est en préparation...") : ""; } } // Sablier construit en code : huit points en cercle, d'opacite decroissante, // le tout mis en rotation par ViewerSpinner. Aucun asset a fournir. static Transform BuildSpinner(Transform Parent) { GameObject Root = new(SpinnerName, typeof(RectTransform)); Root.transform.SetParent(Parent, false); RectTransform Rt = (RectTransform)Root.transform; Rt.anchorMin = Rt.anchorMax = Rt.pivot = new Vector2(0.5f, 0.5f); Rt.anchoredPosition = new Vector2(0f, SpinnerY); Rt.sizeDelta = new Vector2(80f, 80f); const int Dots = 8; const float Radius = 30f; for (int i = 0; i < Dots; i++) { float Angle = i * Mathf.PI * 2f / Dots; GameObject Dot = new("Dot" + i, typeof(RectTransform), typeof(CanvasRenderer), typeof(Image)); Dot.transform.SetParent(Root.transform, false); RectTransform DotRt = (RectTransform)Dot.transform; DotRt.sizeDelta = new Vector2(12f, 12f); DotRt.anchoredPosition = new Vector2(Mathf.Cos(Angle) * Radius, Mathf.Sin(Angle) * Radius); Image Img = Dot.GetComponent(); Img.color = new Color(0.4f, 0.4f, 0.4f, 1f - i / (float)Dots);// tramee = effet de rotation Img.raycastTarget = false; } Root.AddComponent(); return Root.transform; } // ---------- Images ---------- // Cree une Image enfant de Content a partir de la texture telechargee. static void AddImage(Transform Content, Texture2D Tex, int n) { GameObject Go = new("ViewerImage" + n, typeof(RectTransform), typeof(CanvasRenderer), typeof(Image)); Go.transform.SetParent(Content, false); Image Img = Go.GetComponent(); Img.sprite = Sprite.Create(Tex, new Rect(0, 0, Tex.width, Tex.height), new Vector2(0.5f, 0.5f)); Img.preserveAspect = true; // Taille posee a la main : utile tant qu'aucun layout group ne pilote Content. // Un LayoutGroup sur Content, lui, ecrasera ces valeurs, c'est le comportement voulu. float Scale = Tex.width > MaxWidth ? MaxWidth / Tex.width : 1f; ((RectTransform)Go.transform).sizeDelta = new Vector2(Tex.width * Scale, Tex.height * Scale); } // Le courriel saisi dans SENDING. ClientInfo est rempli par Send.SaveWEB ; on retombe sur // le formulaire si VIEWER est ouvert sans etre passe par la. static string ClientEmail() { // Demo : les rendus publies sur le serveur portent ce nom, on ignore le formulaire. if (DemoEmail != "") return DemoEmail; if (_G.ClientInfo != null && _G.ClientInfo.TryGetValue("Email", out string Mail) && !string.IsNullOrEmpty(Mail)) return Mail.Trim(); if (_G.Form != null && _G.Form.Count > 2 && _G.Form[2] != null) return (_G.Form[2].text ?? "").Trim(); return ""; } // Transform.Find traverse les enfants inactifs, contrairement a GameObject.Find. static Transform ContentTransform() { GameObject Viewer = Get.o2("Canvas", "VIEWER"); return Viewer != null ? Viewer.transform.Find("Scroll View/Viewport/Content") : null; } }