Unity尾随渐变式打字机特效
继承BaseMeshEffect制作UI特效。
制作思路:
- 声明一个与文字数量同样长度的数组,用于储存每个文字的透明度。
- 用索引标记当前显示的文字。
- 每帧对从开始处到索引标记的位置进行透明度增加,并储存更新数组。
- 调用graphic.SetVerticesDirty,这样会触发ModifyMesh方法。
- 在ModifyMesh设置顶点透明度即可更新渲染。
注意:
一个文字由两个三角形构成,而在操作顶点流时,获取的顶点是两个三角的顶点,也就是说,一个文字的顶点数是6而非4,需要丢弃多余的顶点。
效果:
代码:
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
[AddComponentMenu("UI/Effects/Typewriter")]
[RequireComponent(typeof(Text))]
public class UIEff_Typewriter : BaseMeshEffect
{
//当前播放的字
[SerializeField]
private int currentIndex = 0;
private int renderDoneIndex = 0;
//每个字完成的透明度
[SerializeField]
private byte advanceInterval = 16;
public byte AdvanceInterval { get => advanceInterval; }
[SerializeField]
private bool isPlaying = false;
public bool IsPlaying { get => isPlaying; }
//每个网格的透明度
private byte[] opacity;
//播放结束
public event Action EndHandler;
[SerializeField]
private float speed = 0.5f;
public float Speed
{
get => speed;
set
{
speed = Math.Min(Math.Max(value, 0f), 1f);
}
}
protected override void Awake()
{
#if UNITY_EDITOR
if (!UnityEditor.EditorApplication.isPlaying) return;
#endif
if (!IsActive()) return;
base.Awake();
Refresh();
}
public void Refresh()
{
currentIndex = 0;
renderDoneIndex = 0;
opacity = null;
}
private void FixedUpdate()
{
if (!IsActive()) return;
if (!isPlaying) return;
if (opacity == null || opacity.Length == 0)
{
graphic.SetVerticesDirty();
return;
}
for (int w = 0; w < Mathf.Lerp(1, 10, speed); w++)
{
for (int i = renderDoneIndex; i < opacity.Length; i++)
{
//文字索引超过当前渲染文字
if (i > currentIndex) break;
//如果当前渲染字超过设定的完成度则前进
if (opacity[currentIndex] >= this.advanceInterval)
{
//不超过最大字数就前进一格字符
if (currentIndex < opacity.Length - 1)
++currentIndex;
}
if (opacity[i] < 255)
{
byte opacityStep = 2;
if ((int)opacity[i] + opacityStep > 255)
{
opacity[i] = 255;
}
else
{
opacity[i] += opacityStep;
}
}
if (opacity[i] == 255)
{
++renderDoneIndex;
}
}
graphic.SetVerticesDirty();
//最后一个透明度为1时结束
if (opacity[opacity.Length - 1] == 255)
{
Stop();
}
}
}
public void Play()
{
Refresh();
isPlaying = true;
}
public void Stop()
{
isPlaying = false;
if(opacity != null)
{
//显示全部文字
for (int i = 0; i < opacity.Length; i++)
{
opacity[i] = 255;
}
currentIndex = opacity.Length - 1;
}
graphic.SetVerticesDirty();
EndHandler?.Invoke();
}
public override void ModifyMesh(VertexHelper vh)
{
#if UNITY_EDITOR
if (!UnityEditor.EditorApplication.isPlaying) return;
#endif
if (!IsActive()) return;
List<UIVertex> verts = new List<UIVertex>();
vh.GetUIVertexStream(verts);
//6点转4点
List<UIVertex> vs = new List<UIVertex>();
for (int i = 1; i <= verts.Count; i += 3)
{
vs.Add(verts[i - 1]);
vs.Add(verts[i]);
}
//初始化每个字的数组长度
if (opacity == null) opacity = new byte[vs.Count / 4];
//设置透明
for (int i = 0; i < vs.Count; i += 4)
{
UIVertex v1 = vs[i + 0];
UIVertex v2 = vs[i + 1];
UIVertex v3 = vs[i + 2];
UIVertex v4 = vs[i + 3];
v1.color.a = opacity[i / 4];
v2.color.a = opacity[i / 4];
v3.color.a = opacity[i / 4];
v4.color.a = opacity[i / 4];
vh.SetUIVertex(v1, i + 0);
vh.SetUIVertex(v2, i + 1);
vh.SetUIVertex(v3, i + 2);
vh.SetUIVertex(v4, i + 3);
}
}
}
共有 0 条评论