Erweiterte Q&A: Optimierung von Speicher und Build-Größe mit Addressables

PATRICK DEVARNEY / UNITY TECHNOLOGIESContributor
Apr 28, 2023|17 Min.
Erweiterte Q&A: Optimierung von Speicher und Build-Größe mit Addressables
Diese Website wurde aus praktischen Gründen für Sie maschinell übersetzt. Die Richtigkeit und Zuverlässigkeit des übersetzten Inhalts kann von uns nicht gewährleistet werden. Sollten Sie Zweifel an der Richtigkeit des übersetzten Inhalts haben, schauen Sie sich bitte die offizielle englische Version der Website an.

Im Februar habe ich im Rahmen meiner Tätigkeit als Senior Software Development Consultant für Unity Accelerate Solutions ein technisches Webinar über das Addressables Asset System geleitet. In der Live-Sitzung habe ich verschiedene Profiling-Tools vorgestellt, mit denen Sie den Laufzeitspeicher und die Build-Größe eines Projekts optimieren können. Das Webinar endete mit einer Fragerunde, und unser Team erhielt mehr Fragen, als wir beantworten konnten.

Die folgenden Ausführungen sind eine Erweiterung dieser abschließenden Frage- und Antwortrunde, damit wir noch mehr Ihrer Fragen beantworten können.

Q: Ist das Addressables-System für leichte Spiele - wie Casual-, Arcade- oder Puzzlespiele - erforderlich, wenn ich keine Speicherprobleme habe?
A: Vielleicht nicht, aber es ist gut zu wissen, dass das Addressables-System nicht nur die Speicherleistung verbessert. Die Möglichkeit zu wählen, wann Inhalte geladen werden, kann die Ladezeiten verbessern. Die Erstellung von Inhalten in Addressables ermöglicht eine iterative Erstellung, die nicht so lange dauert. Wenn Sie zum Beispiel eine kleine Skriptänderung vornehmen, müssen Sie nicht unbedingt alle Ihre Bundles neu erstellen.

Q: Werden geladene Assets freigegeben, wenn die Szene wechselt?
A: Potentially. Geladene Assets aus Addressables, die zur Freigabe bereit sind, weil sie einen Ref Count von Null haben, können während eines Szenenübergangs aus dem Speicher entladen werden. Beim nicht-additiven Übergang von Szenen ist Resources.UnloadUnusedAssets() aufzurufen. Dies belastet zwar die CPU, ermöglicht es aber, AssetBundles teilweise zu entladen.

Q: Arbeiten Objektpooling und Addressables gut zusammen?
A: Ja. Sie können Ihr Objekt einmal aus Addressables laden und dann mehrere Kopien davon instanziieren, um Ihren Pool zu erstellen. Wenn Sie mit dem Pool fertig sind, zerstören Sie alle Objekte und geben das AsyncOperationHandle frei, das zum Laden des Assets verwendet wurde.

Q: Werden Gruppen und Pakete auf einmal in den Speicher geladen?
A: Adressierbare Gruppen sind ein reines Editor-Konzept. Zur Laufzeit haben Sie es nur mit Bundles zu tun. Bundles werden nur dann in den Speicher geladen, wenn sie benötigt werden, und nur der gewünschte Inhalt wird geladen.

Beispiel: Sie haben ein Bündel mit 10 Zeichen darin. Sie bitten Addressables, drei Zeichen zu laden. Die Metadaten des Bündels und die drei Zeichen werden geladen.

Q: Wenn ich ein Asset freigeben möchte, muss ich dann das AsyncOperationHandle oder die AssetReference behalten?
A: Wir empfehlen, den Griff zu behalten und ihn zu verwenden, da Sie für die Freigabe der Inhalte verantwortlich sind, wenn Sie ihn nicht mehr verwenden.

Beispielsweise wählen Mitglieder unseres Teams oft den Weg über das Handle, um den direkten Aufruf von Instantiate/Release für die AssetReference zu vermeiden.

Q: Was sind die Nachteile der vielen kleinen Pakete?
A: In dieser Dokumentation werden mehrere Nachteile von zu vielen Bündeln aufgeführt.

Q: Wenn ein Wirtschaftsgut in einem Bündel benötigt wird, welche Gemeinkosten haben dann die anderen Wirtschaftsgüter in demselben Bündel? Wenn es sich um ein Remote-Bundle handelt, muss es heruntergeladen werden, aber gibt es wirklich keinen Speicher-Overhead durch ungenutzte Assets im Bundle?
A: Richtig, ein Remote-Bundle muss vollständig heruntergeladen werden, bevor Sie es verwenden können.

