From 438aea779e635dc01758cfc398a7d3fc32cfc69a Mon Sep 17 00:00:00 2001 From: han_han9 Date: Thu, 30 Oct 2025 08:01:30 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=B5=84=E6=BA=90?= =?UTF-8?q?=E9=87=8A=E6=94=BEBUG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../max-studio/core/res/RemoteSprite.ts | 130 ++++++++++++++++-- 1 file changed, 119 insertions(+), 11 deletions(-) diff --git a/extensions/max-studio/assets/max-studio/core/res/RemoteSprite.ts b/extensions/max-studio/assets/max-studio/core/res/RemoteSprite.ts index 82b4563..a16f02d 100644 --- a/extensions/max-studio/assets/max-studio/core/res/RemoteSprite.ts +++ b/extensions/max-studio/assets/max-studio/core/res/RemoteSprite.ts @@ -1,13 +1,22 @@ -import { _decorator, isValid, Sprite } from "cc"; -import { EDITOR } from "cc/env"; +import { isValid } from "cc"; +import { assetManager, ImageAsset, Texture2D } from "cc"; +import { SpriteFrame } from "cc"; import { UITransform } from "cc"; +import { Sprite } from "cc"; +import { _decorator } from "cc"; +import { EDITOR } from "cc/env"; import { StringUtils } from "../utils/StringUtils"; -import { RemoteSpriteCache } from "./RemoteSpriteCache"; import LogUtils from "../utils/LogUtils"; const { ccclass, property } = _decorator; +const CACHE_EXPIRE_TIME = 30000; // 30s 缓存30s +const CACHE_CHECK_INTERVAL = 5000; // 5秒检查一次 + @ccclass("RemoteSprite") -export default class RemoteSprite extends Sprite { +export class RemoteSprite extends Sprite { + private static _remoteSpriteCache: Map = new Map(); + private static _loadingSpriteCache: Map> = new Map(); + private static _checkTimer: NodeJS.Timeout; /** * 远程图片URL */ @@ -42,12 +51,25 @@ export default class RemoteSprite extends Sprite { } } + // override set spriteFrame(value: SpriteFrame) { + // super.spriteFrame = value; + // } + public onLoad(): void { super.onLoad(); if (EDITOR) { return; } - RemoteSpriteCache.getInstance().checkAndRegisterSprite(this.spriteFrame); + if (!RemoteSprite._checkTimer) { + RemoteSprite._checkTimer = setInterval(() => { + RemoteSprite.checkCache(); + }, CACHE_CHECK_INTERVAL); + } + + if (!StringUtils.isEmpty(this.spriteFrame?.remoteUrl)) { + this.spriteFrame.addRef(); + this.spriteFrame.lastAccessTime = Date.now(); + } if (!StringUtils.isEmpty(this._remoteUrl) && this.spriteFrame?.remoteUrl !== this._remoteUrl) { this.loadRemoteSprite(this._remoteUrl).catch((err) => { LogUtils.error("RemoteSprite", `onLoad加载远程图片失败: ${this._remoteUrl}`, err); @@ -67,14 +89,56 @@ export default class RemoteSprite extends Sprite { return; } - this.release(); - const sp = await RemoteSpriteCache.getInstance().loadSpriteFrame(url); - // 检查是否在加载过程中URL发生了变化 - if (!isValid(this.node, true) || this._remoteUrl !== url || this.spriteFrame?.remoteUrl === url) { - RemoteSpriteCache.getInstance().releaseResource(url); + if (RemoteSprite._remoteSpriteCache.has(url)) { + const sp = RemoteSprite._remoteSpriteCache.get(url); + sp.lastAccessTime = Date.now(); + sp.addRef(); + this.release(); + this.spriteFrame = sp; return; } + let loadingPromise: Promise = null; + if (RemoteSprite._loadingSpriteCache.has(url)) { + loadingPromise = RemoteSprite._loadingSpriteCache.get(url); + } else { + loadingPromise = new Promise((resolve, reject) => { + LogUtils.log("RemoteSprite", `loadRemoteSprite: ${url}`); + assetManager.loadRemote(url, (err, asset: ImageAsset) => { + if (err) { + LogUtils.error(`loadRemote error: ${url}`, err); + RemoteSprite._loadingSpriteCache.delete(url); + reject(err); + } else { + try { + asset.addRef(); + const texture = new Texture2D(); + texture.image = asset; + const spriteFrame = new SpriteFrame(); + spriteFrame.texture = texture; + spriteFrame.remoteUrl = url; + RemoteSprite._remoteSpriteCache.set(url, spriteFrame); + RemoteSprite._loadingSpriteCache.delete(url); + resolve(spriteFrame); + } catch (createErr) { + RemoteSprite._loadingSpriteCache.delete(url); + reject(createErr); + } + } + }); + }); + RemoteSprite._loadingSpriteCache.set(url, loadingPromise); + } + const sp = await loadingPromise; + sp.addRef(); + sp.lastAccessTime = Date.now(); + // // 检查是否在加载过程中URL发生了变化 + if (sp && (!isValid(this.node, true) || this._remoteUrl !== url || this.spriteFrame?.remoteUrl === url)) { + sp.decRef(false); + return; + } + console.log("loadSpriteFrame:", url); + this.release(); this.spriteFrame = sp; } @@ -86,7 +150,7 @@ export default class RemoteSprite extends Sprite { public release(): void { if (!StringUtils.isEmpty(this.spriteFrame?.remoteUrl)) { - RemoteSpriteCache.getInstance()?.releaseResource(this.spriteFrame.remoteUrl); + this.spriteFrame.decRef(false); } this.spriteFrame = null; } @@ -95,4 +159,48 @@ export default class RemoteSprite extends Sprite { this.release(); super.onDestroy(); } + + private static checkCache() { + // 检查缓存中是否有已加载的SpriteFrame + let clearUrls = []; + + let now = Date.now(); + for (const [url, spriteFrame] of RemoteSprite._remoteSpriteCache) { + if (spriteFrame.refCount <= 0 && spriteFrame.lastAccessTime < now - CACHE_EXPIRE_TIME) { + let texture = spriteFrame.texture as Texture2D; + let imageAsset: ImageAsset = null; + + // 如果已加入动态合图,必须取原始的Texture2D + if (spriteFrame.packable && spriteFrame.original) { + texture = spriteFrame.original._texture as Texture2D; + } + + // 获取ImageAsset引用 + if (texture?.image) { + imageAsset = texture.image; + } + + // 先销毁spriteFrame(这会自动处理对texture的引用) + if (spriteFrame.isValid) { + spriteFrame.destroy(); + } + + // 再销毁texture + if (texture?.isValid) { + texture.destroy(); + } + + // 最后减少ImageAsset的引用计数 + if (imageAsset?.isValid) { + imageAsset.decRef(); + } + clearUrls.push(url); + } + } + + clearUrls.forEach((url) => { + RemoteSprite._remoteSpriteCache.delete(url); + }); + LogUtils.log("清理缓存:", clearUrls.length, RemoteSprite._remoteSpriteCache.size); + } }