FOS Mirror
Version 1.0.1 · benötigt FOS Essentials Core 1.0.0 oder neuer
Schaltet Spiegel ein und aus — nach Entfernung, per Schaltfläche, per Interaktion — von einem einzigen zentralen Manager aus, mit Qualitäts-Voreinstellungen und einer richtungsweisenden Kostenschätzung. Benötigt FOS Essentials Core 1.0.0.
Spiegel sind das Erste, was die Bildrate einer VRChat-Welt ruiniert. Dieses Pack macht sie nicht billig — das kann nichts. Es macht sie kontrolliert: standardmäßig aus, an nur dort und dann, wo es jemand tatsächlich verlangt hat.
Einrichtung
Abschnitt betitelt „Einrichtung“- Erstelle ein leeres GameObject und füge FOS Essentials > Mirror > Mirror Manager hinzu.
- Klicke auf Scan the scene. Jeder VRChat-Spiegel erhält ein
FOSMirrorTarget, seine Spiegelkomponente und sein Manager werden verdrahtet, und die Liste des Managers wird gefüllt. - Entscheide den Mode: exklusiv, oder jeder Spiegel unabhängig.
- Füge dort ein Bedienelement hinzu, wo jemand einen Spiegel schalten können soll: Mirror Control (UI Button) oder Mirror Control (Interact).
Führe den Scan nach dem Hinzufügen neuer Spiegel erneut aus. Bestehende Ziele werden nie überschrieben, jede manuelle Feinabstimmung bleibt erhalten.
Komponenten
Abschnitt betitelt „Komponenten“| Komponente | Aufgabe |
|---|---|
FOSMirrorManager |
Einer je Szene. Besitzt die Schleife, den Entfernungstest und den Exklusivmodus. |
FOSMirrorTarget |
Eines je Spiegel. Hält Einstellungen und den Ein/Aus-Zustand — keine Arbeit pro Frame. |
FOSMirrorButton |
Schaltet einen Spiegel über einen UI-Button. Verdrahtet das OnClick für dich. |
FOSMirrorInteract |
Schaltet einen Spiegel, wenn jemand das tragende Objekt benutzt. |
Das Pack deklariert bewusst keinen eigenen Inspektor für VRC_MirrorReflection. Unity
akzeptiert nur einen je Typ, und VRWorldToolkit bringt bereits einen mit. Hier läuft alles
über die FOS-Komponente, sodass beide Werkzeuge nebeneinander weiterarbeiten.
Was tatsächlich geschaltet wird
Abschnitt betitelt „Was tatsächlich geschaltet wird“Die Komponente VRC_MirrorReflection, über ihr .enabled — nie das GameObject.
SetActive(false) würde jedes OnEnable der Hierarchie erneut ausführen, Udon
eingeschlossen, was den Zustand unbeteiligter Skripte zurücksetzen kann. Es würde außerdem
das Spiegelobjekt selbst verbergen — dabei ist ein Spiegel meist ein verzierter Rahmen, der
sichtbar bleiben soll, wenn er dunkel wird.
Extra components und Extra colliders werden mitgeschaltet.
Colliderleitet nicht vonBehaviourab — er leitet direkt vonComponentab, mit eigenerenabled-Eigenschaft. Deshalb haben Collider eine eigene Liste.
Setze niemals den Collider, mit dem ein Spiegel wieder eingeschaltet wird, in diese Liste: Er würde mit dem Spiegel deaktiviert, und der Spiegel ließe sich nie wieder einschalten. Der Inspektor des Ziels meldet diesen Fall als Fehler.
Wie ein ausgeschalteter Spiegel aussieht
Abschnitt betitelt „Wie ein ausgeschalteter Spiegel aussieht“Die Spiegelkomponente zu deaktivieren stoppt die Spiegelung, aber die Oberfläche wird weiter gezeichnet — und ein Spiegel-Shader ohne etwas zu spiegeln malt reines Schwarz. Ein ausgeschalteter Spiegel ist daher ein schwarzes Rechteck, sofern du nichts anderes sagst.
Zwei Felder am Ziel bestimmen das Aussehen:
| Mirror surface | Off material | Ergebnis |
|---|---|---|
| leer | — | Das schwarze Rechteck. Nichts wird angefasst. |
| gesetzt | leer | Die Oberfläche wird verborgen. Ihr Draw Call entfällt ebenfalls. |
| gesetzt | gesetzt | Die Oberfläche wechselt auf dieses Material: dunkles Glas, mattierte Scheibe, lackierte Platte. |
Der Scan füllt Mirror surface mit dem Renderer am Spiegel, Verbergen ist also das Standardverhalten. Führe den Scan in einer älteren Szene erneut aus, um das zu übernehmen, oder nutze die Schaltfläche Use the mirror’s renderer im Inspektor des Ziels.
Das Pack liefert ein fertiges Aus-Material mit, Materials/FOSMirrorOff.mat — eine dunkle,
glänzende Scheibe. Es nutzt Unitys Standard-Shader, damit das Material von nichts
außerhalb von Unity abhängt. Tausche seinen Shader auf Quest gegen einen mobilen wie
VRChat/Mobile/Standard Lite, oder lass Off material einfach leer: Die Oberfläche zu
verbergen kostet nichts und braucht überhaupt kein Material.
Der Wechsel schreibt renderer.material, nie sharedMaterial — sharedMaterial zur
Laufzeit zu schreiben verändert das Material-Asset auf der Festplatte, was in ClientSim
das Spiegelmaterial dauerhaft überschrieben in deinem Projekt zurückließe.
Rendererleitet vonComponentab, nicht vonBehaviour, und trägt eine eigeneenabled-Eigenschaft — derselbe Grund, aus dem Collider eine eigene Liste bekommen.
Das „Ein”-Material wird beim Weltstart vom Renderer erfasst. Lass das Aus-Material in der Szene nicht am Renderer zugewiesen: Es würde als Spiegel-Aussehen erfasst, und die Spiegelung käme nie zurück. Der Inspektor meldet diesen Fall als Fehler.
Das gilt auch für das Entfernungs-Culling, und das ist meist erwünscht: Ein Spiegel außer Reichweite verliert sowohl seine Spiegelung als auch seine Oberfläche.
Die zwei Hälften des Spiegelzustands
Abschnitt betitelt „Die zwei Hälften des Spiegelzustands“Ein Spiegel wird nur gezeichnet, wenn beide zutreffen:
| Hälfte | Besitzer | Gesetzt durch |
|---|---|---|
| Manueller Zustand — will ihn jemand an? | Das Ziel | Schaltfläche, Interaktion, startsOn |
| Reichweite — ist die Person nah genug? | Der Manager | Entfernungstest, Budget je Frame |
Ein Spiegel, den jemand ausgeschaltet hat, bleibt aus, so nah man auch herangeht. Die Reichweite schaltet nie von selbst einen Spiegel ein — sie lässt nur einen zurückkommen, der bereits an ist.
Die Entfernung
Abschnitt betitelt „Die Entfernung“Dieselbe Logik wie bei FOS UI Culling: quadrierte Entfernungen, zwei Schwellen, Prüfungen reihum, und ein Schreibvorgang nur, wenn sich der Zustand wirklich ändert.
| Option | Wirkung |
|---|---|
| Default distance | Meter, unterhalb derer ein Spiegel gezeichnet werden darf, sofern das Ziel es nicht überschreibt. |
| Show distance (Ziel) | Überschreibung je Spiegel. -1 bedeutet „den Wert des Managers erben”. |
| Distance reference (Ziel) | Punkt, von dem aus gemessen wird. Leer bedeutet der Spiegel selbst. |
| Hysteresis | Zusätzliche Meter, bevor wieder abgeschaltet wird. |
| Checks per frame | Wie viele Spiegel pro Frame geprüft werden. Standard 2. |
Der Inspektor des Ziels schreibt das Ergebnis aus — „Effective distance: 12 m (inherited
from the manager)“ — damit du dir nie merken musst, was -1 bedeutet.
Entfernungen werden einmalig im Start gelesen. Eine zur Laufzeit zu ändern erfordert
einen Aufruf von RefreshTargets() am Manager.
Ein gerade eingeschalteter Spiegel wird sofort gemessen, statt auf seinen Turnus zu warten: Direkt vor einem Spiegel zu stehen, der vier Frames zum Erscheinen braucht, wirkt kaputt.
Die Modi
Abschnitt betitelt „Die Modi“| Modus | Verhalten |
|---|---|
| Free | Jeder Spiegel ist unabhängig. Nichts hindert jemanden daran, alle einzuschalten. |
| Exclusive | Einen einzuschalten schaltet alle anderen aus. |
Der Exklusivmodus ist die wirksamste Einstellung dieses Packs, denn der zweite Kostenfaktor einer Welt ist, wie viele Spiegel gleichzeitig gezeichnet werden — und diese Zahl bestimmt sonst die erste neugierige Person.
Er greift auch beim Start: Sind mehrere Spiegel auf „startet an” gestellt, tut es nur der erste, und der Inspektor des Managers sagt das.
Schaltbereich, je Spiegel
Abschnitt betitelt „Schaltbereich, je Spiegel“| Bereich | Netzwerk | Wer zahlt |
|---|---|---|
| Local | Gar keines | Nur wer geschaltet hat |
| Synchronized | [UdonSynced], manuelle Synchronisierung |
Alle in der Instanz |
Local ist der Standard und die richtige Wahl für einen öffentlichen Spiegel: Wer den eigenen Avatar prüfen will, bekommt einen, und niemand sonst bezahlt dafür.
Synchronized ist für einen Spiegel gedacht, der zur Welt gehört — eine Tanzbühne, eine
gemeinsame Umkleide. Der Besitz wird vor dem Schreiben der Variablen übernommen, und der
Zustand wird für später Beitretende aus OnDeserialization wiederhergestellt.
Ein
SendCustomNetworkEventwird für spät Hinzukommende nicht wiederholt. Deshalb lebt der Zustand in einer synchronisierten Variablen und nicht in einem Ereignis.
Da der Zustand am Ziel gespeichert wird, lassen sich beide Bereiche in einer Szene frei mischen. Im Exklusivmodus solltest du wissen, was Mischen bedeutet: Einen lokalen Spiegel auszuschalten schaltet ihn nur für dich aus.
Manuelle Synchronisierung ist auf etwa eine Serialisierung pro Sekunde und Verhalten begrenzt. Einen synchronisierten Spiegel im Exklusivmodus einzuschalten schreibt auch in die übrigen synchronisierten Spiegel — mach also nicht ein Dutzend Spiegel synchronisiert, wenn nur einer es braucht.
Die Qualität
Abschnitt betitelt „Die Qualität“Zwei Einstellungen des VRChat-Spiegels liegen am Ziel offen: die gespiegelten Layer und das Abschalten der Pixel-Lichter.
| Voreinstellung | Gespiegelte Layer |
|---|---|
| Untouched (Standard) | Nichts wird angewandt; der Spiegel behält seine eigenen Einstellungen. |
| Players only | Player + MirrorReflection — Avatare vor der Skybox. |
| Players and world | Default + Player + Environment + MirrorReflection. |
| World only | Default + Environment. Kein Avatar wird je zweimal gezeichnet. |
| Nothing | Eine leere Maske. Der Spiegel zeichnet eine leere Spiegelung. |
| Custom | Was du von Hand in Reflected layers einstellst. |
Die Voreinstellung löst sich beim Auswählen sofort in das Feld Reflected layers auf —
die Maske, die du siehst, ist also die, die angewandt wird. Der Manager schreibt sie im
Start in den Spiegel; der Inspektor des Ziels bietet außerdem Apply to the mirror now,
um sie schon im Editor zu schreiben, praktisch, wenn die Prüfungen von VRWorldToolkit sie
sehen sollen.
Untouched existiert, weil ein Werkzeug niemals stillschweigend von Hand gemachte
Einstellungen überschreiben darf. Es wird nichts geschrieben, bis du eine Voreinstellung
wählst.
Der Inspektor warnt vor den klassischen Fehlern: die unangetastete Standardmaske,
gespiegeltes UiMenu, gespiegeltes PlayerLocal, und gespiegelte Personen ohne den Layer
MirrorReflection, was dazu führt, dass sie einander sehen, sich selbst aber nicht.
Die Kostenschätzung
Abschnitt betitelt „Die Kostenschätzung“Die Inspektoren von Ziel und Manager zeigen eine richtungsweisende Punktzahl von 100. Sie ist eine gewichtete Schätzung, keine Messung, und entsteht in dieser Reihenfolge:
-
Die gespiegelten Layer, der beherrschende Faktor. Ein Spiegel zeichnet die Szene ein zweites Mal — in VR zweimal. Was er kostet, ist also das, was er erneut zeichnet. Avatare zu spiegeln verdoppelt den schwersten Posten einer belebten Instanz, und dieser Posten wächst mit der Zahl der Anwesenden, nicht mit der Welt. Ein Spiegel „nur Welt” kostet einen Bruchteil desselben Spiegels mit Personen, bei gleicher Größe und Position.
-
Wie viele Spiegel gleichzeitig gezeichnet werden können. Zwei eingeschaltete Spiegel bedeuten zwei zusätzliche Render-Durchgänge; zwischen ihnen wird nichts geteilt. Der Exklusivmodus streicht diesen Faktor ganz, weshalb die Szenenwertung den schlechtesten Spiegel nimmt statt der Summe.
-
Die Bildschirmabdeckung, zuletzt und schwach gewichtet. VRChat rendert einen Spiegel in eine Textur, deren Größe die Auflösung des Headsets bestimmt, nicht der Spiegel: Ein großer Spiegel kostet also nicht mechanisch mehr als ein kleiner. Größe zählt indirekt — ein großer Spiegel ist von weiter weg sichtbar, füllt sich öfter mit Avataren und lässt sich schlechter über die Entfernung ausblenden.
Nutze die Punktzahl, um zwei Einstellungen derselben Welt zu vergleichen. Sie ist kein absoluter Wert und ersetzt kein Profiling.
Das Gizmo
Abschnitt betitelt „Das Gizmo“Wird ein Spiegel ausgewählt, zeichnet es seine Ebene, einen Pfeil entlang der spiegelnden Seite und ein rotes Kreuz auf der Rückseite. Ein Spiegel ist ein flaches Objekt: Nichts in der Szenenansicht unterscheidet einen richtig platzierten von einem, der um 180° gedreht wurde — man merkt es üblicherweise im Spiel, vor einem schwarzen Rechteck stehend.
Die Normale wird aus dem Mesh gelesen, nicht aus dem Transform erraten, und bleibt damit richtig, welches Mesh der Spiegel auch verwendet.
Das Gizmo lebt in einem Editor-Skript mit [DrawGizmo]. Es ist bewusst kein
OnDrawGizmos im Udon-Verhalten: Udon ruft diese Methode nie auf, und UdonSharp müsste sie
trotzdem kompilieren — samt Gizmos und Handles, die nicht bereitstehen.
Scripting
Abschnitt betitelt „Scripting“Jeder öffentliche Einstiegspunkt kommt ohne Parameter aus, jedes Skript erreicht ihn also
mit SendCustomEvent:
| Aufruf | An | Wirkung |
|---|---|---|
FOSRequestToggle() |
FOSMirrorTarget |
Kippt den Spiegel |
FOSRequestOn() / FOSRequestOff() |
FOSMirrorTarget |
Erzwingt einen Zustand |
TriggerMirror() |
FOSMirrorButton / FOSMirrorInteract |
Führt die Aktion des Bedienelements aus |
FOSTurnAllOff() |
FOSMirrorManager |
Notschalter: schaltet jeden Spiegel aus |
RefreshTargets() |
FOSMirrorManager |
Baut die Caches nach einer Szenenänderung neu auf |
Die OnClick-Verdrahtung, die eine UI-Schaltfläche braucht, ist
SendCustomEvent("TriggerMirror") am UdonBehaviour des Bedienelements. Von Hand richtig
hinzubekommen heißt, auf das UdonBehaviour statt auf die sichtbare Komponente zu zielen und
den Ereignisnamen ohne Tippfehler zu schreiben — ein Tippfehler löst keinen Fehler aus, die
Schaltfläche tut schlicht nichts. Der Inspektor verdrahtet sie mit einem Klick und sagt
dir, ob sie es schon ist.
Hinweise und Grenzen
Abschnitt betitelt „Hinweise und Grenzen“- Ein Manager je Szene. Mehrere Manager würden sich um dieselben Spiegel streiten; der Inspektor meldet das als Fehler.
- Alte Spiegel werden übersprungen. Nur die Spiegelkomponente des SDK3 ist Udon zugänglich. Der Scan zählt sie und sagt es dir.
- Zur Laufzeit erzeugte Spiegel werden nicht automatisch erfasst. Fülle
targetsund rufeRefreshTargets()auf. - Ein Spiegel ohne Manager lässt sich nicht schalten. Der Scan weist ihn zu; der Inspektor des Ziels sagt es, wenn er fehlt.
- Quest und PC verhalten sich hier identisch — es gibt keinen plattformspezifischen Code, und es könnte ihn nicht geben: Udon wird einmal für beide kompiliert.