Ungeladene Assets in einem geladenen Asset-Bundle haben nur minimalen Overhead zur Laufzeit. Wenn Sie Assets aus einem Bundle laden, müssen Sie auch die Metadaten des Bundles laden. Zu diesen Metadaten gehört auch ein Inhaltsverzeichnis, in dem alle Assets des Pakets aufgeführt sind. Mehr Assets in einem Bündel bedeuten größere Metadaten.

Sie können sich diesen Speicher-Overhead ansehen, indem Sie mit dem Unity Memory Profiler eine Aufnahme machen. Auf der Registerkarte "All of Memory" wird eine Liste aller "SerializedFile"-Objekte im Speicher angezeigt, eine für jedes Bundle. Diese Objekte sind die Metadaten Ihrer Bundles.

Weitere Informationen zu diesen Metadaten finden Sie in unserer Dokumentation.

Q: Wenn ich in einer offenen Welt arbeite, welche Bündelungsstrategien kann ich verwenden, um einzelne Assets zu entladen, ohne ein halbes Bündel zu entladen und sich auf Resources.UnloadUnusedAssets() zu verlassen, um es zu bereinigen, ohne den Overhead zu haben, dass jedes Asset in seinem eigenen Bündel ist?
A: Das Wichtigste ist, dass Inhalte gebündelt werden sollten, wenn sie gleichzeitig entladen werden sollen. Wenn Ihre Spielwelt "statische" Inhalte hat, wie z. B. Bäume und Felsen für einen bestimmten Lebensraum, die vom Spieler nicht bewegt werden, sollten diese Inhalte gebündelt werden. Alle "dynamischen" Inhalte, wie Gegenstände, die der Spieler aufheben kann, sollten separat gebündelt werden.

Dieser Blogbeitrag und das verlinkte GitHub-Repository behandeln das Aufteilen von Paketen für ein Open-World-Spiel. Es bietet auch eine Möglichkeit zur Deduplizierung von Paketen, um den Speicher-Overhead der einzelnen Pakete zu reduzieren. Die Stufen 4 und 5 sind besonders relevant für offene Welten.

Q: Wann sollte ich "AssetBundle CRC" aktiviert lassen?
A: Es wird empfohlen, diese Funktion zu aktivieren, wobei zwischengespeicherte AssetBundles für entfernte Gruppen ausgeschlossen werden, und für lokale Gruppen zu deaktivieren. Die Prüfung dient nur dazu, sicherzustellen, dass die Daten beim Herunterladen nicht beschädigt wurden. Es gibt fast keinen Grund, die Prüfung für lokale AssetBundles durchzuführen.

Q: Wann lohnt sich die Verwendung von Addressables aufgrund von CPU-Leistungsproblemen beim Laden und Entladen von Assets nicht?
A: Das Addressables-System wirkt sich positiv auf die CPU-Ladeleistung aus, da nicht alle Inhalte im Voraus geladen werden müssen.

Wenn Sie beim Laden einer Szene keine Addressables verwenden, müssen Sie alle Inhalte und Referenzen laden. Wenn Sie den Inhalt in Addressables verschieben, können Sie wählen, wann welcher Inhalt geladen werden soll.

Nehmen wir an, Sie haben einen Inventar-Manager in einer Szene, der einen Verweis auf 1.000 Inventargegenstände hat. Wenn Sie keine Addressables verwenden, müssen Sie jedes Mesh, jede Textur, jeden Audioclip usw. für all diese Inventargegenstände laden. Wenn Sie mit dem Laden dieses Inhalts warten, wird das Laden der Szene schneller sein.

Q: Müssen alle Abhängigkeiten eines adressierbaren Assets auch adressierbar sein, oder ist das nur notwendig, wenn sie gemeinsam genutzt werden?
A: Abhängigkeiten müssen nicht als adressierbar gekennzeichnet werden. Abhängigkeiten werden bei Bedarf während des Build-Prozesses in Addressables gezogen.

Wenn Sie beispielsweise eine Player-Voreinstellung als adressierbar kennzeichnen, müssen Sie das Mesh, die Texturen oder das Audio des Players nicht mehr manuell als adressierbar markieren. Wenn das Bundle erstellt wird, werden alle Abhängigkeiten, die noch nicht in Addressables vorhanden sind, automatisch in das Player-Prefab-Bundle aufgenommen.

