本方案仅供参考
总结:简单试探了下把字画到同一张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行为统一。
就给个思路。有什么问题留言讨论下,作者本人也比较菜,多多指正。
随便改变大小