// PanelWorldMenu.cs // A custom UI panel (CreatePanel) shown in WORLDSPACE: it stands in the world on a flat surface, so it works in VR as // well as on desktop and mobile. Point it at an object to have it float above (Kiosk), or leave Kiosk empty and it // stands at a spot you set. It has buttons, a per-player volume slider, a shared lights toggle, a category dropdown and // an inventory grid of things to pick. Everything a player does comes back here as that player (PanelEvent.Username). // // It can also show a live picture: paint a script canvas (CreateCanvas) on an object, slot that object in SignObject, // and the panel mirrors it live with AddLiveTexture. using System.Collections; using System.Collections.Generic; using IHXR.Simulation; public class PanelWorldMenu : ScriptBase { [Header("Where it stands")] [Tooltip("Float the panel above this object. Leave empty to stand it at Position below.")] public SceneObject Kiosk; public Vector3 Position = new Vector3(0f, 1.6f, 0f); [Tooltip("Local offset above the Kiosk object (metres).")] public Vector3 AboveKiosk = new Vector3(0f, 1.4f, 0f); [Range(0.3f, 6f)] public float WidthMeters = 1.4f; [Tooltip("Turn to face each viewer (good for a free-standing sign).")] public bool FaceViewer = true; [Header("Look")] public string Title = "Kiosk"; public Color TitleColor = new Color(0.8f, 0.95f, 1f); public PanelFont Font = PanelFont.Rounded; public Color AccentColor = new Color(0.3f, 0.85f, 1f); [Header("Items to pick")] public List ItemIcons = new List(); public List ItemNames = new List(); [Header("Live picture (optional)")] [Tooltip("An object a script canvas (CreateCanvas) is painted on: the panel mirrors it live.")] public SceneObject SignObject; private ScriptPanel panel; private PanelInventory grid; private PanelToggle lights; public override void Start() { panel = CreatePanel("kiosk", 420); panel.SetTitle(Title).SetTitleStyle(TitleColor, 24) .SetFont(Font, 16) .SetAccentColor(AccentColor) .SetLayout(1, 8, 16); // Stand it in the world. if (Kiosk != null && !Kiosk.IsNull) panel.PlaceOnObject(Kiosk, AboveKiosk, WidthMeters, FaceViewer); else panel.PlaceInWorld(Position, WidthMeters, FaceViewer); panel.AddButton("Say hello", e => SendScriptChat(e.Username + " waved from the kiosk.")); panel.AddSlider("Volume", 0f, 100f, 70f, e => Log(e.Username + " set volume " + (int)e.Value), 1f); lights = panel.AddToggle("Lights on", true, e => SendScriptChat("Lights turned " + (e.IsOn ? "on" : "off") + " by " + e.Username)); panel.AddDropdown("Category", new List { "Weapons", "Tools", "Wearables" }, 0, e => Log(e.Username + " browsing " + e.Text)); // A live picture of a painted object (optional). if (SignObject != null && !SignObject.IsNull) panel.AddLiveTexture(SignObject, 150); // An inventory grid: a cell per item. Clicking one comes back as that player with the cell index. panel.AddLabel("Pick one:"); grid = panel.AddInventory(4, 74, OnPick); RefreshGrid(); panel.ShowToEveryone(); } private void RefreshGrid() { var cells = new List(); for (int i = 0; i < ItemNames.Count; i++) { TextureItem icon = i < ItemIcons.Count ? ItemIcons[i] : null; cells.Add(new PanelCell(ItemNames[i], icon)); } if (cells.Count == 0) cells.Add(new PanelCell("Add items", null)); grid.SetCells(cells); } private void OnPick(PanelEvent e) { // e.Index is the cell, e.Text its label, e.Username who picked. if (e.Index < 0 || e.Index >= ItemNames.Count) return; SendScriptChat(e.Username + " picked " + e.Text + " from the kiosk."); } }