Q: Was passiert mit einem Asset, das ich vergessen habe freizugeben, wenn ich die Szene wechsle?
A: Ein Szenenwechsel hat nicht zwangsläufig negative Auswirkungen auf die Griffe. Wenn Sie jedoch ein Asset laden und vergessen, seinen Handle freizugeben, bleibt das Asset im Speicher erhalten.

Addressables verfügt über ein internes Referenz-Zählsystem. Handles sind die Art und Weise, wie wir mit diesem System interagieren. Das Laden eines Assets erhöht den Referenzzähler, das Freigeben verringert den Referenzzähler.

Die Ersteller sind dafür verantwortlich, die Anzahl der Verweise auf dem neuesten Stand zu halten. Das Asset befindet sich so lange im Speicher, wie die Anzahl der Verweise größer als eins ist.

Q: Bezogen auf das Webinar-Beispiel: Nehmen wir an, ich entwickle ein Open-World-Spiel. Der Boss befindet sich irgendwo in der offenen Welt. Wie verwende ich Addressables, wenn der Spieler zum Boss geht? Sende ich den Befehl zum Laden des Schwertes asynchron, über einen Auslöser, in einer bestimmten Entfernung vom Feind, oder etwas anderes?
A: Es kann ein schmaler Grat sein, zu entscheiden, wann Inhalte geladen und entladen werden sollen. Sie wollen sicher sein, dass der Boss bereit ist, wenn der Spieler ihn sehen muss, aber Sie wollen ihn vielleicht nicht zu früh laden, wenn der Spieler noch in der Lage ist, sich umzudrehen und dem Boss auszuweichen.

Das Gute daran ist, dass Sie den Zeitpunkt des Ladens und Entladens von Inhalten beliebig oft wiederholen können - Sie müssen es nicht gleich beim ersten Versuch perfekt hinbekommen.

Für den Anfang schlagen wir vor, den gesamten Inhalt einer bestimmten "Zone" zu laden, wenn der Spieler sich ihr nähert (z. B. wenn der Spieler sich dem Eingang eines Verlieses nähert, was dazu führt, dass alles innerhalb des Verlieses geladen wird). Wenn dies zu einer unnötigen Belastung des Speichers führt, können Sie feinkörnigere Lade- und Entladevorgänge hinzufügen.

Wenn das Schwert nicht schnell genug geladen wird, sollten Sie in Erwägung ziehen, den Ladeauslöser früher zu starten, die Ladezeit der Schwert-Assets zu verbessern, indem Sie das CPU-Modul von Unity Profiler verwenden, um zu sehen, was geladen wird, oder Addressables synchron zu verwenden, um sicherzustellen, dass der Ladevorgang abgeschlossen ist.

Diese Dokumentation enthält weitere Details und einen Codeschnipsel für synchrone Addressables.

Q: Wenn ich ein adressierbares Gerät lade, wenn eine Szene beginnt, muss ich dann einen Ladebildschirm für dieses Gerät haben?
A: Das Laden aus Addressables erfolgt in der Regel asynchron, etwa mit Addressables.LoadAssetAsync().

Es kann sein, dass Sie einige Inhalte nicht laden wollen, bevor Sie einen Ladebildschirm verlassen. Sie können diese AsyncOperationHandles sammeln und warten, bis die erforderlichen Handles abgeschlossen sind, bevor Sie gehen.

Q: Wie groß ist der Speicherbedarf der adressierbaren Metadaten zur Laufzeit (vor dem Laden der Daten)?
A: Während der Initialisierung von Addressables wird die Katalogdatei geladen, so dass Addressables weiß, wie die Etiketten und Adressen den Assets auf der Festplatte oder an entfernten Orten zugeordnet werden können. Ein größerer Katalog bedeutet einen größeren Speicher-Overhead zur Laufzeit.

Die Größe des Katalogs kann reduziert werden, indem unnötige Daten entfernt werden, z. B. indem keine Bezeichnungen oder GUIDs in Gruppen aufgenommen werden, die nicht benötigt werden, oder indem die Größe der vorhandenen Daten reduziert wird. Zum Beispiel, indem Sie den internen Asset-Benennungsmodus einer Gruppe auf GUID statt auf Dateiname oder vollständigen Pfad (der länger sein kann) einstellen. Sie können die Größe des Laufzeitspeichers des Katalogs im Unity Memory Profiler einsehen.

