Wenn Sie Cocos Creator verwenden möchten, um einige Spiele in größerem Maßstab zu erstellen, ist die Ressourcenverwaltung ein Problem, das gelöst werden muss. Im Verlauf des Spiels stellen Sie möglicherweise fest, dass der Speicherverbrauch des Spiels nur zunimmt und nie abnimmt, selbst wenn Sie derzeit nur sehr wenige Ressourcen verwenden und cc.loader.release verwenden, um zuvor geladene Ressourcen freizugeben, aber die meisten zuvor verwendeten Ressourcen bleiben im Speicher! Warum passiert das? Probleme mit der Ressourcenverwaltung in Cocos CreatorDurch die Ressourcenverwaltung werden hauptsächlich drei Probleme gelöst: das Laden von Ressourcen, die Suche (Verwendung) von Ressourcen und die Freigabe von Ressourcen. Das Hauptproblem, das hier diskutiert werden muss, ist das Problem der Ressourcenfreigabe. Dieses Problem scheint sehr einfach zu sein und ist in Cocos2d-x tatsächlich sehr einfach, wird jedoch in js kompliziert, da es schwierig ist, zu verfolgen, ob eine Ressource freigegeben werden kann. In Cocos2d-x verwenden wir Referenzzählungen. Wenn die Referenzzählung 0 ist, müssen wir nur die Referenzzählung beibehalten. Darüber hinaus ist unsere Ressourcenverwaltung in Cocos2d-x relativ dezentralisiert. Die Engine-Ebene bietet nur Singletons wie TextureCache und AudioManager, um bestimmte spezifische Ressourcen zu verwalten. Die meisten Ressourcen müssen von uns selbst verwaltet werden. In Cocos Creator werden unsere Ressourcen einheitlich von cc.loader verwaltet und Prefab wird umfassend verwendet. Die komplexe Referenzbeziehung zwischen Prefab und verschiedenen Ressourcen erhöht die Schwierigkeit der Ressourcenverwaltung. Ressourcenabhängigkeiten Ressource A kann von den Ressourcen B, C und D abhängen, und Ressource D hängt von Ressource E ab. Dies ist eine sehr häufige Situation der Ressourcenabhängigkeit. Wenn wir Jede geladene Ressource wird in den _cache von cc.loader eingefügt, aber cc.loader.release gibt nur die übergebene Ressource frei, ohne Ressourcenabhängigkeiten zu berücksichtigen. Wenn Sie sich für den Ressourcenladeprozess hinter cc.loader interessieren, können Sie hier nachlesen: https://www.cnblogs.com/ybgame/p/10576884.html Wenn wir die abhängigen Ressourcen gemeinsam freigeben möchten, stellt Cocos Creator eine komplizierte Methode bereit: Obwohl diese Methode Ressourcen freigeben kann, kann sie Ressourcen freigeben, die nicht freigegeben werden sollten. Wenn eine Ressource F von D abhängt, führt dies dazu, dass die Ressource F nicht ordnungsgemäß funktioniert. Da die Cocos Creator-Engine die Ressourcenabhängigkeiten nicht gut verwaltete, wussten wir nicht, dass F immer noch von uns abhängig war, als wir D veröffentlichten. Auch wenn keine F-Abhängigkeit besteht, sind wir nicht sicher, ob D freigegeben werden kann. Beispielsweise rufen wir cc.loader auf, um D zu laden, und laden dann A. Zu diesem Zeitpunkt wurde D geladen und A kann direkt verwendet werden. Wenn D jedoch gleichzeitig mit A freigegeben wird, entspricht dies nicht unseren Erwartungen. Wir erwarten, dass D nicht automatisch zusammen mit der Freigabe anderer Ressourcen freigegeben wird, wenn wir D nicht explizit freigeben. Sie können es einfach testen, indem Sie den Entwicklermodus von Chrome öffnen und in das Konsolenfenster tippen. Wenn es sich um eine alte Version von Cocos Creator handelt, können Sie alle Texturen in cc.textureCache speichern, und die neue Version entfernt textureCache, aber wir können cc.loader._cache eingeben, um alle Ressourcen anzuzeigen. Wenn zu viele Ressourcen vorhanden sind und Sie nur an der Menge interessiert sind, können Sie Object.keys(cc.loader._cache).length eingeben, um die Gesamtzahl der Ressourcen anzuzeigen. Wir können einmal vor dem Laden der Ressourcen, einmal nach dem Laden und einmal nach der Freigabe einen Dump durchführen, um den Cache-Status in cc.loader zu vergleichen. Natürlich können Sie auch einige praktische Methoden schreiben, z. B. das Ausgeben nur von Bildern oder das Ausgeben der Unterschiede zwischen dem vorherigen Dump und dem vorherigen. RessourcennutzungNeben dem Problem der Ressourcenabhängigkeit müssen wir auch das Problem der Ressourcennutzung lösen. Ersteres ist das Problem der Ressourcenorganisation innerhalb von cc.loader und letzteres ist das Problem der Ressourcennutzung in der Logik der Anwendungsschicht. Wenn wir beispielsweise eine Ressource freigeben müssen, wenn eine Schnittstelle geschlossen wird, stehen wir auch vor dem Problem, ob sie freigegeben werden soll, z. B. ob eine andere Schnittstelle, die nicht geschlossen wurde, die Ressource verwendet? Wird die Ressource anderweitig benötigt, darf sie nicht freigegeben werden! ResLoaderHier habe ich einen ResLoader entwickelt, um das Problem zu lösen, das cc.loader nicht lösen konnte. Der Schlüssel besteht darin, für jede Ressource eine CacheInfo zu erstellen, um die Abhängigkeits- und Nutzungsinformationen der Ressource aufzuzeichnen und so zu bestimmen, ob die Ressource freigegeben werden kann. Verwenden Sie ResLoader.getInstance().loadRes(), um cc.loader.loadRes() zu ersetzen, und ResLoader.getInstance().releaseRes(), um cc.loader.releaseRes() zu ersetzen. Für Abhängigkeiten erstellt ResLoader automatisch eine Zuordnung, wenn Ressourcen geladen werden, und bricht die Zuordnung automatisch ab, wenn Ressourcen freigegeben werden. Außerdem erkennt es, ob die nicht zugeordneten Ressourcen freigegeben werden können, und folgt dann der Freigabelogik. Für die Verwendung wird ein Verwendungsparameter bereitgestellt, um zu unterscheiden, wo die Ressource verwendet wird und ob die Ressource an anderer Stelle verwendet wird. Wenn eine Ressource keine Abhängigkeiten von anderen Ressourcen aufweist und nicht von anderer Logik verwendet wird, kann die Ressource freigegeben werden. /** * Ressourcenladeklasse* 1. Zeichnet automatisch die Referenzbeziehung auf, nachdem der Ladevorgang abgeschlossen ist, und zeichnet die umgekehrte Abhängigkeit gemäß DependKeys* 2 auf. Unterstützt die Ressourcennutzung. Wenn beispielsweise eine bestimmte geöffnete Benutzeroberfläche Ressource A verwendet und Ressource B an anderer Stelle freigegeben wird, verweist Ressource B auf Ressource A. Wenn es keine andere Ressource gibt, die auf Ressource A verweist, wird die Freigabe von Ressource A ausgelöst. * 3. Möglichkeit zur sicheren Freigabe abhängiger Ressourcen (auf eine Ressource wird gleichzeitig von mehreren Ressourcen verwiesen, und die Ressource wird erst freigegeben, wenn alle anderen Ressourcen freigegeben sind) * * 17.07.2018 von Bao Ye*/ // Callback für die Verarbeitung des Ladens von Ressourcen, Exporttyp ProcessCallback = (completedCount: Zahl, totalCount: Zahl, Element: beliebig) => void; // Rückruf für Abschluss des Ressourcenladens Exporttyp CompletedCallback = (Fehler: Fehler, Ressource: beliebig) => void; // Referenzieren und verwenden Sie die Strukturschnittstelle CacheInfo { Verweise: Set<string>, verwendet: Set<string> } // LoadRes-Methode Parameterstruktur Schnittstelle LoadResArgs { URL: Zeichenfolge, Typ?: Typ von cc.Asset, beiAbgeschlossen?: Abgeschlossener Rückruf, beiFortschritt?: Prozessrückruf, verwenden?: Zeichenfolge, } // ReleaseRes-Methodenparameterstrukturschnittstelle ReleaseResArgs { URL: Zeichenfolge, Typ?: Typ von cc.Asset, verwenden?: Zeichenfolge, } // Kompatibilitätsverarbeitung let isChildClassOf = cc.js["isChildClassOf"] wenn (!istKindKlasseVon) { istKindKlasseVon = cc["istKindKlasseVon"]; } exportiere Standardklasse ResLoader { private _resMap: Map<string, CacheInfo> = neue Map<string, CacheInfo>(); privater statischer _resLoader: ResLoader = null; öffentliche statische getInstance(): ResLoader { wenn (!this._resLoader) { this._resLoader = neuer ResLoader(); } gib dies zurück._resLoader; } öffentliche statische zerstören(): void { wenn (this._resLoader) { this._resLoader = null; } } privater Konstruktor() { } /** * Holen Sie sich ein Ressourcenelement von cc.loader * @param URL Abfrage-URL * @param type Der abzufragende Ressourcentyp*/ private _getResItem(url: string, type: typeof cc.Asset): any { Lassen Sie ccloader: beliebig = cc.loader; : Lassen Sie das Element = ccloader._cache[url]; wenn (!Artikel) { let uuid = ccloader._getResUuid(url, Typ, false); wenn (uuid) { let ref = ccloader._getReferenceKey(uuid); Element = ccloader._cache[ref]; } } Artikel zurückgeben; } /** * Parametervorverarbeitung der loadRes-Methode */ private _makeLoadResArgs(): LoadResArgs { wenn (Argumente.Länge < 1 || Typ der Argumente[0] != "Zeichenfolge") { console.error(`_makeLoadResArgs-Fehler ${arguments}`); gibt null zurück; } let ret: LoadResArgs = { url: arguments[0] }; für (lass i = 1; i < Argumente.Länge; ++i) { wenn (i == 1 und isChildClassOf(Argumente[i], cc.RawAsset)) { // Bestimmen Sie, ob es sich um den ersten Parametertyp handelt ret.type = Argumente[i]; } sonst wenn (i == arguments.length - 1 && Typ der Argumente[i] == "string") { //Feststellen, ob es sich um den letzten verwendeten Parameter handelt ret.use = Argumente[i]; } sonst wenn (Typ der Argumente[i] == "Funktion") { // Sonst ist es eine Funktion if (arguments.length > i + 1 && typeof arguments[i + 1] == "function") { ret.onProgess = Argumente[i]; } anders { ret.onCompleted = Argumente[i]; } } } Rückkehr ret; } /** * Parametervorverarbeitung der Methode releaseRes */ private _makeReleaseResArgs(): ReleaseResArgs { wenn (Argumente.Länge < 1 || Typ der Argumente[0] != "Zeichenfolge") { console.error(`_makeReleaseResArgs-Fehler ${arguments}`); gibt null zurück; } let ret: ReleaseResArgs = { url: arguments[0] }; für (lass i = 1; i < Argumente.Länge; ++i) { wenn (Typ der Argumente[i] == "Zeichenfolge") { ret.use = Argumente[i]; } anders { ret.type = Argumente[i]; } } Rückkehr ret; } /** * Generieren Sie eine Ressource mit dem Schlüssel * @param wo es verwendet werden soll, z. B. Szene, UI, Pool * @param who-Benutzer, wie etwa Login, UIHelp... * @param warum Grund für die Verwendung, benutzerdefiniert ... */ public static makeUseKey(wobei: Zeichenfolge, wer: Zeichenfolge = "keine", warum: Zeichenfolge = ""): Zeichenfolge { returniere `use_${where}_by_${who}_for_${why}`; } /** * Informationen zum Ressourcen-Cache abrufen * @param key Die abzurufende Ressourcen-URL */ öffentliche getCacheInfo(Schlüssel: Zeichenfolge): CacheInfo { wenn (!this._resMap.has(Schlüssel)) { this._resMap.set(Schlüssel, { Verweise: neues Set<string>(), verwendet: new Set<string>() }); } gib dies zurück._resMap.get(Schlüssel); } /** * Beginnen Sie mit dem Laden der Ressourcen * @param url Ressourcen-URL * @param Typ Ressourcentyp, Standard ist null * @param onProgess Rückruf für Ladefortschritt* @param onCompleted Rückruf für Ladeabschluss* @param Verwende den Ressourcennutzungsschlüssel, der gemäß der Methode makeUseKey generiert wurde*/ öffentliche loadRes(URL: Zeichenfolge, Verwendung?: Zeichenfolge); öffentliche loadRes(URL: Zeichenfolge, bei Abschluss: Abgeschlossener Rückruf, verwenden?: Zeichenfolge); öffentliche loadRes(URL: Zeichenfolge, bei Fortschritt: ProcessCallback, bei Abschluss: CompletedCallback, verwenden?: Zeichenfolge); öffentliche loadRes(URL: Zeichenfolge, Typ: Typ von cc.Asset, Verwendung?: Zeichenfolge); öffentliche loadRes(URL: Zeichenfolge, Typ: Typ von cc.Asset, bei Abschluss: Abgeschlossener Rückruf, Verwendung?: Zeichenfolge); öffentliche loadRes(URL: Zeichenfolge, Typ: Typ von cc.Asset, bei Fortschritt: ProcessCallback, bei Abschluss: CompletedCallback, Verwendung?: Zeichenfolge); öffentliche loadRes() { : let resArgs: LoadResArgs = this._makeLoadResArgs.apply(this, Argumente); Konsole.Zeit("loadRes|"+resArgs.url); let finishCallback = (Fehler: Fehler, Ressource: beliebig) => { // Umgekehrte Assoziationsreferenz (markiere alle referenzierten Ressourcen mit der Markierung, dass diese Ressource auf sie verweist) let addDependKey = (Element, RefKey) => { wenn (Element && Element.dependKeys && Array.isArray(Element.dependKeys)) { für (let depKey von item.dependKeys) { //Aufzeichnen, dass die Ressource von mir referenziert wird this.getCacheInfo(depKey).refs.add(refKey); // cc.log(`${depKey} ref durch ${refKey}`); Lassen Sie ccloader: beliebig = cc.loader; let depItem = ccloader._cache[depKey] addDependKey(depItem, refKey) } } } : Lassen Sie das Element = this._getResItem(resArgs.url, resArgs.type); wenn (Artikel && Artikel.URL) { Fügt eine URL hinzu, die mit dem Schlüsselwert übereinstimmt, den Sie angeben möchten. } anders { cc.warn(`addDependKey item error1! für ${resArgs.url}`); } // Füge eine Referenz auf dich selbst hinzu, wenn (Element) { let info = this.getCacheInfo(item.url); } info.refs.add(item.url); // Ressourcennutzung aktualisieren, wenn (resArgs.use) { } info.uses.add(resArgs.use); } } // Rückruf für Ausführungsabschluss if (resArgs.onCompleted) { resArgs.onCompleted(Fehler, Ressource); } console.timeEnd("loadRes|"+resArgs.url); }; // Vorhersagen, ob die Ressource geladen wurde let res = cc.loader.getRes(resArgs.url, resArgs.type); wenn (res) { finishCallback(null, res); } anders { cc.loader.loadRes(resArgs.url, resArgs.type, resArgs.onProgess, finishCallback); } } /** * Ressourcen freigeben * @param url Die freizugebende URL * @param Typ Ressourcentyp* @param use freizugebender Ressourcennutzungsschlüssel, generiert gemäß der Methode makeUseKey*/ öffentliche ReleaseRes (URL: Zeichenfolge, Verwendung?: Zeichenfolge); öffentliche ReleaseRes(URL: Zeichenfolge, Typ: Typ von cc.Asset, Verwendung?: Zeichenfolge) öffentliche releaseRes() { /**Ressourcen nicht vorübergehend freigeben*/ // zurückkehren; : Lassen Sie uns resArgs verwenden. : Lassen Sie das Element = this._getResItem(resArgs.url, resArgs.type); wenn (!Artikel) { console.warn(`releaseRes-Element ist null ${resArgs.url} ${resArgs.type}`); zurückkehren; } cc.log("Resloader-Freigabeelement"); // cc.log(Argumente); let cacheInfo = this.getCacheInfo(item.url); wenn (resArgs.use) { cacheInfo.uses.delete(resArgs.use) } diese._release(Artikel, Artikel.URL); } // Eine Ressource freigeben private _release(item, itemUrl) { wenn (!Artikel) { zurückkehren; } let cacheInfo = this.getCacheInfo(item.url); // Entfernen Sie den Verweis auf sich selbst cacheInfo.refs.delete(itemUrl); wenn (cacheInfo.uses.size == 0 und cacheInfo.refs.size == 0) { // Dereferenzieren let delDependKey = (item, refKey) => { wenn (Element && Element.dependKeys && Array.isArray(Element.dependKeys)) { für (let depKey von item.dependKeys) { Lassen Sie ccloader: beliebig = cc.loader; let depItem = ccloader._cache[depKey] this._release(depItem, refKey); } } } delDependKey(Element, Element-URL); //Wenn keine UUID vorhanden ist, geben Sie die URL direkt frei wenn (Artikel.uuid) { cc.loader.release(Element.uuid); cc.log("Resloader gibt Element per UUID frei:" + item.url); } anders { cc.loader.release(Artikel.URL); cc.log("Resloader gibt Element per URL frei:" + item.url); } } } /** * Bestimmen Sie, ob eine Ressource freigegeben werden kann * @param url Ressourcen-URL * @param Typ Ressourcentyp* @param use freizugebender Ressourcennutzungsschlüssel, generiert gemäß der Methode makeUseKey*/ öffentliche checkReleaseUse(URL: Zeichenfolge, Verwendung?: Zeichenfolge): Boolesch; öffentliche checkReleaseUse(URL: Zeichenfolge, Typ: Typ von cc.Asset, Verwendung?: Zeichenfolge): Boolesch öffentliche checkReleaseUse() { : Lassen Sie uns resArgs verwenden. : Lassen Sie das Element = this._getResItem(resArgs.url, resArgs.type); wenn (!Artikel) { console.log(`kann nicht freigegeben werden, Element ist null ${resArgs.url} ${resArgs.type}`); gibt true zurück; } let cacheInfo = this.getCacheInfo(item.url); lassen Sie checkUse = false; lassen Sie checkRef = false; wenn (resArgs.use && cacheInfo.uses.size > 0) { wenn (cacheInfo.uses.size == 1 && cacheInfo.uses.has(resArgs.use)) { checkUse = wahr; } anders { checkUse = falsch; } } anders { checkUse = wahr; } wenn ((cacheInfo.refs.size == 1 && cacheInfo.refs.has(item.url)) || cacheInfo.refs.size == 0) { checkRef = wahr; } anders { checkRef = falsch; } Rückgabewert: checkUse && checkRef; } } Verwenden von ResLoaderResLoader ist sehr einfach zu verwenden. Hier ist ein einfaches Beispiel. Wir können auf die Schaltfläche „Dump“ klicken, um die Gesamtzahl der aktuellen Ressourcen anzuzeigen. Klicken Sie auf cc.load und cc.release und dumpen Sie sie einmal. Wir können feststellen, dass es am Anfang 36 Ressourcen gibt, 40 Ressourcen nach dem Laden und 39 Ressourcen nach der Freigabe. Nur eine Ressource wird freigegeben. Wenn wir ResLoader zum Testen verwenden, werden wir feststellen, dass nach der Freigabe nur 34 Ressourcen vorhanden sind. Dies liegt daran, dass die Ressourcen der zuvor geladenen Szene auch von den Testressourcen abhängig sind, sodass diese Ressourcen ebenfalls freigegeben werden. Solange wir ResLoader zum Laden und Entladen von Ressourcen verwenden, gibt es kein Problem mit Ressourcenverlusten. Beispielcode: @ccklasse exportiere Standardklasse NetExample erweitert cc.Component { @Eigenschaft(cc.Node) AttachNode: cc.Node = null; @Eigenschaft(cc.Label) dumpLabel: cc.Label = null; onLoadRes() { cc.loader.loadRes("Prefab/HelloWorld", cc.Prefab, (Fehler: Fehler, prefab: cc.Prefab) => { wenn (!Fehler) { cc.instantiate(prefab).parent = dieser.attachNode; } }); } beimEntladenRes() { dies.attachNode.removeAllChildren(true); cc.loader.releaseRes("Prefab/Hallo Welt"); } beiMeinLoadRes() { ResLoader.getInstance().loadRes("Prefab/HelloWorld", cc.Prefab, (Fehler: Fehler, prefab: cc.Prefab) => { wenn (!Fehler) { cc.instantiate(prefab).parent = dieser.attachNode; } }); } beiMeinUnloadRes() { dies.attachNode.removeAllChildren(true); ResLoader.getInstance().releaseRes("Prefab/HelloWorld"); } beiDump() { let Loader:any = cc.loader; this.dumpLabel.string = `Aktuelle Gesamtzahl der Ressourcen: ${Object.keys(Loader._cache).length}`; } } Sie können sehen, dass im obigen Beispiel der Knoten zuerst entfernt und dann freigegeben wird. Dies ist die korrekte Verwendungsweise. Was passiert, wenn ich ihn direkt freigebe, ohne ihn zu entfernen? ? Da die Textur freigegeben wird, wird Cocos Creator beim nachfolgenden Rendering weiterhin Fehler melden. ResLoader ist nur eine Grundlage. Wenn wir ResLoader direkt verwenden, müssen wir uns keine Gedanken über Ressourcenabhängigkeiten machen, aber wir müssen uns dennoch Gedanken über die Ressourcennutzung machen. Bei der tatsächlichen Verwendung möchten wir möglicherweise, dass der Ressourcenlebenszyklus wie folgt aussieht:
Wir können eine Komponente implementieren, die an das Objekt hängt. Wenn wir Logik in das Objekt oder andere Komponenten des Objekts schreiben und Ressourcen laden, verwenden wir diese Ressourcenverwaltungskomponente, um sie zu laden, und diese Komponente verwaltet die Freigabe der Ressourcen. Auch die Benutzeroberfläche und die Szenen sind ähnlich. . Der Projektcode befindet sich unter: https://github.com/wyb10a10/cocos_creator_framework. Sie können ihn anzeigen, indem Sie die Szene ResExample im Szenenverzeichnis öffnen. Oben finden Sie ausführliche Informationen zum Ressourcenmanagement des allgemeinen Framework-Designs von CocosCreator. Weitere Informationen zum Ressourcenmanagement des Framework-Designs von CocosCreator finden Sie in den anderen verwandten Artikeln auf 123WORDPRESS.COM! Das könnte Sie auch interessieren:
|
<<: So löschen Sie Tabellendaten in MySQL
>>: So verwenden Sie Docker-Compose, um Django-Anwendungen offline bereitzustellen
Manchmal müssen wir den Hyperlink <a> anstel...
Entwickler, die mit Element-UI vertraut sind, hab...
Eine Umgebung Installieren Sie VMware Tools auf C...
1. Die mysqldump-Sicherungsmethode verwendet eine...
Vorwort Jeder Entwickler, der mit JS in Berührung...
Frage: Kann der Ursprungsserver keine Darstellung...
In diesem Artikel wird der spezifische Code für d...
Inhaltsverzeichnis 1. Traversal-Klasse 1. fürJede...
Hexo bindet einen benutzerdefinierten Domänenname...
Dieser Artikel stellt 4 Methoden zum Erzielen ein...
CSS (Cascading Style Sheet) wird zum Verschönern ...
Als Linux-Einsteiger habe ich schon oft einfache ...
Inhaltsverzeichnis Erklärung des V-Texts bei „if“...
Vue-Methoden und -Eigenschaften 1. Methoden Verwe...
Lebenszyklusklassifizierung Jede Komponente von V...