文本抖动效果
前言
在部分电子游戏中,当角色处于狂喜、紧张或恐惧等激动情绪时,角色对话框中的文字会触发抖动等效果,这为游戏增色不少,如下。
当我在网上查找相关资料时,没找到相关的实现,也可能是我搜索的关键词不对。
总之今天来实现一下这个效果。
实现思路
目标效果是:在同一帧的动效中,文本中的每个字符都各自向随机方向偏移了一小段距离。
传统的Text
组件我不清楚能不能成,但是TextMeshPro
一定能成,因为TextMeshPro
中的文本渲染是基于Mesh
的,只要能拿到每个字符对应的Mesh
顶点数据,就能单独给每个字符设置位置、颜色等数据。如果不明白的话去补一下Mesh
相关知识就好。
实现思路如下:
- 获取
TextMeshPro
中的顶点数组和每个字符。 - 遍历每个字符,对每一个字符,都随机生成偏移量,并将该偏移量应用在这个字符对应的所有顶点数据上。
- 更新组件渲染。
可能的困难
我们需要在同一帧内随机生成各个字符的偏移,这些偏移要保证各不相同。
在同一帧中,除非改变传入的min
或max
的值,否则无论调用多少次,引擎中的Random.Range(min, max)
的返回值都会是同一个数。
解决方案:基于内存地址,使用C#
的System.Random
,详情见->Unity并发取随机导致相同解决方法。
代码实现
using System;
using System.Collections;
using System.Runtime.InteropServices;
using TMPro;
using UnityEngine;
public class FontSingleBeat : MonoBehaviour
{
TextMeshProUGUI text;
/// <summary>
/// 速度(时间间隔)
/// </summary>
public float shakeSpeed = 0.05f;
/// <summary>
/// 幅度
/// </summary>
public float shakeAmount = 1f;
private Vector3[] m_rawVertex;
private void Awake()
{
text = this.GetComponent<TextMeshProUGUI>();
text.ForceMeshUpdate();
}
private void Start()
{
GetRawVertex();
StartCoroutine(ShakeText());
}
private void GetRawVertex()
{
if(text.textInfo.characterCount > 0)
{
TMP_CharacterInfo charInfo = text.textInfo.characterInfo[0];
TMP_MeshInfo meshInfo = text.textInfo.meshInfo[charInfo.materialReferenceIndex];
//创建对象来保存初始值
m_rawVertex = new Vector3[meshInfo.vertices.Length];
for (int i = 0; i < meshInfo.vertices.Length; i++)
{
m_rawVertex[i] = new Vector3(meshInfo.vertices[i].x, meshInfo.vertices[i].y, meshInfo.vertices[i].z);
}
}
else
{
Debug.LogError("GetRawVertex Failed.");
}
}
IEnumerator ShakeText()
{
while (true)
{
for (int i = 0; i < text.textInfo.characterCount; i++)
{
// 获取字符信息和MeshInfo
TMP_CharacterInfo currentCharInfo = text.textInfo.characterInfo[i];
TMP_MeshInfo meshInfo = text.textInfo.meshInfo[currentCharInfo.materialReferenceIndex];
int vertexCount;
if (i < text.textInfo.characterCount - 1)
{
TMP_CharacterInfo nextCharInfo = text.textInfo.characterInfo[i + 1];
vertexCount = nextCharInfo.vertexIndex - currentCharInfo.vertexIndex;
}
else
{
vertexCount = meshInfo.vertices.Length - currentCharInfo.vertexIndex;
}
// 获取起始顶点索引
int vertexIndex = currentCharInfo.vertexIndex;
// 随机生成位移量
int mult = 100;
float xOffset = GetRandom((int)-shakeAmount * mult, (int)shakeAmount * mult, i);
float yOffset = GetRandom((int)-shakeAmount * mult, (int)shakeAmount * mult, i + text.textInfo.characterCount);
Vector3 offset = new Vector3(xOffset, yOffset) / 100f;
//print(xOffset + ", " + yOffset);
// 顶点偏移
Vector3[] vertices = meshInfo.vertices;
for (int j = vertexIndex; j < vertexIndex + vertexCount; j++)
{
vertices[j] = m_rawVertex[j] + offset;
}
// 刷新单个字符
//text.SetVerticesDirty();
//text.SetMaterialDirty();
}
text.UpdateVertexData();
yield return new WaitForSeconds(shakeSpeed);
}
}
/// <summary>
/// 获取基地址的
/// </summary>
/// <param name="o"></param>
/// <returns></returns>
public int GetMemory(object o)
{
GCHandle h = GCHandle.Alloc(o, GCHandleType.WeakTrackResurrection);
IntPtr addr = GCHandle.ToIntPtr(h);
return int.Parse(addr.ToString());
}
/// <summary>
/// 产生随机数
/// 调用它就可以产生你要的随机数了,如果有需求可以自己重载
/// </summary>
/// <param name="min">最小值</param>
/// <param name="Max">最大值</param>
/// <returns></returns>
public float GetRandom(int min, int Max, int iSeed)
{
System.Random rd = new System.Random(GetMemory(iSeed));
return (rd.Next(min, Max));
}
}
最终效果
上面一排是目标效果;下面一排是文本整体偏移抖动。
参考资料
Unity并发取随机导致相同解决方法_fairen的博客-CSDN博客
标签:字符,int,text,vertices,meshInfo,TextMeshPro,unity,textInfo,文本 From: https://www.cnblogs.com/OtusScops/p/17480963.html