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

news2024/11/13 9:29:03

本方案仅供参考

从需求出发

游戏类型:微信小游戏

帧数限定:60

已知的几种方案:

1:场上只存在一个mesh,每帧把所有字绘制到一个mesh。

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

缺点:每帧重绘速度慢、且会导致大量GC(字体的texture会很离谱的产生大量gc,并且无解(绝了))

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

且不支持动画系统的动效。

2:单纯使用对象池,网上很多这种,但是这种跟没说一样,对象池有用我还用找其他的?

3:场上存在多个mesh,每次把前5帧的字统计起来(使用队列),统一绘制到一张mesh上。如果游戏锁定帧数为60帧,在使用对象池的情况下,每秒最多有13个mesh生成。这个经过测试是完全可以容忍的(100个mesh每个mesh100个字,能跑120帧)。

优点:不需要每帧重绘,只需要每隔一段时间检查一下是否有新的飘字即可。而且动效只需要在

缺点:分类比较难搞。不支持行为种类过多的飘字(每一个类需要的mesh两就会多一组(13个),超过60个不如普通对象池)。3种以内。依赖对象池。

3多个mesh的方式:

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

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

上代码:

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 string helloText = "Hello";

    // 每个字符的独立行为
    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()
    {
        org = transform.position;
        // 为每个 "Hello" 生成网格信息
        totalCharacters = helloText.Length * 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;
        }

        if (Input.GetMouseButtonDown(0)) // 0 代表鼠标左键
        {
            Debug.Log("重绘");
            GenerateMesh();
        }

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

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

            for (int j = 0; j < helloText.Length; j++)
            {
                char c = helloText[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行为统一。

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

随便改变大小

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2118709.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HTTP状态码解析:在Haskell中判断响应成功与否

在互联网的世界里&#xff0c;HTTP状态码是服务器与客户端之间通信的一种语言。它们告诉我们请求是否成功&#xff0c;或者遇到了什么问题。在进行网络编程时&#xff0c;正确地解析和处理这些状态码是至关重要的。本文将探讨HTTP状态码的基本概念&#xff0c;并展示如何在Hask…

KUKA中级学习4:修改软件中机器人名字,纠正示教器时间,下载备份文件进示教器

这里写目录标题 一、修改机器人名字1.1、程序安装下载二、示教器时间修改2.1、时间修改&#xff0c;示教器全英文显示三、下载备份文件 一、修改机器人名字 1.1、程序安装下载 选下面这个 二、示教器时间修改 2.1、时间修改&#xff0c;示教器全英文显示 三、下载备份文件 …

FancyVideo环境搭建推理

引子 很少关注360开源的代码&#xff0c;最近360AI团队开源了最新视频模型FancyVideo&#xff0c;据说RTX3090可跑。可以在消费级显卡 (如 GeForce RTX 3090) 上生成任意分辨率、任意宽高比、不同风格、不同运动幅度的视频&#xff0c;其衍生模型还能够完成视频扩展、视频回溯…

springboot+vue+mybatis计算机毕业设计网上购物系统+PPT+论文+讲解+售后

本文首先实现了网上购物系统设计与实现管理技术的发展随后依照传统的软件开发流程&#xff0c;最先为系统挑选适用的言语和软件开发平台&#xff0c;依据需求分析开展控制模块制做和数据库查询构造设计&#xff0c;随后依据系统整体功能模块的设计&#xff0c;制作系统的功能模…

《JavaEE进阶》----13.<Spring Boot【配置文件】>

本篇博客讲解 1.SpringBoot配置文件的格式以及对应的语法 2.了解两个配置文件格式的差异、优缺点。 我们这里只做简单的介绍。看会&#xff0c;了解&#xff0c;学会读取就行了。 因为配置文件实在太多了&#xff0c;这里只做基础的介绍。 一、配置文件的作用 前言 计算机中有许…

E5053A 微波下变频器

_XLT新利通_ E5053A 微波下变频器 E5052B SSA 专用的微波下变频器 Keysight E5053A 是一款与 E5052B 信号源分析仪&#xff08;SSA&#xff09;相关的微波下变频器。 如果您需要设计和测试微波或毫米波频率的信号源&#xff0c;E5053A 支持您扩展该分析仪的频率范围。 从…

阿卡迈 Akamai 逆向分析2

在Lzo这个url中点击第一个 进入以后有个HPH是我们需要破解的参数 我们搜索所有的HPH 大概有10个地方&#xff0c;我们需要全部打上断点(部分HPH用不到) 大约这5个地方的HPH需要破解 第一步 清除cookie f5进行刷新 需要破解K1H&#xff0c; 58位的数组其中下标 1 3 15 25 53需…

C# winforms 窗口延迟初始化 splash 定时器

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 源码指引&#xff1a;github源…

微课录制技巧|高效录制微课的方法,如何高效录制微课?

在教育领域&#xff0c;微课作为一种新兴的教学方式&#xff0c;越来越受到教师和学生的欢迎。本文将为您详细介绍如何高效录制微课&#xff0c;以及如何利用各种资源来提升备课和教学的质量。 微课录制技巧&#xff1a; 录制前的准备 在开始录制前&#xff0c;确保您已经明确…

客服知识库与员工培训:打造专业客服团队的秘密武器

在竞争激烈的商业环境中&#xff0c;优质的客户服务已成为企业脱颖而出的关键要素之一。而构建一个高效、专业的客服团队&#xff0c;则离不开一个全面、精准的客服知识库。客服知识库不仅是信息的宝库&#xff0c;更是员工培训与技能提升的秘密武器&#xff0c;它在新员工入职…

Adobe Illustrator非矢量图片的交集利用剪切蒙版实现

AI不支持对于非矢量图片的交集处理&#xff0c;但是可以通过剪切蒙版类似地实现需求。 如下图&#xff0c;字母F是一张PNG图片&#xff0c;为位图文件&#xff08;非矢量&#xff09;。 现在我需要将这种图片与黑色的矩形求交&#xff1a; 将两个目标全部选中&#xff0c;鼠标…

AI 浪潮中的一体化数据库|外滩大会之OceanBase实录

2024 年 9 月 5 日至 7 日&#xff0c;在上海黄浦世博园区&#xff0c;“2024 Inclusion 外滩大会”盛大举行。期间&#xff0c;9月6日&#xff0c;由OceanBase携手赛迪顾问共同策划并主办了 “AI浪潮中的分布式数据库&#xff1a;探索行业增长新动能与关键业务负载实践”。本…

优橙240419期就业榜来啦!就业班平均就业薪资8,333.3元!梦想不会发光,发光的是追梦的你!

有多坚定的信念&#xff0c;就有多勇毅的行动&#xff0c;就能开辟多光明的未来。时隔3个月&#xff0c;优橙240419就业喜报已送达&#xff01; 就业班平均就业薪资8,333.3元&#xff0c;就业学员即将奔赴祖国各地。 行百里者半九十。人类的美好理想&#xff0c;都不可能唾手而…

构建Web3社交平台:DeBox式DApp开发全攻略

要仿照DeBox构建一款Web3社交平台系统&#xff0c;首先需要理解DeBox的核心功能和技术架构&#xff0c;并根据自己的目标和用户需求进行调整和创新。以下是一个基本的开发步骤指南&#xff0c;帮助你从概念到实践&#xff0c;逐步构建一个类似的Web3社交平台。 1. 明确项目目标…

vue3 +百度地图 实现 地点检索,输入联想,经纬度,逆地理编码,创建标记,label等

由于百度地图文档确实有点欠缺&#xff0c;在这里记录一下 vue3 百度地图&#xff08;js api 3.0&#xff09;实现效果如下实现方式注意事项 vue3 百度地图&#xff08;js api 3.0&#xff09; 需求&#xff1a; 地图弹框组件&#xff0c;可以搜索地图点&#xff0c;输入联想…

算法-双指针技巧

文章目录 算法概述奇偶数字归位寻找重复数字接雨水救生艇问题 算法概述 设置两个指针的技巧&#xff0c;其实这种说法很宽泛&#xff0c;似乎没什么可总结的 有时候所谓的双指针技巧&#xff0c;就单纯是代码过程用双指针的形式表达出来而已。 没有单调性(贪心)方面的考虑有时…

基于Python的网络编程

现代的应用程序都离不开网络&#xff0c;网络编程是非常重要的技术。Python提供了两个不同层次的网络编程API&#xff1a;基于Socket的低层次网络编程和基于URL的高层次网络编程。Sockrt采用TCP、UDP等协议&#xff0c;这些协议属于低层次的通信协议&#xff1b;URL采用HTTP和H…

4G工业路由器:SR700的智能连接解决方案

在现代工业环境中&#xff0c;网络连接的稳定性和速度是确保生产效率和数据安全的关键。SR700 4G工业路由器凭借其卓越的性能和多样的功能&#xff0c;成为了工业自动化和物联网应用中的理想选择。本文将详细介绍SR700的产品特点&#xff0c;并结合一个真实的项目案例&#xff…

网络传输的基本流程

目录 0.前言 1.TCP/IP四层协议模型的认识 2.数据传输的大致流程 3.局域网通信的原理 4.同一网段下两台主机之间的通信 5.不同网段下两台主机之间的通信 0.前言 不知道你有没有这样的疑问&#xff0c;为什么不同的设备之间能够进行数据的发送和接收&#xff1f;不同的通信…

小米嵌入式面试题目RTOS面试题目 嵌入式面试题目

第一章-非RTOS bootloader工作流程 MCU启动流程 通信协议&#xff0c;SPI IIC MCU怎么选型&#xff0c;STM32F1和F4有什么区别 外部RAM和内部RAM区别&#xff0c;怎么分配 外部总线和内部总线区别 MCU上的固件&#xff0c;数据是怎么分配的 MCU启动流程 IAP是怎么升级的…