原理就是监听UILabel.onPostFill事件,然后修改里面的verts等属性
mLabel = GetComponent();
mLabel.onPostFill += OnPostFillHandle;
void OnPostFillHandle(UIWidget widget, int bufferOffset, BetterList verts, BetterList uvs, BetterList cols){
int max = mEffects.Count;
for (int i = 0; i < max; i++) {
var effectEntry = mEffects [i];
int start = effectEntry.index * 4;
int end = start + effectEntry.text.Length * 4;
if (start > verts.size)
continue;
if (end > verts.size)
continue;
ApplyEffects (verts, uvs, cols, start, end, effectEntry);
}
}
void ResetEffectValue(){
wordTempValue.Clear ();
}
void ApplyEffects(BetterList verts, BetterList uvs, BetterList cols, int start, int end,EffectEntry effectEntry){
for (int i = start; i < end; ++i) {
Vector3 vert = verts [i];
Color32 color = cols [i];
byte alpha = color.a;
string key = effectEntry.key;
if (key.StartsWith ("{!")) {
vert.x += angryStrength * (Random.insideUnitCircle * angryJitterStrength).x * (i % 2 == 0 ? 1 : -1);
vert.y += angryStrength * (Random.insideUnitCircle * angryJitterStrength).y * (i % 2 == 0 ? 1 : -1);
color = angryColor;
} else if (key.StartsWith ("{~")) {
int wordIndex = (int)((float)i / 4f) - start;
float wordInterval = wordIndex * waveGap;
float angle = GetWordVar (i, wordInterval) + waveSpeed;
vert.y += Mathf.Sin (angle) * waveDistance;
color = waveColor;
SetWordVar (i, angle);
}
else if(key.StartsWith ("{@")){
float angle = GetWordVar (i) + waveSpeed;
vert.x += Mathf.Cos(angle) * angryStrength - Mathf.Sin(angle) * angryStrength;
vert.y += Mathf.Cos(angle) * angryStrength + Mathf.Sin(angle) * angryStrength;
color = waveColor;
SetWordVar (i, angle);
}
else if(key.StartsWith ("{#")){
float angle = GetWordVar (i) + happySpeed;
vert.y += Mathf.Sin (angle) * happyDistance;
color = happyColor;
SetWordVar (i, angle);
}
color.a = alpha;
verts [i] = vert;
cols [i] = color;
}
}
效果预览:
完整代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Text.RegularExpressions;
[RequireComponent(typeof(UILabel))]
public class SmartUILabel : MonoBehaviour {
public struct EffectEntry
{
public int id;
public int index;
public string key;
public string text;
}
public struct SplitEntry
{
public int id;
public int index;
public string left;
public string right;
public string total;
public string value;
}
///
/// How many characters will be printed per second.
///
public int charsPerSecond = 20;
public bool keepFullDimensions = true;
public float angryStrength = 2f;
public float angryJitterStrength = 2f;
public Color angryColor = Color.red;
public float waveGap = 0.5f;
public float waveSpeed = 0.2f;
public float waveDistance = 5f;
public Color waveColor=Color.yellow;
public float happyDistance = 5f;
public Color happyColor=Color.yellow;
public float happySpeed = 0.25f;
public UnityEngine.Events.UnityEvent onFinished;
UILabel mLabel;
string mFullText = "";
int mCurrentOffset = 0;
float mNextChar = 0f;
bool mReset = true;
bool mActive = false;
int mTempId=0;
List mEffects=new List();
List mSplitEntries=new List();
public bool IsActive { get { return mActive; } }
void OnEnable () { mReset = true; mActive = true; }
void OnDisable () { Finish(); }
public void Replay(){
mReset = true;
mActive = true;
mNextChar = 0f;
mCurrentOffset = 0;
Update();
}
public void Finish ()
{
if (mActive)
{
mActive = false;
if (!mReset)
{
mCurrentOffset = mFullText.Length;
mLabel.text = mFullText;
}
onFinished.Invoke ();
}
}
void Update () {
if (mActive) {
if (mReset)
{
mReset = false;
Reset ();
}
if (string.IsNullOrEmpty(mFullText)) return;
while (mCurrentOffset < mFullText.Length && mNextChar <= RealTime.time) {
int lastOffset = mCurrentOffset;
char lastChar = mFullText [lastOffset];
++mCurrentOffset;
if (mCurrentOffset > mFullText.Length) break;
char currentChar = (mCurrentOffset <= mFullText.Length - 1) ? mFullText [mCurrentOffset] : '\0';
float delay = 1f / charsPerSecond;
if (mNextChar == 0f)
{
mNextChar = RealTime.time + delay;
}
else mNextChar += delay;
if (lastChar=='{' && (currentChar == '<' || currentChar=='>')) {
int keyEndIndex = mFullText.IndexOf ('}', mCurrentOffset);
if (keyEndIndex != -1) {
string realKey = mFullText.Substring (lastOffset, keyEndIndex - lastOffset + 1);
mFullText = mFullText.Remove (lastOffset, keyEndIndex - lastOffset + 1);
mCurrentOffset -= 1;
bool isSpeedUp = realKey.IndexOf (">>") != -1;
Regex reg = new Regex (@"[{}><\s]");
string k = reg.Replace (realKey,"");
float realValue = 0f;
float.TryParse (k, out realValue);
mNextChar += realValue * (isSpeedUp ? 1f : -1f);
}
}
if (mCurrentOffset >= mFullText.Length) {
mLabel.text = mFullText;
onFinished.Invoke ();
mActive = false;
} else {
mLabel.text = keepFullDimensions ?
mFullText.Substring (0, mCurrentOffset) + "[00]" + mFullText.Substring (mCurrentOffset) :
mFullText.Substring (0, mCurrentOffset);
}
}
}
if (HasEffect) {
mLabel.MarkAsChanged ();
}
}
void SplitValues(){
SplitValue ("!");
SplitValue ("~");
SplitValue ("@");
SplitValue ("#");
foreach (var oldEffect in mEffects) {
Debug.Log ("Key=" + oldEffect.key + " value:" + oldEffect.text + " Index:" + oldEffect.index);
}
}
void SplitValue(string matchKey){
string content=mFullText;
string splitRegex = "({" + matchKey + @"})(\S*?)({/" + matchKey + "})";
string newContent = content;
mSplitEntries.Clear ();
foreach (Match match in Regex.Matches(content, splitRegex)) {
string key = matchKey;
string text = match.Groups [2].Value;
int index = match.Index;
string total = match.Value;
string left = match.Groups [1].Value;
string right = match.Groups [3].Value;
SplitEntry splitEntry = new SplitEntry ();
splitEntry.id = ++mTempId;
splitEntry.index = index;
splitEntry.left = left;
splitEntry.right = right;
splitEntry.value = text;
splitEntry.total = total;
mSplitEntries.Add (splitEntry);
}
for (int i = 0; i < mSplitEntries.Count; i++) {
SplitEntry splitEntry = mSplitEntries [i];
string total = splitEntry.total;
string left = splitEntry.left;
string right = splitEntry.right;
int rightIndex = total.IndexOf (right);
total = total.Remove (rightIndex, right.Length);
int leftIndex = total.IndexOf (left);
total = total.Remove (leftIndex, left.Length);
int newRightIndex = splitEntry.index + rightIndex;
newContent = newContent.Remove (newRightIndex, right.Length);
if (newRightIndex < splitEntry.index) {
splitEntry.index -= right.Length;
}
int newLeftIndex = splitEntry.index + leftIndex;
newContent = newContent.Remove (newLeftIndex, left.Length);
if (newLeftIndex < splitEntry.index ) {
splitEntry.index -= left.Length;
}
for (int j = 0; j < mSplitEntries.Count; j++) {
var newSplitEntry = mSplitEntries [j];
if (newSplitEntry.id.Equals(splitEntry.id))
continue;
if (newRightIndex < newSplitEntry.index) {
newSplitEntry.index -= right.Length;
}
if (newLeftIndex < newSplitEntry.index ) {
newSplitEntry.index -= left.Length;
}
mSplitEntries [j] = newSplitEntry;
}
for (int j = 0; j < mEffects.Count; j++) {
var newEffectEntry = mEffects [j];
if (newEffectEntry.id.Equals(splitEntry.id))
continue;
if (newRightIndex < newEffectEntry.index) {
newEffectEntry.index -= right.Length;
}
if (newLeftIndex < newEffectEntry.index ) {
newEffectEntry.index -= left.Length;
}
mEffects [j] = newEffectEntry;
}
EffectEntry effectEntry = new EffectEntry ();
effectEntry.id = splitEntry.id;
effectEntry.index = splitEntry.index;
effectEntry.key = splitEntry.left;
effectEntry.text = splitEntry.value;
mEffects.Add (effectEntry);
mSplitEntries [i] = splitEntry;
}
mFullText = newContent;
}
void Reset(){
if (mLabel != null) {
mLabel.onPostFill -= OnPostFillHandle;
}
ResetEffectValue ();
mEffects.Clear ();
mSplitEntries.Clear ();
mCurrentOffset = 0;
mTempId = 0;
mLabel = GetComponent();
mLabel.onPostFill += OnPostFillHandle;
mFullText = mLabel.processedText;
SplitValues ();
}
void OnPostFillHandle(UIWidget widget, int bufferOffset, BetterList verts, BetterList uvs, BetterList cols){
int max = mEffects.Count;
for (int i = 0; i < max; i++) {
var effectEntry = mEffects [i];
int start = effectEntry.index * 4;
int end = start + effectEntry.text.Length * 4;
if (start > verts.size)
continue;
if (end > verts.size)
continue;
ApplyEffects (verts, uvs, cols, start, end, effectEntry);
}
}
void ResetEffectValue(){
wordTempValue.Clear ();
}
void ApplyEffects(BetterList verts, BetterList uvs, BetterList cols, int start, int end,EffectEntry effectEntry){
for (int i = start; i < end; ++i) {
Vector3 vert = verts [i];
Color32 color = cols [i];
byte alpha = color.a;
string key = effectEntry.key;
if (key.StartsWith ("{!")) {
vert.x += angryStrength * (Random.insideUnitCircle * angryJitterStrength).x * (i % 2 == 0 ? 1 : -1);
vert.y += angryStrength * (Random.insideUnitCircle * angryJitterStrength).y * (i % 2 == 0 ? 1 : -1);
color = angryColor;
} else if (key.StartsWith ("{~")) {
int wordIndex = (int)((float)i / 4f) - start;
float wordInterval = wordIndex * waveGap;
float angle = GetWordVar (i, wordInterval) + waveSpeed;
vert.y += Mathf.Sin (angle) * waveDistance;
color = waveColor;
SetWordVar (i, angle);
}
else if(key.StartsWith ("{@")){
float angle = GetWordVar (i) + waveSpeed;
vert.x += Mathf.Cos(angle) * angryStrength - Mathf.Sin(angle) * angryStrength;
vert.y += Mathf.Cos(angle) * angryStrength + Mathf.Sin(angle) * angryStrength;
color = waveColor;
SetWordVar (i, angle);
}
else if(key.StartsWith ("{#")){
float angle = GetWordVar (i) + happySpeed;
vert.y += Mathf.Sin (angle) * happyDistance;
color = happyColor;
SetWordVar (i, angle);
}
color.a = alpha;
verts [i] = vert;
cols [i] = color;
}
}
Dictionary wordTempValue=new Dictionary();
float GetWordVar(int index){
return GetWordVar (index, 0f);
}
float GetWordVar(int index,float defaultValue){
if (!wordTempValue.ContainsKey (index)) {
wordTempValue.Add (index, defaultValue);
}
return wordTempValue [index];
}
void SetWordVar(int index,float value){
if (!wordTempValue.ContainsKey (index)) {
wordTempValue.Add (index, 0f);
}
wordTempValue [index] = value;
}
bool HasEffect{
get{
return mEffects.Count > 0;
}
}
}