unity UGUI高性能飘字解决方案(对象池+合并网格)

本方案仅供参考

总结:简单试探了下把字画到同一张mesh上的性能,虽然不尽人意(没有想想中的那么快)。最终解决方案还是对象池+分区块+设置上限+分帧加载的方法。

从需求出发

游戏类型:微信小游戏

帧数限定:60

已知的几种方案:

1:粒子代替飘字:这个属于是骚操作,需要自己做图集,并且依赖unity的粒子系统

2:(性能瓶颈在对象池和drawcall)单纯使用对象池,网上很多这种,但是这种跟没说一样,对象池有用我还用找其他的?

但是还是可以优化的,可以把战斗区域分成几块,设置每块的加载上限(类似于LOD)、unity使用textMeshPro、分帧加载、给字添加相同材质开启GPUInstance、把动效传到shader等等。这个目前是现有解决方案。

3:(性能瓶颈在每帧重绘)适用于每帧变化的飘字(手动动态合批)场上只存在一个mesh,每帧把所有字绘制到一个mesh。

优点:每帧都重绘,高度定制化,可以随意添加、删除。

缺点:每帧重绘速度慢、且会导致大量GC(字体的texture会很离谱的产生大量gc,并且无解(绝了))且不支持动画系统的动效。优化:可能自己制作图集就会好点儿?

NGUI中如何减少CacheFontForText耗时_font cachefontfortext-CSDN博客​​​​​​

优化:我们可以使用computeShader来优化顶点的计算

4:(性能瓶颈在mesh的数量)适用于每帧加载较多行为相同的飘字(改变策略的动态合批)场上存在多个mesh,每次把前5帧的字统计起来(使用队列),统一绘制到一张mesh上。如果游戏锁定帧数为60帧,在使用对象池的情况下,每秒最多有13个mesh生成。这个经过测试是完全可以容忍的(100个mesh每个mesh100个字,能跑120帧),其实就是4的pro版本。

优点:不需要每帧重绘,只需要每隔一段时间检查一下是否有新的飘字即可。而且动效只需要挂载一个obj上。简单易懂。

缺点:不支持动作较多的飘字,每次合一个之后动作都是统一的。依赖对象池。

5:多个mesh的方式:

这个性能更好,支持15000个飘字同屏飘,100帧以上。

说白了就是把同一帧、同一类的字绘制在同一个mesh上,然后用同一个动画管理。

上代码(核心部分,剩下的根据描述自己来就行):

using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(CanvasRenderer))]
public class UGUIFloatingTextBatch : MonoBehaviour
{
    public Font font; // 字体
    public Material fontMaterial; // 字体的材质
    public Color color = Color.white; // 文本颜色
    public float floatSpeed = 50f; // 基础飘动速度
    public float fadeDuration = 10f; // 淡出时长
    public int numberOfTexts = 1000; // 生成的文本数量
    public int currentNum = 1;
    private Mesh mesh;
    private CanvasRenderer canvasRenderer;
    private float timer = 0;
    private List<string> texts = new List<string>();

    // 每个字符的独立行为
    private Vector3[] positions;
    private Color[] colors;
    private float[] alphas; // 用于控制每个字符的透明度


    int totalCharacters;
    Vector3[] vertices; // 每个字符4个顶点
    Vector2[] uvs;
    int[] triangles; // 每个字符2个三角形
    Color[] meshColors;
    Vector3 org;
    void Start()
    {
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        texts.Add("123123");
        currentNum = texts.Count;
        org = transform.position;
        // 为每个 "Hello" 生成网格信息
        totalCharacters = 10 * numberOfTexts;
        vertices = new Vector3[totalCharacters * 4]; // 每个字符4个顶点
        uvs = new Vector2[vertices.Length];
        triangles = new int[totalCharacters * 6]; // 每个字符2个三角形
        meshColors = new Color[vertices.Length];

        // 初始化位置和颜色数据
        positions = new Vector3[numberOfTexts];
        colors = new Color[numberOfTexts];
        alphas = new float[numberOfTexts];

        for (int i = 0; i < numberOfTexts; i++)
        {
            // 随机初始位置
            positions[i] = new Vector3(Random.Range(-500, 500), Random.Range(-300, 300), 0);
            colors[i] = color; // 初始化颜色为全白
            alphas[i] = 1f; // 初始透明度为1(不透明)
        }

        // 获取CanvasRenderer组件
        canvasRenderer = GetComponent<CanvasRenderer>();

        // 生成初始的字体网格
        GenerateMesh();

        // 设置材质:如果没有指定字体材质,使用字体自带的默认材质
        if (fontMaterial != null)
        {
            canvasRenderer.SetMaterial(fontMaterial, null);
        }
        else
        {
            fontMaterial = font.material;
            canvasRenderer.SetMaterial(fontMaterial, null);
        }
    }

