diff --git a/extensions/max-studio/assets/max-studio/core/res/CCRemoteSprite.ts b/extensions/max-studio/assets/max-studio/core/res/CCRemoteSprite.ts index e69de29..a14d6d6 100644 --- a/extensions/max-studio/assets/max-studio/core/res/CCRemoteSprite.ts +++ b/extensions/max-studio/assets/max-studio/core/res/CCRemoteSprite.ts @@ -0,0 +1,193 @@ +import { nsStringUtils } from "@taqu/Util/StringUtils"; +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"; +const { ccclass, property } = _decorator; + +@ccclass("CCRemoteSprite") +export class CCRemoteSprite extends Sprite { + private static _remoteSpriteCache: Map = new Map(); + private static _loadingSpriteCache: Map> = new Map(); + private static _checkTimer: NodeJS.Timeout; + /** + * 远程图片URL + */ + @property({ + displayName: "远程图片URL", + tooltip: "远程图片的URL地址", + visible: true + }) + private _remoteUrl: string = ""; + + public get remoteUrl(): string { + return this._remoteUrl; + } + + public set remoteUrl(value: string) { + const newValue = value || ""; + if (this._remoteUrl !== newValue) { + this._remoteUrl = newValue; + if (nsStringUtils.isEmpty(this._remoteUrl)) { + this.release(); + return; + } + if (this.spriteFrame == null || this._remoteUrl !== this.spriteFrame.remoteUrl) { + this.loadRemoteSprite(this._remoteUrl).catch(err => { + app.log.error("RemoteSprite", `加载远程图片失败: ${this._remoteUrl}`, err); + // 可以添加默认图片或错误状态处理 + if (this.isValid) { + this.spriteFrame = null; + } + }); + } + } + } + + public onLoad(): void { + super.onLoad(); + if (EDITOR) { + return; + } + if (!CCRemoteSprite._checkTimer) { + CCRemoteSprite._checkTimer = setInterval(() => { + CCRemoteSprite.checkCache(); + }, 5000); + } + + if (!nsStringUtils.isEmpty(this.spriteFrame?.remoteUrl)) { + this.spriteFrame.addRef(); + } + if (!nsStringUtils.isEmpty(this._remoteUrl) && this.spriteFrame?.remoteUrl !== this._remoteUrl) { + this.loadRemoteSprite(this._remoteUrl).catch(err => { + app.log.error("RemoteSprite", `onLoad加载远程图片失败: ${this._remoteUrl}`, err); + }); + } else if (nsStringUtils.isEmpty(this._remoteUrl) && !nsStringUtils.isEmpty(this.spriteFrame?.remoteUrl)) { + this.release(); + } + } + + private async loadRemoteSprite(url: string): Promise { + if (nsStringUtils.isEmpty(url)) { + return; + } + + // URL 相同,且已经加载完成,直接返回 + if (this.spriteFrame && this.spriteFrame.remoteUrl == url) { + return; + } + + this.release(); + + if (CCRemoteSprite._remoteSpriteCache.has(url)) { + const sp = CCRemoteSprite._remoteSpriteCache.get(url); + sp.addRef(); + this.spriteFrame = sp; + return; + } + let loadingPromise: Promise = null; + if (CCRemoteSprite._loadingSpriteCache.has(url)) { + loadingPromise = CCRemoteSprite._loadingSpriteCache.get(url); + } else { + loadingPromise = new Promise((resolve, reject) => { + assetManager.loadRemote(url, (err, asset: ImageAsset) => { + if (err) { + app.log.error(`loadRemote error: ${url}`, err); + CCRemoteSprite._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; + CCRemoteSprite._remoteSpriteCache.set(url, spriteFrame); + CCRemoteSprite._loadingSpriteCache.delete(url); + resolve(spriteFrame); + } catch (createErr) { + CCRemoteSprite._loadingSpriteCache.delete(url); + reject(createErr); + } + } + }); + }); + CCRemoteSprite._loadingSpriteCache.set(url, loadingPromise); + } + + const sp = await loadingPromise; + sp.addRef(); + // // 检查是否在加载过程中URL发生了变化 + if (sp && (!isValid(this.node, true) || this._remoteUrl !== url || this.spriteFrame?.remoteUrl === url)) { + sp.decRef(); + return; + } + console.log("loadSpriteFrame:", url); + this.spriteFrame = sp; + } + + public setSize(widthOrHeight: number, height?: number): void { + height ??= widthOrHeight; + this.sizeMode = Sprite.SizeMode.CUSTOM; + this.getComponent(UITransform).setContentSize(widthOrHeight, height); + } + + public release(): void { + if (!nsStringUtils.isEmpty(this.spriteFrame?.remoteUrl)) { + this.spriteFrame.decRef(); + } + this.spriteFrame = null; + } + + public onDestroy(): void { + this.release(); + super.onDestroy(); + } + + private static checkCache() { + // 检查缓存中是否有已加载的SpriteFrame + let clearUrls = []; + + for (const [url, spriteFrame] of CCRemoteSprite._remoteSpriteCache) { + if (spriteFrame.refCount <= 0) { + 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); + } + } + + app.log.log("清理缓存:", clearUrls.length); + clearUrls.forEach(url => { + CCRemoteSprite._remoteSpriteCache.delete(url); + }); + } +}