DSVGO
Um unsere Webseite für Sie optimal zu gestalten und fortlaufend verbessern zu können, verwenden wir Cookies. Durch die weitere Nutzung der Webseite stimmen Sie der Verwendung von Cookies zu.
Server Wartungsarbeiten
...
Aspect Ratio = Seitenverhältnis
Für einfaches Layering gilt folgende Hierarchie:
[Beispiel sprite renderer]
Bedeutet also:
Anmerkungen:
...
Um bessere Performance aus der UI herauszuholen müssen wir uns vor allem zweier Dinge bewusst sein:
Bevor es jetzt weiter geht noch ein Hinweis: Wie bei allem was mit Performance zu tun hat, gibt es kaum einen universell besten Weg Dinge anzugehen, sondern spezifische Lösungen für Probleme mit der eigenen UI. Diese Lösungen können dann wieder an anderen Stellen Dinge kaputt machen, beispielsweise sind mehrere Canvasses eine gute Idee, jedoch erhöhen sie auch die Anzahl an Batches; Spritemesh wird Overdraw verringern, allerdings gibt es jetzt mehr Polygone und man muss die Importeinstellung "Tight" verwenden. Es ist also ein Ausprobieren und Abschätzen, was für einen den meisten Mehrwert bringt. Am Ende kann wahrscheinlich nur der Profiler wirklich bei der Entscheidung helfen.
Gerade auf Mobile ist Overdraw ein Performance-Killer. Er entsteht, wenn sehr viele transparente Objekte über einander liegen. Es werden also oft Pixel gerendered, die später im Rendervorgang überschrieben werden, weil sie tatsächlich verdeckt sind.
Nochmal: Die gesamte UI ist transparent.
Es ist also allgemein ratsam beim Bau von Elementen darauf zu achten, dass alle statischen Elemente zu einer Textur zusammengeführt werden. Hierzu ein Beispiel:
[Beispiel vorher nachher]
Man kann bereits beim Export von Grafiken darauf achten, dass sie möglichst wenig leere Pixel enthalten und beispielsweise nicht unvorteilhaft rotiert sind.
[Beispiel]
Bei sehr detailierten Sprites kann es sich lohnen, die Funktion "Use Sprite Mesh" der Image-Komponente zu verwenden.
[Beispiel vorher nachher]
Man kann sehen, dass nun ein mehr oder minder genaues Mesh des Sprites verwendet wird, anstelle eines einzelnen Rechtecks. Dafür wird allerdings die Einstellung "Mesh Type - Tight" beim Import benötigt.
Unity bietet Möglichkeiten an Overdraw zu visualisieren - welche es gibt ist abhängig von der verwendeten Renderpipeline. Der Modus für die Built-in-Renderpipeline sieht beispielsweise so aus:
[Beispiel]
Allgemein wird der Inhalt von Canvasses über ein generiertes dynamisches Mesh gerendered. Dieses Mesh wird jedes mal neu generiert wenn sich etwas innerhalb des Canvas verändert. Wenn sich also ein einzelnes Element verändert, muss das ganze Canvas neu generiert werden. Zusätzlich dazu muss auch noch das Layout neu generiert werden - warum das ebenfalls schlecht ist kommt später im Abschnitt Layouts.
Eine erste Faustregel ist es also mehrere Canvasses zu benutzen und nicht die gesamte UI in ein riesiges Canvas zu Packen.
Im nächsten Schritt kann man dann ein jedes Canvas noch unterteilen, indem man Sub - bzw. Nested Canvasses hinzufügt.
Einen Beispiel Artikel von Unity findet man hier.
Die grundsätzliche Idee ist es UI in statische bzw. dynamische Elemente und damit auch Canvasse zu unterteilen. Dadurch sollen Elemente die sich manchmal bis sehr häufig verändern von denen, die das gar nicht tun, abgeschottet werden, damit sie nicht in Neu-Berechnungen eingeschlossen werden.
Im Artikel von Unity geht es um einen Timer. Dieser besteht aus einem Text und einer Zeitanzeige. Es ist klar, dass sich der Text "Current time" nie ändern wird, während die Anzeige der Zeit sich kontinuierlich updated. Entsprechend bringen wird die Zeitanzeige in ein eigenes Subcanvas, unter dem Canvas auf dem der Text liegt. Wenn sich jetzt die Zeit verändert, wird nur das Subcanvas neu berechnet - der Text bleibt unberührt.
[Beispiel]
...
Anchor | ||||
---|---|---|---|---|
|
Um bessere Performance aus der UI herauszuholen müssen wir uns vor allem zweier Dinge bewusst sein:
Bevor es jetzt weiter geht noch ein Hinweis: Wie bei allem was mit Performance zu tun hat, gibt es kaum einen universell besten Weg Dinge anzugehen, sondern spezifische Lösungen für Probleme mit der eigenen UI. Diese Lösungen können dann wieder an anderen Stellen Dinge kaputt machen, beispielsweise sind mehrere Canvasses eine gute Idee, jedoch erhöhen sie auch die Anzahl an Batches; Spritemesh wird Overdraw verringern, allerdings gibt es jetzt mehr Polygone und man muss die Importeinstellung "Tight" verwenden. Es ist also ein Ausprobieren und Abschätzen, was für einen den meisten Mehrwert bringt. Am Ende kann wahrscheinlich nur der Profiler wirklich bei der Entscheidung helfen.
Gerade auf Mobile ist Overdraw ein Performance-Killer. Er entsteht, wenn sehr viele transparente Objekte über einander liegen. Es werden also oft Pixel gerendered, die später im Rendervorgang überschrieben werden, weil sie tatsächlich verdeckt sind.
Nochmal: Die gesamte UI ist transparent.
Es ist also allgemein ratsam beim Bau von Elementen darauf zu achten, dass alle statischen Elemente zu einer Textur zusammengeführt werden. Hierzu ein Beispiel:
[Beispiel vorher nachher]
Man kann bereits beim Export von Grafiken darauf achten, dass sie möglichst wenig leere Pixel enthalten und beispielsweise nicht unvorteilhaft rotiert sind.
[Beispiel]
Bei sehr detailierten Sprites kann es sich lohnen, die Funktion "Use Sprite Mesh" der Image-Komponente zu verwenden.
[Beispiel vorher nachher]
Man kann sehen, dass nun ein mehr oder minder genaues Mesh des Sprites verwendet wird, anstelle eines einzelnen Rechtecks. Dafür wird allerdings die Einstellung "Mesh Type - Tight" beim Import benötigt.
Unity bietet Möglichkeiten an Overdraw zu visualisieren - welche es gibt ist abhängig von der verwendeten Renderpipeline. Der Modus für die Built-in-Renderpipeline sieht beispielsweise so aus:
[Beispiel]
Allgemein wird der Inhalt von Canvasses über ein generiertes dynamisches Mesh gerendered. Dieses Mesh wird jedes mal neu generiert wenn sich etwas innerhalb des Canvas verändert. Wenn sich also ein einzelnes Element verändert, muss das ganze Canvas neu generiert werden. Zusätzlich dazu muss auch noch das Layout neu generiert werden - warum das ebenfalls schlecht ist kommt später im Abschnitt Layouts.
Eine erste Faustregel ist es also mehrere Canvasses zu benutzen und nicht die gesamte UI in ein riesiges Canvas zu Packen.
Im nächsten Schritt kann man dann ein jedes Canvas noch unterteilen, indem man Sub - bzw. Nested Canvasses hinzufügt.
Einen Beispiel Artikel von Unity findet man hier.
Die grundsätzliche Idee ist es UI in statische bzw. dynamische Elemente und damit auch Canvasse zu unterteilen. Dadurch sollen Elemente die sich manchmal bis sehr häufig verändern von denen, die das gar nicht tun, abgeschottet werden, damit sie nicht in Neu-Berechnungen eingeschlossen werden.
Im Artikel von Unity geht es um einen Timer. Dieser besteht aus einem Text und einer Zeitanzeige. Es ist klar, dass sich der Text "Current time" nie ändern wird, während die Anzeige der Zeit sich kontinuierlich updated. Entsprechend bringen wird die Zeitanzeige in ein eigenes Subcanvas, unter dem Canvas auf dem der Text liegt. Wenn sich jetzt die Zeit verändert, wird nur das Subcanvas neu berechnet - der Text bleibt unberührt.
[Beispiel]
Anchor | ||||
---|---|---|---|---|
|
Auto Layout funktioniert über ein sog. "dirty flag"-System. Wenn sich ein Layoutelement verändert und es damit einen umgebenden Layoutcontroller ungültig macht (z.B. Size oder Scale verändert), wird es als "dirty" markiert, worauf das Layoutsystem dann reagieren kann.
Problem: Layoutelemente sind Komponenten, also kann auf jedem Element oder auch Parent eins oder mehrere sein.
Um die Neu-Berechnung des Layouts korrekt auszuführen, wird nach dem Layoutcontroller gesucht, der am weitesten oben in der Hierarchie steht. Das ganze passiert natürlich über GetComponent() auf jedem Objekt. So wird jedes Element, dass sein Layout auf dirty setzen will, minimal einen Aufruf von GetComponent() nach sich ziehen. Jeder geschaltete Layoutcontroller vervielfältigt dieses Problem.
Wodurch wird ein Layout als dirty markiert?
Kurzform: Durch fast alles ...
Nur ein paar Beispiele:
Lösungen: Was kann man dagegen tun?
Kurz gesagt: Benutzt keinen Animator für die UI. Stattdessen solltet ihr Code und/oder Tweens verwenden um eure UI zu animieren. Für Tweens gibt es das sehr gute Package DotweenPro.
[Beispiel]
Warum ist ein Animator Setup, wie auf dem Standard-Button von Unity, schlecht?
Ein Animator wendet jeden Frame seine Werte auf die animierten Objekte an, egal ob sich Werte in der Animation verändert haben oder nicht. Das bedeutet, dass jeden Frame alle animierten Objekte als dirty markiert werden, was wie bereits beschrieben diverse Aktionen im Layout bzw. Canvas Code nach sich zieht.
Entsprechend ist der Animator nicht dafür geeignet States abzubilden - wie z.B. hover, selected im Standard-Button Beispiel - sondern sollte nur zum Abspielen von Animationen verwendet werden, da sich in diesem meist sowieso jeden Frame etwas verändert, was dann dirtying nach sich zieht.
Den Graphics Raycaster benötigt man auf jedem Canvas (und Sub-Canvas!) das (Touch-)Input erhalten soll. Tatsächlich ist er nicht wirklich ein Raycaster, sondern prüft ob sich ein Punkt innerhalb eines Rechtecks befindet und das für jedes RectTransform unter dem Canvas, dass als interaktiv markiert ist. Als interaktiv markiert sind alle Komponenten mit dem Toggle "Raycast Target" auf an (beispielsweise Image).
[Beispiel]
Faustregel: Achte darauf den Toggle auszuschalten, wo es Sinn macht!
Mit der Sorting Group gruppiert man ein GameObject und seine Childs als Einheit für das Rendern - ähnlich wie es das Canvas tut. Das bedeutet, dass außerhalb der Gruppe die Sortieroptionen und die Z-Position des Gruppen-Parents für alle Childs gelten.
[Beispiel]
Innerhalb der Gruppe werden Renderer nach den für sie geltenden Regeln sortiert: Beispielsweise gelten für Sprites Sorting-Layer und Wert, während Z zugunsten der Hierarchie ignoriert wird (erneut, wie beim Canvas). Bei 3D-Renderern wird nach Z sortiert und bei gleichem Z-Wert der Hierarchie nach.
[Beispiele]
Insbesondere bei 3D-Renderern ist wichtig, dass Sorting nur auf transparente Geometry angewendet wird. Wird der Render-Mode "Opaque" verwendet sortieren 3D-Renderer mit Sorting-Groups trotzdem über die Z-Position gegen andere Sorting-Groups.
[Beispiel]
Sorting groups sind für zwei Anwendungen wichtig:
Anchor | ||||
---|---|---|---|---|
|
Wie man seine UI am besten layered ist sehr stark vom Spiel abhängig. Um eine Idee davon zu haben kann man sich beispielsweise folgende Fragen stellen:
Die UI könnte ein klassisches HUD sein - in diesem Fall nutzt man ganz einfach den Modus "Screen Space - Overlay" und sortiert verschiedene Canvasse über den Sorting Wert.
In diesem Fall würde ich keinen "Screen Space - Camera" Modus verwenden. Diese Art von Canvas sortiert sich immer nach Abstand zur Kamera auf Z - ihr X- und Y-Wert ist immer 0. Entsprechend könnten Weltobjekte nun ungewollt vor der UI erscheinen und es wird unmöglich die Sortierung im Scene View nachzuvollziehen.
Canvas mit "World Space" ist dein Freund. Sortiert werden kann das Canvas dann über Abstand zur Kamera oder mittels Sortieroptionen.
Angenommen wir haben ein 2D-Spiel mit Seitenansicht. Es wäre denkbar, dass z.B. Texte zwischen Spielebene und Hintergrund auftauchen sollen.
[Beispiel]
Für diesen Effekt sollten wir "Screen Space - Camera" oder "World Space" verwenden. Mit beiden Optionen können wir entsprechend unserer Szene die UI entweder über Tiefe oder Sortieroptionen einsortieren. Wenn sich diese UI dem Bildschirm oder der Kamera anpassen soll ist "Screen Space - Camera" zu verwenden.
Ein solches Setup erlaubt es auch Partikelsysteme in der Welt vor bzw. hinter der UI zu rendern. Warum das sehr praktisch ist steht im Kapitel über Partikel.
Wenn UI gegen Welt sortiert wird stellt sich dann die Frage nach der Sortierung:
Meiner Meinung nach schließen sich diese beiden Optionen ein wenig aus und bieten unterschiedliche Vor- bzw. Nachteile. Das liegt vor allem an der zuvor beschriebenen einfachen Hierarchie - Sortieroptionen werden immer vor Z angewendet. Letztendlich muss die Wahl hier vor allem anderen mit der Sortierung der Spielszenen funktionieren.
Ich halte folgende Kombinationen für sinnvoll:
Eine Sortierung nach Tiefe bietet vor allem den "What you see is what you get" - Vorteil. Wenn wir im Scene View auf 3D schalten, können wir unser Layering ganz bequem überblicken.
[Beispiel]
Die verschiedenen Ebenen der Spielszene könnten mit einer Sorting Group versehen werden, damit man diese intern über Sortieroptionen sortieren kann.
[Beispiel]
Nachteil ist natürlich, dass sie nicht funktioniert, wenn Z nicht die Transparenzachse ist - also für Top-Down eher nicht zu gebrauchen.
Wenn die Transparenzachse oder die Szenenhierarchie eine Sortierung per Tiefe nicht zulässt, eignen sich die Sortieroptionen am besten, auch wenn sie die Sortierung unübersichtlicher machen.
Ein Mittelweg kann es sein allein die Sortierung der UI über Z zu regeln und für die Welt Sortieroptionen zu nutzen. Dafür würde man alle Canvasses auf ein Vordergrundlayer verschieben, das über dem Weltlayer liegt. So behält man sich den Überblick der Tiefensortierung bei.
[Beispiel]
[Beispiel - wie schaltet man ihn an?]
Mit dem Device Simulator kann man sehen, wie sich unter anderem die UI auf verschiedenen Geräten verhält. Es ist auch möglich das Gerät zu drehen und die Safe Zone anzeigen zu lassen.
[Beispiel]
Man sollte stets verschiedenste gängige Geräte testen - mindestens Android bzw. iOS jeweils als Phone und Tablet. Insbesondere bei der Safe Zone unterscheiden sich die Geräte sehr:
[Beispiel]
Apple macht die Safe Zone gerne symmetrisch und hat außerdem eine vertikale Safe Zone für den Home-Button.
[Beispiel]
Android ist hingegen oft asymmetrisch.
[Beispiel]
Tablets haben oft genug gar keine Safe Zone.
Um all diese Dinge sehen zu können und entsprechend mit Code oder Design zu reagieren, ist der Device Simulator unabdingbar
...
Auto Layout funktioniert über ein sog. "dirty flag"-System. Wenn sich ein Layoutelement verändert und es damit einen umgebenden Layoutcontroller ungültig macht (z.B. Size oder Scale verändert), wird es als "dirty" markiert, worauf das Layoutsystem dann reagieren kann.
Problem: Layoutelemente sind Komponenten, also kann auf jedem Element oder auch Parent eins oder mehrere sein.
Um die Neu-Berechnung des Layouts korrekt auszuführen, wird nach dem Layoutcontroller gesucht, der am weitesten oben in der Hierarchie steht. Das ganze passiert natürlich über GetComponent() auf jedem Objekt. So wird jedes Element, dass sein Layout auf dirty setzen will, minimal einen Aufruf von GetComponent() nach sich ziehen. Jeder geschaltete Layoutcontroller vervielfältigt dieses Problem.
Wodurch wird ein Layout als dirty markiert?
Kurzform: Durch fast alles ...
Nur ein paar Beispiele:
Lösungen: Was kann man dagegen tun?
Kurz gesagt: Benutzt keinen Animator für die UI. Stattdessen solltet ihr Code und/oder Tweens verwenden um eure UI zu animieren. Für Tweens gibt es das sehr gute Package DotweenPro.
[Beispiel]
Warum ist ein Animator Setup, wie auf dem Standard-Button von Unity, schlecht?
Ein Animator wendet jeden Frame seine Werte auf die animierten Objekte an, egal ob sich Werte in der Animation verändert haben oder nicht. Das bedeutet, dass jeden Frame alle animierten Objekte als dirty markiert werden, was wie bereits beschrieben diverse Aktionen im Layout bzw. Canvas Code nach sich zieht.
Entsprechend ist der Animator nicht dafür geeignet States abzubilden - wie z.B. hover, selected im Standard-Button Beispiel - sondern sollte nur zum Abspielen von Animationen verwendet werden, da sich in diesem meist sowieso jeden Frame etwas verändert, was dann dirtying nach sich zieht.
Den Graphics Raycaster benötigt man auf jedem Canvas (und Sub-Canvas!) das (Touch-)Input erhalten soll. Tatsächlich ist er nicht wirklich ein Raycaster, sondern prüft ob sich ein Punkt innerhalb eines Rechtecks befindet und das für jedes RectTransform unter dem Canvas, dass als interaktiv markiert ist. Als interaktiv markiert sind alle Komponenten mit dem Toggle "Raycast Target" auf an (beispielsweise Image).
[Beispiel]
Faustregel: Achte darauf den Toggle auszuschalten, wo es Sinn macht!
Mit der Sorting Group gruppiert man ein GameObject und seine Childs als Einheit für das Rendern - ähnlich wie es das Canvas tut. Das bedeutet, dass außerhalb der Gruppe die Sortieroptionen und die Z-Position des Gruppen-Parents für alle Childs gelten.
[Beispiel]
Innerhalb der Gruppe werden Renderer nach den für sie geltenden Regeln sortiert: Beispielsweise gelten für Sprites Sorting-Layer und Wert, während Z zugunsten der Hierarchie ignoriert wird (erneut, wie beim Canvas). Bei 3D-Renderern wird nach Z sortiert und bei gleichem Z-Wert der Hierarchie nach.
[Beispiele]
Insbesondere bei 3D-Renderern ist wichtig, dass Sorting nur auf transparente Geometry angewendet wird. Wird der Render-Mode "Opaque" verwendet sortieren 3D-Renderer mit Sorting-Groups trotzdem über die Z-Position gegen andere Sorting-Groups.
[Beispiel]
Sorting groups sind für zwei Anwendungen wichtig:
...
Wie man seine UI am besten layered ist sehr stark vom Spiel abhängig. Um eine Idee davon zu haben kann man sich beispielsweise folgende Fragen stellen:
Die UI könnte ein klassisches HUD sein - in diesem Fall nutzt man ganz einfach den Modus "Screen Space - Overlay" und sortiert verschiedene Canvasse über den Sorting Wert.
In diesem Fall würde ich keinen "Screen Space - Camera" Modus verwenden. Diese Art von Canvas sortiert sich immer nach Abstand zur Kamera auf Z - ihr X- und Y-Wert ist immer 0. Entsprechend könnten Weltobjekte nun ungewollt vor der UI erscheinen und es wird unmöglich die Sortierung im Scene View nachzuvollziehen.
Canvas mit "World Space" ist dein Freund. Sortiert werden kann das Canvas dann über Abstand zur Kamera oder mittels Sortieroptionen.
Angenommen wir haben ein 2D-Spiel mit Seitenansicht. Es wäre denkbar, dass z.B. Texte zwischen Spielebene und Hintergrund auftauchen sollen.
[Beispiel]
Für diesen Effekt sollten wir "Screen Space - Camera" oder "World Space" verwenden. Mit beiden Optionen können wir entsprechend unserer Szene die UI entweder über Tiefe oder Sortieroptionen einsortieren. Wenn sich diese UI dem Bildschirm oder der Kamera anpassen soll ist "Screen Space - Camera" zu verwenden.
Ein solches Setup erlaubt es auch Partikelsysteme in der Welt vor bzw. hinter der UI zu rendern. Warum das sehr praktisch ist steht im Kapitel über Partikel.
Wenn UI gegen Welt sortiert wird stellt sich dann die Frage nach der Sortierung:
Meiner Meinung nach schließen sich diese beiden Optionen ein wenig aus und bieten unterschiedliche Vor- bzw. Nachteile. Das liegt vor allem an der zuvor beschriebenen einfachen Hierarchie - Sortieroptionen werden immer vor Z angewendet. Letztendlich muss die Wahl hier vor allem anderen mit der Sortierung der Spielszenen funktionieren.
Ich halte folgende Kombinationen für sinnvoll:
Eine Sortierung nach Tiefe bietet vor allem den "What you see is what you get" - Vorteil. Wenn wir im Scene View auf 3D schalten, können wir unser Layering ganz bequem überblicken.
[Beispiel]
Die verschiedenen Ebenen der Spielszene könnten mit einer Sorting Group versehen werden, damit man diese intern über Sortieroptionen sortieren kann.
[Beispiel]
Nachteil ist natürlich, dass sie nicht funktioniert, wenn Z nicht die Transparenzachse ist - also für Top-Down eher nicht zu gebrauchen.
Wenn die Transparenzachse oder die Szenenhierarchie eine Sortierung per Tiefe nicht zulässt, eignen sich die Sortieroptionen am besten, auch wenn sie die Sortierung unübersichtlicher machen.
Ein Mittelweg kann es sein allein die Sortierung der UI über Z zu regeln und für die Welt Sortieroptionen zu nutzen. Dafür würde man alle Canvasses auf ein Vordergrundlayer verschieben, das über dem Weltlayer liegt. So behält man sich den Überblick der Tiefensortierung bei.
[Beispiel]
[Beispiel - wie schaltet man ihn an?]
Mit dem Device Simulator kann man sehen, wie sich unter anderem die UI auf verschiedenen Geräten verhält. Es ist auch möglich das Gerät zu drehen und die Safe Zone anzeigen zu lassen.
[Beispiel]
Man sollte stets verschiedenste gängige Geräte testen - mindestens Android bzw. iOS jeweils als Phone und Tablet. Insbesondere bei der Safe Zone unterscheiden sich die Geräte sehr:
[Beispiel]
Apple macht die Safe Zone gerne symmetrisch und hat außerdem eine vertikale Safe Zone für den Home-Button.
[Beispiel]
Android ist hingegen oft asymmetrisch.
[Beispiel]
Tablets haben oft genug gar keine Safe Zone.
Um all diese Dinge sehen zu können und entsprechend mit Code oder Design zu reagieren, ist der Device Simulator unabdingbar.
...
Partikeleffekte für die UI sind von Unity nicht von Haus aus unterstützt. Man kann zwar ein herkömmliches Partikelsystem an Childs eines Canvas hängen - das führt dann zu dieser komisch wirkenden Mischung von Transform und RectTransform - aber das Sortieren funktioniert nur mäßig:
[Beispiele]
Ein weiterer Grund warum Partikel besser nicht in der UI sein sollten ist Performance. Partikelsysteme sind konstant in Bewegung und würden das Canvas immer wieder neu zeichnen lassen (mehr dazu im Kapitel Performance).
Besser ist es Partikelsysteme in der Welt zu belassen. Dann kann man sie auch einfacher gegen die UI sortieren, in dem man den richtigen Canvas Render Mode wählt (siehe Layering, Canvas).
[Beispiel]
Wenn man seine Partikel dennoch im Canvas haben möchte, um sich beispielsweise das Layouting zu erleichtern, kann man auf ein Rendertextur Setup zurückgreifen.
[Beispiel]
Hier werden die Partikel von einer separaten Kamera gerendert, deren Bild dann auf der Rendertextur angezeigt wird.
Zuletzt noch ein sehr gutes Package, was neben dem Rendertextur Ansatz auch noch andere Lösungen bietet: hier.
Tools für UI- oder UX-Design, je nach dem wie genau man mit dem Namen ist, helfen massiv bei der Arbeit an UI. Von Wireframes bis hin zu Click-Prototypen kann man damit alles erstellen und das meistens sogar kollaborativ. Entwickler können später als Betrachter hinzugefügt werden, damit sie alle Layouts, Abstände und Größen genauestens ins Spiel bringen können.
...