Q: Was macht der Unity-Editor in der Zeit, in der er Addressables erstellt?
A: Ein Build-Berichtsprotokoll wird im Ordner /Library ausgegeben. Dieses Protokoll zeigt jeden Schritt des Erstellungsprozesses an. Um dem Protokoll zusätzliche Details hinzuzufügen, folgen Sie diesem Pfad und wählen Sie "Use Detailed Build Log": Aktivieren von Bearbeiten > Voreinstellungen > Skriptfähige Build-Pipeline > Detailliertes Build-Protokoll verwenden.

Sehen Sie sich das Bildmaterial und die Dokumentation zur Anzeige des Protokolls an.

Q: Hat Resources.Load() auch ein Duplikationsproblem?
A: Ja. Es kann sinnvoll sein, die Inhalte der Adressaten und die Inhalte der Ressourcen als unterschiedliche "Welten" zu betrachten. Wenn Sie eine Textur in /Resources haben, wird eine Kopie dieser Textur in die Resources-Datei aufgenommen. Wenn Bundles in Addressables von dieser Textur abhängen, enthält jedes Bundle eine implizite Kopie der Textur. Sie haben dann mehrere Kopien der Textur auf der Festplatte und möglicherweise mehrere Kopien im Speicher.

Um diese Duplizierung zu vermeiden, verschieben Sie die Textur aus /Resources und fügen Sie sie einer Addressables-Gruppe hinzu.

Q: Treten ähnliche Probleme mit der Größe der Festplatte auf, die durch das Entfernen doppelter Bundles gelöst werden, wenn Sie keine Addressables verwenden?
A: Ja. Im Webinar und auf den Folien zeigen wir, wie durch die Deduplizierung der beiden Wasserrennszenen die Build-Größe erheblich reduziert werden konnte.

Q: Wie kann ich Shader-Variantenduplikate verhindern?
A: Shader können wie jedes andere Asset dedupliziert werden, indem sie explizit in einer Gruppe deklariert werden.

Wenn ein Asset explizit in einer Addressables-Gruppe deklariert ist, die in Ihren Build aufgenommen wird, wird dieses Asset nicht in mehreren Bundles dupliziert.

Speziell für Shader ist es bei Projekten üblich, eine Gruppe "Shared Shaders" zu verwenden, die Shader enthält, von denen Sie erwarten, dass sie während der gesamten Lebensdauer Ihrer Anwendung im Speicher benötigt werden, und die von vielen Assets gemeinsam genutzt werden.

Q: Verdoppeln zwei Unity-Szenen, die sich dieselbe Voreinstellung teilen, die Baugröße?
A: Dies hängt davon ab, ob die Voreinstellung, von der die Szenen abhängen, explizit in Addressables enthalten ist und ob sich die Szenen in denselben oder in unterschiedlichen Paketen befinden.

In den Webinar-Folien und in diesem Blog-Beitrag unter Stufe 4 finden Sie eine visuelle Erklärung, wie die Duplizierung erfolgt.

Wichtig ist, dass alle Inhalte, die in ein Bundle aufgenommen werden, auf alle ihre Abhängigkeiten zugreifen können müssen. Wenn Sie eine Szene in ein Bundle packen, müssen alle ihre Abhängigkeiten entweder sein:

  • Explizit irgendwo in Addressables enthalten
  • Implizit im selben Bündel enthalten

Q: Ist es möglich, Duplikate in bestimmten Gruppen zu vergleichen, um zu verhindern, dass alle Spielelemente in eine isolierte Gruppe gepackt werden?
A: Ja. Sie können die integrierte Deduplizierungsregel ausführen und dann die Assets im Fenster Adressierbare Gruppen in bessere Gruppen einteilen.

Ein skalierbarerer Ansatz besteht darin, eigene Addressables AnalyzeRules zu schreiben, die dann im Fenster Analyze erscheinen. Die eingebauten Regeln werden als C# im Addressables-Paket geliefert und können als Grundlage dienen.

Sie können zum Beispiel alle Duplikate in allen Ihren Gruppen suchen, die mit "Zeichen-" beginnen. Alle impliziten Duplikate können in eine Gruppe "Gemeinsame Zeichen" eingefügt werden.