    void Update()
    {
        transform.position += Vector3.up * floatSpeed * Time.deltaTime;
        timer += Time.deltaTime;

        //如果时间超过淡出时长,销毁物体
        if (timer >= fadeDuration)
        {
            transform.position = org;
            timer = 0;
        }

    }
    //重绘
    public void GenerateMesh()
    {
        if (mesh == null)
        {
            mesh = new Mesh();
        }
        float xOffset = 0f;
        int vertexOffset = 0;
        int triangleOffset = 0;

        for (int i = 0; i < texts.Count; i++)
        {
            // 为每个 "Hello" 设置网格
            Vector3 startPosition = positions[i];

            for (int j = 0; j < texts[i].Length; j++)
            {
                char c = texts[i][j];
                font.RequestCharactersInTexture(c.ToString(), font.fontSize, FontStyle.Normal);
                font.GetCharacterInfo(c, out CharacterInfo characterInfo, font.fontSize);

                // 设置每个字符的顶点
                float xMin = startPosition.x + xOffset + characterInfo.minX;
                float xMax = startPosition.x + xOffset + characterInfo.maxX;
                float yMin = startPosition.y + characterInfo.minY;
                float yMax = startPosition.y + characterInfo.maxY;

                vertices[vertexOffset] = new Vector3(xMin, yMin, 0);
                vertices[vertexOffset + 1] = new Vector3(xMin, yMax, 0);
                vertices[vertexOffset + 2] = new Vector3(xMax, yMax, 0);
                vertices[vertexOffset + 3] = new Vector3(xMax, yMin, 0);

                // 设置UV坐标
                uvs[vertexOffset] = characterInfo.uvBottomLeft;
                uvs[vertexOffset + 1] = characterInfo.uvTopLeft;
                uvs[vertexOffset + 2] = characterInfo.uvTopRight;
                uvs[vertexOffset + 3] = characterInfo.uvBottomRight;

                // 设置初始颜色
                meshColors[vertexOffset] = colors[i];
                meshColors[vertexOffset + 1] = colors[i];
                meshColors[vertexOffset + 2] = colors[i];
                meshColors[vertexOffset + 3] = colors[i];

                // 设置三角形
                triangles[triangleOffset] = vertexOffset;
                triangles[triangleOffset + 1] = vertexOffset + 1;
                triangles[triangleOffset + 2] = vertexOffset + 2;
                triangles[triangleOffset + 3] = vertexOffset;
                triangles[triangleOffset + 4] = vertexOffset + 2;
                triangles[triangleOffset + 5] = vertexOffset + 3;

                vertexOffset += 4;
                triangleOffset += 6;

                xOffset += characterInfo.advance; // 下一个字符的X偏移量
            }

            // 重置 xOffset,开始下一组 "Hello"
            xOffset = 0f;
        }

        // 将生成的顶点、UV坐标、三角形和颜色赋值给网格
        mesh.vertices = vertices;
        mesh.uv = uvs;
        mesh.triangles = triangles;
        mesh.colors = meshColors;

        // 使用CanvasRenderer将网格设置为UI元素
        canvasRenderer.SetMesh(mesh);
    }

    //刷新,暂时不用
    //void UpdateMesh()
    //{
    //    Vector3[] vertices = mesh.vertices;
    //    Color[] meshColors = mesh.colors;

    //    float xOffset = 0f;
    //    int vertexOffset = 0;

    //    for (int i = 0; i < numberOfTexts; i++)
    //    {
    //        Vector3 startPosition = positions[i];

    //        for (int j = 0; j < helloText.Length; j++)
    //        {
    //            font.GetCharacterInfo(helloText[j], out CharacterInfo characterInfo, font.fontSize);

    //            float xMin = startPosition.x + xOffset + characterInfo.minX;
    //            float xMax = startPosition.x + xOffset + characterInfo.maxX;
    //            float yMin = startPosition.y + characterInfo.minY;
    //            float yMax = startPosition.y + characterInfo.maxY;

    //            vertices[vertexOffset] = new Vector3(xMin, yMin, 0);
    //            vertices[vertexOffset + 1] = new Vector3(xMin, yMax, 0);
    //            vertices[vertexOffset + 2] = new Vector3(xMax, yMax, 0);
    //            vertices[vertexOffset + 3] = new Vector3(xMax, yMin, 0);

    //            // 更新颜色
    //            meshColors[vertexOffset] = colors[i];
    //            meshColors[vertexOffset + 1] = colors[i];
    //            meshColors[vertexOffset + 2] = colors[i];
    //            meshColors[vertexOffset + 3] = colors[i];

    //            vertexOffset += 4;
    //            xOffset += characterInfo.advance;
    //        }

    //        // 重置 xOffset
    //        xOffset = 0f;
    //    }

    //    // 更新网格的顶点和颜色
    //    mesh.vertices = vertices;
    //    mesh.colors = meshColors;

    //    // 更新CanvasRenderer
    //    canvasRenderer.SetMesh(mesh);
    //}
}

这么配置,然后创建很多个mesh就行

 然后绘制就ok。

缺点:不支持每个字单独飘。每个mesh行为统一。

就给个思路。有什么问题留言讨论下,作者本人也比较菜,多多指正。

随便改变大小

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值