如何优化预制体启动速度啊,大佬们。 我这边希望实现这种功能

  • Creator 版本:

  • 目标平台:

  • 重现方式:

  • 首个报错:

  • 之前哪个版本是正常的:

  • 手机型号:

  • 手机浏览器:

  • 编辑器操作系统:

  • 重现概率:

就是我想在Cocos中 实现一个类似 FGUI 的Loader的功能。
因为游戏中如果加载一个预制体,会优先将预制体关联的所有资源全部加载后 ,之后再加载预制体资源,以确保预制体的所有关联内容都能被正常的显示出来。

但是这样的话,譬如我打开一个预制体,预制体中有N张大图,每个都3M的那种,就会很影响预制体打开的速度。
所以我不希望预制体引用这个图片,而是单独保存UUID 或 路径 ,使他能够换一种方式加载,并能在游戏中动态加载 且在编辑器下也能够 正常的展示

有没有大佬呀,不想写, 我想当个伸手党,能直接给我贴一个吗。 跪谢 :laughing:

你敢不敢打开GLoader那个类,看一下。

异步加载?预加载?
你这么复杂的需求。。。
可以考虑先用空白纹理绘制/占用,等异步纹理完成后替换,比较复杂/感觉没必要。
有一些引擎支持

import { _decorator, AssetManager, assetManager, Component, Node, resources, Sprite, SpriteFrame } from ‘cc’;

import { EDITOR } from ‘cc/env’;

const { ccclass, property, executeInEditMode } = _decorator;

function extractResourcesPath(dbUrl: string): string {

const prefix = 'db://assets/resources/';

if (!dbUrl.startsWith(prefix))

    return '';

return dbUrl

    .slice(prefix.length)   // 去前缀

    .replace(/@.+$/, '')    // 去 sub-asset

    .replace(/\.[^/.]+$/, ''); // 去扩展名

}

@ccclass(‘SpriteLoader’)

@executeInEditMode

export class SpriteLoader extends Sprite {

@property({ type: SpriteFrame, serializable: false , visible:false })

protected _spriteFrame: SpriteFrame | null = null;

@property({ type: String, serializable: true, readonly: true })

public mSpriteUrl: string;

@property({ type: String, serializable: true, readonly: true })

public mSpriteUUID: string;

@property({ type: SpriteFrame, serializable: false })

get spriteFrame(): SpriteFrame {

    const descriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(Sprite.prototype), 'spriteFrame') || Object.getOwnPropertyDescriptor(Sprite.prototype, 'spriteFrame');

    return descriptor?.get?.call(this);

}

set spriteFrame(value: SpriteFrame) {

    if (EDITOR) {

        this.SetSpriteData( value );

    } else {

        console.warn("非编辑器模式,不能进行此值的设置");

    }

}  

public SetSpriteData(spriteFrame: SpriteFrame) {

    if (spriteFrame == undefined)

        this.mSpriteUUID = "";

    else

        this.mSpriteUUID = spriteFrame._uuid;//获取到位移UUID

    if (EDITOR) {

        Editor.Message.request('asset-db', 'query-url', spriteFrame._uuid).then((data1) => {

            let resourcesPath: string = extractResourcesPath(data1);

            this.url = resourcesPath;

            this.UpdateSpriteFrame();//立即更新图片资源

        });

    } else {

        this.UpdateSpriteFrame();//立即更新图片资源

    }

}

@property({ type: String })

public get url(): string {

    return this.mSpriteUrl;

}

public set url(path: string) {

    this.mSpriteUrl = path;

    this.UpdateSpriteFrame();

}

public get assetUUID(): string { return this.mSpriteUUID; }

public set assetUUID(uuid: string) {

    this.mSpriteUUID = uuid;

}

private InitSpriteFrame() {

    this.UpdateSpriteFrame();

}

private UpdateSpriteFrame() {

    if (EDITOR) {

        assetManager.loadAny({ uuid: this.mSpriteUUID, }, (err, data) => {

            console.log(err);

            console.log(data);

            const descriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(Sprite.prototype), 'spriteFrame') || Object.getOwnPropertyDescriptor(Sprite.prototype, 'spriteFrame');

            descriptor?.set?.call(this,data);//调用父类构造

        });

    } else {

        setTimeout( ()=>{

            resources.load( `${this.mSpriteUrl}/spriteFrame` , SpriteFrame , ( err , data )=>{

                const descriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(Sprite.prototype), 'spriteFrame') || Object.getOwnPropertyDescriptor(Sprite.prototype, 'spriteFrame');

                descriptor?.set?.call(this,data);//调用父类构造

            });

        } , 3000 );

    }

}

protected onLoad(): void {

    super.onLoad();

    this.InitSpriteFrame();//初始化精灵节点

    this.url = "GameResource/Font/登录界面";

}

public onDestroy(): void {

    super.onDestroy();

}

}

代码差不多就是这个样子,可以直接跑。
思路就是,不再对Sprite的spriteFrame 进行序列化存储。 而是改为 编辑器以UUID作为 图片参数 , 运行模式以 图片路径作为参数。 来动态加载图片 ,这样应该能够很好的提升预制体打开速度

写个插件,把预制体变成代码,不要预制体了。直接跑代码。这肯定快。

测试了一下,符合我心中的逻辑。
使用了这个 代码后,预制体的加载 与 图片资源解绑了,不需要等所有图片加载完毕后才会打开预制体。

能有效提高预制体加载 与 打开速度

那还不如直接用fgui

这不就是动态加载吗? 你prefab拆分下就能满足

拆分是啥意思啊,把一个预制体变成两个,麻烦了吧

而且哪怕拆分了预制体,那么拆分出来的预制体在加载过程中依然需要加载所有依赖资源后,才能正常创建。 并没有解决 预制体的加载与启动速度。

比如加载1个prefab,需要加载1M的资源,就把这个prefab拆分,不要一次加载完,分布延迟来处理

这是UI的ts描述文件,大小2kb


这是运行时动态生成的界面我保存为prefab的大小

prefab需要解析,然后实例化
UIts描述文件,直接实例化

大家对flutter/swifui 这种声明式ui + singal 响应式怎么看

我感觉你可以试试我这个方案。拆分预制体总是有更多的工作量,而且人工拆分程序的工作量直接就上去了:编辑时需要额外考虑预制体拆分为多少个子预制体,开发编码是需要考虑预制体内组件如何获取,如何分帧等等一些列工作。

但实际上一个预制体里面关联资源的情况90%都是图片

这也是一个方向,其实在做的时候就要尽量避免prefab过大的问题了

SpriteLoader.zip (1.2 KB)

感觉这种更适合做 debug 调试工具界面,组建类型少,布局简单,省了创建 prefab。

我的想法是这样容易复用,ai也能轻松写然后自己再改改

这个预制体是代码来创建吗?不能在编辑器里拖拖拽拽了吧

嗯,要的就是不能,ai理解不了编辑器和拖拽这种事