FOS UI Culling
Version 1.0.1 · benötigt FOS Essentials Core 1.0.0 oder neuer
Deaktiviert Welt-UIs, wenn die lokale Person außer Reichweite ist, gesteuert von einem einzigen zentralen Manager. Benötigt FOS Essentials Core 1.0.0.
Einrichtung
Abschnitt betitelt „Einrichtung“- Erstelle ein leeres GameObject und füge FOS Essentials > UI Culling > UI Culling Manager hinzu.
- Klicke auf Scan the scene. Jeder World-Space-Canvas erhält ein
FOSUICullingTarget, seinCanvasund seinGraphicRaycasterwerden verdrahtet, und die Liste des Managers wird gefüllt. - Stelle Default distance ein. Überschreibe sie bei Bedarf je UI in der Ziel-Komponente.
Führe den Scan nach dem Hinzufügen neuer UIs erneut aus. Bestehende Ziele werden nie überschrieben, jede manuelle Feinabstimmung bleibt erhalten.
Komponenten
Abschnitt betitelt „Komponenten“| Komponente | Aufgabe |
|---|---|
FOSUICullingManager |
Einer je Szene. Besitzt die Schleife und steuert alles. |
FOSUICullingTarget |
Eines je UI. Hält nur Einstellungen — keine Arbeit pro Frame. |
Optionen
Abschnitt betitelt „Optionen“| Option | Wirkung |
|---|---|
| Default distance | Meter, unterhalb derer eine UI gezeigt wird, sofern das Ziel es nicht überschreibt. |
| Show distance (Ziel) | Überschreibung je UI. -1 bedeutet „den Wert des Managers erben”. |
| Distance reference (Ziel) | Punkt, von dem aus gemessen wird. Leer bedeutet der Canvas selbst. |
| Hysteresis | Zusätzliche Meter, bevor wieder verborgen wird. Verhindert Flackern, wenn jemand genau auf der Grenze steht. |
| Checks per frame | Wie viele UIs pro Frame geprüft werden. Standard 4. |
| Line of sight | Optionaler Verdeckungsdurchlauf. Standardmäßig aus. |
| Obstacle layers | Layer, die beim Verdeckungsdurchlauf als Hindernis gelten. |
Wie eine UI ihre Entfernung bestimmt
Abschnitt betitelt „Wie eine UI ihre Entfernung bestimmt“Zwei Ebenen, einmalig beim Start des Managers aufgelöst:
- Die Default distance des Managers gilt für jede UI.
- Die Show distance eines Ziels überschreibt sie — außer sie bleibt auf
-1, was den Wert des Managers erben bedeutet.
float distance = target.showDistance;if (distance < 0f) distance = defaultShowDistance;Eine Szene mit Default distance = 15 und einem Ziel auf 8 zeigt diese eine UI also bei
8 Metern und alles andere bei 15.
Der Inspektor des Ziels schreibt das Ergebnis aus — „Effective distance: 15 m (inherited
from the manager)“ — damit du dir nie merken musst, was -1 bedeutet.
Da der Wert einmalig im Start gelesen wird, erfordert eine Änderung zur Laufzeit
einen Aufruf von RefreshTargets() am Manager.
Von wo aus gemessen wird
Abschnitt betitelt „Von wo aus gemessen wird“Vom Transform des Canvas, bei einem Panel also von seinem Pivot, meist der Mitte. Ein breites Panel erscheint daher nach seiner Mitte und nicht nach seiner nächsten Kante.
Wenn das zählt, ziehe irgendein Transform in Distance reference am Ziel — etwa ein leeres Kind an der Kante — und die Messung nutzt stattdessen dieses.
Entwurfsnotizen
Abschnitt betitelt „Entwurfsnotizen“Warum ein zentraler Manager statt eines Skripts je UI
Abschnitt betitelt „Warum ein zentraler Manager statt eines Skripts je UI“Udon ist eine interpretierte VM, rund hundertmal langsamer als C#. Die beherrschenden Kosten sind die Anzahl tickender Verhalten, nicht die Rechnerei darin. Ein Verhalten, das dreißig quadrierte Entfernungen vergleicht, ist weit billiger als dreißig Verhalten, die überhaupt irgendetwas tun.
FOSUICullingTarget hält deshalb nur Konfiguration. Der Manager liest sie einmal im
Start, legt sie in flachen Arrays ab und rührt danach nur noch Unity-Komponenten direkt
an. Dreißig UIs kosten genau ein aktives Verhalten.
Warum Entfernung und kein Raycast
Abschnitt betitelt „Warum Entfernung und kein Raycast“Ein Strahl beantwortet „steht etwas im Weg”, nicht „bin ich nah”. Er kann den Entfernungstest nicht ersetzen — man bräuchte ihn ohnehin zuerst. Raycasting hat hier einen echten Nutzen, aber als zweite Stufe: siehe Line of sight unten.
Warum Entfernung und keine Trigger-Collider
Abschnitt betitelt „Warum Entfernung und keine Trigger-Collider“Trigger-Volumen sind wirklich günstig: ereignisgesteuert, die Broadphase läuft in nativem Code. Sie verlieren an drei praktischen Punkten:
- ein Collider muss je UI dimensioniert werden, was die automatische Einrichtung zunichtemacht;
OnPlayerTriggerEnterfeuert nur am GameObject mit dem Collider, was ein Verhalten je UI erzwingt, um es zu empfangen;- die Spielerkapsel muss das Volumen tatsächlich überlappen. Eine UI hoch an der Wand mit kleinem Trigger wird verfehlt, und ein Fehler in der Layer-Matrix scheitert lautlos.
Was tatsächlich deaktiviert wird
Abschnitt betitelt „Was tatsächlich deaktiviert wird“Der Canvas, der Raycaster und die Collider — nicht SetActive(false).
- Den
Canvaszu deaktivieren stoppt das Rendern sofort und hält die Hierarchie am Leben. - Die Collider zu deaktivieren ist das, was den Zeiger von VRChat stoppt. Dieser Schritt ist nicht optional: siehe unten.
- Den
GraphicRaycasterzu deaktivieren nimmt die UI aus Unitys UI-Ereignisauflösung. SetActive(false)würde beim Zurückschalten einen vollständigen Layout-Neuaufbau erzwingen und jedesOnEnableerneut ausführen, Udon eingeschlossen, was den Zustand unbeteiligter Skripte zurücksetzen kann.
Warum die Collider wichtiger sind als der Raycaster
Abschnitt betitelt „Warum die Collider wichtiger sind als der Raycaster“Der Zeiger von VRChat läuft nicht über den GraphicRaycaster. Sein eigener Raycaster
macht einen Physik-Raycast und sucht dann ein VRC_UiShape an dem getroffenen
Collider:
int hitCount = Physics.RaycastNonAlloc(...); // trifft COLLIDERVRC_UiShape shape = hitObject.GetComponent<VRC_UiShape>();Nur den Canvas zu deaktivieren verbirgt die UI also, lässt aber ihren Collider stehen.
Der Zeiger rastet weiterhin darauf ein, und ihre Schaltflächen bleiben anklickbar —
eine unsichtbare UI, die noch reagiert. Das ist ein funktionaler Fehler, kein kosmetischer.
Der Scan füllt die Liste Colliders automatisch aus dem GameObject des Canvas. Hat eine UI Collider anderswo, trage sie von Hand nach; der Inspektor des Ziels warnt, wenn er nicht gelistete Collider findet.
Colliderleitet nicht vonBehaviourab — er leitet direkt vonComponentab, mit eigenerenabled-Eigenschaft. Deshalb haben Collider eine eigene Liste, statt unter Extra components zu stehen.
Die Sichtlinie
Abschnitt betitelt „Die Sichtlinie“Ist sie aktiv, wird eine bereits in Reichweite befindliche UI verborgen, wenn ein
Hindernis die Sicht versperrt. Ein Physics.Linecast je geprüfter UI, vom Kopf aus
geworfen — von den Füßen aus würde er den Boden streifen und jede Unebenheit erwischen.
Er läuft nur für UIs, die den Entfernungstest bereits bestanden haben; eine UI in zweihundert Metern kostet also nie einen Strahl. Lass ihn dennoch aus, sofern die Welt ihn nicht wirklich braucht.
Die Staffelung
Abschnitt betitelt „Die Staffelung“Ziele werden reihum geprüft, Checks per frame auf einmal. Bei 4 Prüfungen und 40 UIs beträgt die Latenz im schlimmsten Fall 10 Frames — weniger als eine Fünftelsekunde, unsichtbar für eine UI, die in der Ferne auftaucht.
Der erste Frame ist die Ausnahme: Dort wird alles auf einmal ausgewertet, sonst blieben entfernte UIs beleuchtet, bis der Zeiger eine volle Runde gedreht hat.
Sichtbarkeit wird nur geschrieben, wenn sie sich ändert; eine stabile Szene führt also null Komponentenschreibvorgänge aus.
Hinweise und Grenzen
Abschnitt betitelt „Hinweise und Grenzen“- Ein Manager je Szene. Mehrere Manager würden sich um dieselben Canvas streiten; der Inspektor meldet das als Fehler.
- Verschachtelte Canvas werden übersprungen. Den Wurzel-Canvas zu deaktivieren hält seine Kinder ohnehin vom Zeichnen ab; ein Ziel je Ebene würde nur die Liste zumüllen.
- Screen-Space-Canvas werden übersprungen, standardmäßig: Sie werden in einer VRChat-Welt niemandem angezeigt.
- Zur Laufzeit erzeugte UIs werden nicht automatisch erfasst. Fülle
targetsund rufeRefreshTargets()auf. ShowAll()schaltet zum Debuggen alles wieder ein, ohne den Manager zu entfernen.
