求助!复用节点动态设置spriteFrame的性能问题排查

2D游戏中,通过FragmentManager创建Fragment对象池,生成碎片的时候从池中取出,然后因为碎片的图不一样就需要创建的时候动态传进来,现在遇到的问题是这样做一旦生成碎片过多draw call导致帧数下降,除了每个不同的碎片创建单独的预制体还有其他办法吗?

碎片管理器 FragmentManager.ts

import { _decorator, Component, instantiate, Node, Prefab, randomRange, Sprite, SpriteFrame, Vec3 } from 'cc';
import { Game } from '../Game/Game';
import { Fragment } from './Fragment';
import { ObjectPool } from '../Services/ObjectPool';

const { ccclass, property } = _decorator;

@ccclass('FragmentManager')
export class FragmentManager extends Component {
    @property(Prefab) fragmentPrefab: Prefab;


    private fragmentPool: ObjectPool<Node> = null;


    private static instance: FragmentManager;

    public static get Instance(): FragmentManager {
        return this.instance;
    }

    protected onLoad(): void {
        this.initPool();
        FragmentManager.instance = this;
    }


    private initPool() {
        this.fragmentPool = new ObjectPool<Node>(() => {
            return instantiate(this.fragmentPrefab);
        }, 200, "Fragment");
    }


    createFragments(position: Vec3, fragmentSpriteFrames: Array<SpriteFrame>) {
        fragmentSpriteFrames.forEach(spriteFrame => {
            const fragmentNode = this.fragmentPool.get();
            const fragment = fragmentNode.getComponent(Fragment);

            fragmentNode.active = true;
            fragmentNode.setPosition(position);
            fragmentNode.parent = Game.Instance.Floor;

            // 设置碎片的精灵帧
            const sprite = fragmentNode.getComponent(Sprite);
            if (sprite) {
                sprite.spriteFrame = spriteFrame;
            }

            // 设置碎片的随机大小和初始化参数
            const scale = randomRange(0.95, 1.25);
            fragmentNode.setScale(scale, scale, scale);

            fragment?.initFragment(position);
        });
    }

    public recycleFragment(fragmentNode: Node) {
        fragmentNode.getComponent(Fragment)?.reset();
        this.fragmentPool.put(fragmentNode);
    }
}

碎片挂载的脚本 Fragment

import { _decorator, Component, instantiate, Node, Prefab, randomRange, Sprite, SpriteFrame, Vec3 } from 'cc';
import { Game } from '../Game/Game';
import { Fragment } from './Fragment';
import { ObjectPool } from '../Services/ObjectPool';

const { ccclass, property } = _decorator;

@ccclass('FragmentManager')
export class FragmentManager extends Component {
    @property(Prefab) fragmentPrefab: Prefab;


    private fragmentPool: ObjectPool<Node> = null;


    private static instance: FragmentManager;

    public static get Instance(): FragmentManager {
        return this.instance;
    }

    protected onLoad(): void {
        this.initPool();
        FragmentManager.instance = this;
    }


    private initPool() {
        this.fragmentPool = new ObjectPool<Node>(() => {
            return instantiate(this.fragmentPrefab);
        }, 200, "Fragment");
    }


    createFragments(position: Vec3, fragmentSpriteFrames: Array<SpriteFrame>) {
        fragmentSpriteFrames.forEach(spriteFrame => {
            const fragmentNode = this.fragmentPool.get();
            const fragment = fragmentNode.getComponent(Fragment);

            fragmentNode.active = true;
            fragmentNode.setPosition(position);
            fragmentNode.parent = Game.Instance.Floor;

            // 设置碎片的精灵帧
            const sprite = fragmentNode.getComponent(Sprite);
            if (sprite) {
                sprite.spriteFrame = spriteFrame; //  fragmentSpriteFrames 都是来自于同一个图集中的图片,这里设置spriteFrame 是否导致Draw Call 增加呢?
            }

            // 设置碎片的随机大小和初始化参数
            const scale = randomRange(0.95, 1.25);
            fragmentNode.setScale(scale, scale, scale);

            fragment?.initFragment(position);
        });
    }

    public recycleFragment(fragmentNode: Node) {
        fragmentNode.getComponent(Fragment)?.reset();
        this.fragmentPool.put(fragmentNode);
    }
}
1赞

先把 buffer 调大,防止过多碎片,容易产生Draw Call

如果想进一步减少,不同纹理造成的 Draw Call

可以考虑:【UI渲染优化】多纹理合批 3.x

多纹理合批是通过 修改渲染层级调整处理的吗

如果遇见mask遮罩 会被打断dc吗

不修改层级,mask是会打断,gpu要设换状态,做模板测试

试了下你的多纹理合批,发现内存暴涨,合批的图集 2048*2048 一下子占用好几张

线上测试?,是没做散图释放,方便来回测试合图。

就是你的测试链接,进的第一个UI界面 大概占用55mb左右,用插件看了下,有几个图集占的比较多

测试用的,
散图没释放,
不然怎么可能来回切,

源图没释放,仅测试示范
实际使用,是可以释放,原来的散图

引擎自动合图,是会占用内存,但张数有限