基于Unity实现的文字线条效果,代码添加到Text组件的物体上修改Type属性即可看到效果。
2021/4/22. 添加分辨率适配
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.UI;public enum LineType {none,upperline,middleLine,underLine
}public class Line {public Vector2 position = Vector2.zero;public Vector2 size = Vector2.one;public Vector2 pivot = Vector2.zero;public Color color = Color.black;public Line(Text text, LineType type, int index) {var characters = text.cachedTextGenerator.characters;var lines = text.cachedTextGenerator.lines[index];UICharInfo charInfo = characters[lines.startCharIdx];CanvasScaler cs = text.GetComponentInParent<CanvasScaler>();float scaleFactor = (Screen.width * cs.referenceResolution[1] / (Screen.height * cs.referenceResolution[0])) >= 1 ? cs.referenceResolution[1] / Screen.height : cs.referenceResolution[0] / Screen.width;position = CalculatePosition(text.alignment, text.rectTransform.rect.width,(charInfo.cursorPos.y - text.cachedTextGenerator.lines[index].height * GetTypeWeight(type)) * scaleFactor);size = new Vector2(GetWidth(characters, lines.startCharIdx, characters.Count - 1, text.rectTransform.rect.width),text.fontSize * 0.1f == 0 ? float.Epsilon : text.fontSize * 0.1f) * scaleFactor;color = text.color;SetPivot(text.alignment);}// public Line(Text text, LineType type, int index, int start, int length) {// UICharInfo charInfo = text.cachedTextGenerator.characters[start];// position = new Vector2(charInfo.cursorPos.x, charInfo.cursorPos.y - text.cachedTextGenerator.lines[index].height * GetTypeWeight(type));// size = new Vector2(GetWidth(text.cachedTextGenerator.characters, start, length - 1),// text.fontSize * 0.1f == 0 ? float.Epsilon : text.fontSize * 0.1f);// color = text.color;// SetPivot(text.alignment);// }float GetTypeWeight(LineType type) {switch (type) {case LineType.upperline:return 0;case LineType.middleLine:return 0.5f;case LineType.underLine:default:return 1;}}float GetWidth(IList<UICharInfo> characters, int start, int end, float width) {float num = 0;int lastCharIdx = end - 1;for (int i = start; i < end; ++i) {if (characters[i].cursorPos[0] > characters[i + 1].cursorPos[0]) {num = characters[i].cursorPos[0] + characters[i].charWidth - characters[start].cursorPos[0];break;}if (i == lastCharIdx) {num = characters[i + 1].cursorPos[0] - characters[start].cursorPos[0];break;}}return num >= width ? width * 0.5f : num;}public Vector2 CalculatePosition(TextAnchor anchor, float width, float y) {switch (anchor) {case TextAnchor.MiddleCenter:case TextAnchor.UpperCenter:case TextAnchor.LowerCenter:return Vector2.up * y;case TextAnchor.MiddleRight:case TextAnchor.UpperRight:case TextAnchor.LowerRight:return new Vector2(width * 0.5f, y);default:return new Vector2(-width * 0.5f, y);}}void SetPivot(TextAnchor anchor) {switch (anchor) {case TextAnchor.LowerLeft:pivot = Vector2.zero;break;case TextAnchor.LowerCenter:pivot = new Vector2(0.5f, 0);break;case TextAnchor.LowerRight:pivot = new Vector2(1, 0);break;case TextAnchor.MiddleLeft:pivot = new Vector2(0, 0.5f);break;case TextAnchor.MiddleCenter:pivot = Vector2.one * 0.5f;break;case TextAnchor.MiddleRight:pivot = new Vector2(1, 0.5f);break;case TextAnchor.UpperLeft:pivot = new Vector2(0, 1);break;case TextAnchor.UpperCenter:pivot = new Vector2(0.5f, 1);break;case TextAnchor.UpperRight:pivot = Vector2.one;break;default:pivot = Vector2.zero;break;}}
}public class LineMesh : MonoBehaviour {private Text text;public LineType type = LineType.none;private bool isInitialize = false;private int characterCount = 0;private LineType currentType = LineType.none;private Color currentColor = Color.white;private FontStyle currentFontStyle;private TextAnchor currentAnchor;private float lineSpacing;private List<Image> lines;// 标签化正则, 暂时未实现// 删除线//private const string deleteLineEvaluator = @"~~[^~]+?~~";//private const string deleteLineContentEvaluator = @"(?<=~~)[^~]+?(?=~~)";// 上划线//private const string underLineEvaluator = @"<up>.*</up>";//private const string underLineContentEvaluator = @"(?<=<up>).*(?=</up>)";// 下划线//private const string underLineEvaluator = @"<un>.*</un>";//private const string underLineContentEvaluator = @"(?<=<un>).*(?=</un>)";private void Awake() {text = this.GetComponent<Text>();lines = new List<Image>();}// Start is called before the first frame updateprivate void Start() {UpdateParams();isInitialize = true;}// Update is called once per frameprivate void Update() {if (isInitialize) {if (type == LineType.none && currentType != type) {currentType = type;ClearLines();} else if (currentType != type || characterCount != text.cachedTextGenerator.characterCount ||currentColor != text.color || currentFontStyle != text.fontStyle ||currentAnchor != text.alignment || lineSpacing != text.lineSpacing) {UpdateParams();List<Line> lines = GetLines(type);CreateLines(lines);}}}private void UpdateParams() {characterCount = text.cachedTextGenerator.characterCount;currentType = type;currentColor = text.color;currentFontStyle = text.fontStyle;currentAnchor = text.alignment;lineSpacing = text.lineSpacing;}private List<Line> GetLines(LineType type) {if (type == LineType.none) return null;List<Line> lineInfos = new List<Line>();for (int j = 0; j < text.cachedTextGenerator.lineCount; ++j) {Line line = new Line(text, type, j);if (line.size[0] == 0 || line.size[1] == 0)continue;lineInfos.Add(line);}return lineInfos;}private void ClearLines() {foreach (var line in lines)Destroy(line.gameObject);lines.Clear();}private void CreateLines(List<Line> lineInfos) {if (lineInfos == null) return;ClearLines();for (int i = 0; i < lineInfos.Count; ++i) {Image img = new GameObject().AddComponent<Image>();img.transform.SetParent(text.transform, false);img.name = $"line{i}";img.color = lineInfos[i].color;img.rectTransform.pivot = lineInfos[i].pivot;lines.Add(img);Texture2D texture = new Texture2D((int)lineInfos[i].size[0], (int)lineInfos[i].size[1], TextureFormat.ARGB32, false);var colors = texture.GetPixels();for (int j = 0; j < colors.Length; ++j)colors[j] = img.color;texture.SetPixels(colors);texture.Apply();img.sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), Vector2.zero);img.SetNativeSize();img.rectTransform.sizeDelta = img.rectTransform.sizeDelta;lines[i].rectTransform.anchoredPosition = lineInfos[i].position;}}
}