Q: Werden Sie Remote-Builds und lokale Pfade behandeln?
A: Wir haben nicht auf Remote- und lokale Pfade eingegangen, die im Webinar als "Addressables Profiles" bezeichnet werden. Wir beschreiben jedoch in dieser Dokumentation, was Addressables Profiles sind und wie man sie verwendet.

Q: Wie funktioniert Addressables mit Cloud Content Delivery (CCD)?
A: Die CCD-Integration wird in dieser Dokumentation behandelt.

Q: Können Sie bitte Hinweise auf bewährte Verfahren zur Implementierung von Varianten mit niedriger und hoher Auflösung von Addressables geben?
A: Ein Beispiel finden Sie im Addressables Sample auf GitHub.

Q: Was ist, wenn der Inhalt des Pakets verschlüsselt ist? Entschlüsselt das UnityDataTool auch den Inhalt?
A: Nein. Die Daten müssen erst entschlüsselt werden, bevor UnityDataTool den Inhalt analysieren kann.

Q: Ist es ein unterstützter Anwendungsfall, Bundles aus einem Unity-Projekt zu erstellen und die Bundles zur Laufzeit aus einer App zu laden, die aus einem anderen Projekt erstellt wurde?
A: Ja. Dies wird durch die gleichzeitige Verwendung mehrerer Kataloge abgedeckt.

Q: Gibt es Nachteile bei der Verwendung von InstantiateAsync, oder Situationen, in denen es besser ist, LoadAsync + manuelles Instantiate zu verwenden?
A: Es wird empfohlen, Addressables.LoadAssetAsync() zu verwenden und Object.Instantiate() aufzurufen. Addressables.InstantiateAsync() hat einen größeren Leistungsaufwand.

Q: Ich habe eine Menge von ScriptableObjects mit mindestens 1-2 Sprites als Variablen referenziert. Wenn ich die Sprites in Addressables ändern möchte, muss ich dann die Verweise auf Addressables einzeln ändern, oder gibt es einen Trick, dies zu tun?
A: Ein Editor-Skript ist wahrscheinlich der richtige Weg, um diese Referenzen zu konvertieren.

Sie können die AssetReference-Felder zu Ihrem ScriptableObject hinzufügen (und die Sprite-Felder vorübergehend behalten). Dann können Sie ein Editor-Skript schreiben, das Ihre ScriptableObjects durchläuft, das Sprite-Asset in Addressables nachschlägt, um den zugehörigen AddressableAssetEntry zu finden, und die Adresse speichert oder eine AssetReference erstellt, die im ScriptableObject gespeichert wird.

Schließlich können Sie die direkten Sprite-Referenzen entfernen und den zugehörigen Code durch den AssetReference-Code ersetzen.

Q: Kann ich adressierbare Dateien für WebGL-Spiele verwenden? Wenn ja, gibt es bestimmte Dinge, auf die man achten sollte?
A: Ja, und ja. Zwei Dinge sind zu beachten: Erstens unterstützt WebGL kein Threading, verwenden Sie also keine Tasks. Zweitens funktioniert die Zwischenspeicherung bei WebGL anders - wir haben schon früher Probleme mit der Zwischenspeicherung von entfernten AssetBundles gesehen.

Q: Wenn ich Shader.Find("ShaderName") verwende, kommt dies aus dem Build oder den Addressables?
A: Diese stammen aus dem Build des Unity-Players, nicht aus den Addressables. Shader.Find() gibt keine Ergebnisse von AssetBundles zurück.

Q: Wie kann ich das Fenster Adressierbare Gruppen organisieren, wenn ich viele Gruppen mit ähnlichem Namen habe?
A: Für die Organisation der Benutzeroberfläche der Adressatengruppen können Sie die Gruppenhierarchie mit Bindestrichen aktivieren. Dadurch werden Gruppen mit ähnlichem Namen zusammengefasst. Zum Beispiel werden "Charakter-Person" und "Charakter-Person2" in der Benutzeroberfläche unter der Gruppierung "Charakter" angezeigt.

Dies hat keinen Einfluss auf die Erstellung von Bundles. Dies ist nur eine organisatorische Änderung der Benutzeroberfläche.

Teilen Sie uns Ihr Feedback im Addressables-Forum mit. Achten Sie auf neue technische Blogs von anderen Unity-Entwicklern im Rahmen der fortlaufenden Serie Tech from the Trenches .