TextMeshPro图文混排的两种实现方式,不打图集

TMP图文混排

接到一个需求,TextMeshPro 图文混排。

方案一:TMP自带图文混排

优点 布局适应优秀,字体左中右布局位置都很不错
缺点 用到的图片需要打包,使用Resources加载

使用方法

打包图集

打包图集这里推荐使用TexturePacker工具,功能强大,完美适配。这个软件也可以用于压缩图集。

TexturePacker链接

1.将你需要的散图或文件夹拖到这个区域,或使用添加精灵按钮添加。

2.选中下图按钮,选择Json(Array), 注意是Array。

下面的图片模式和打包可以自己尝试,主要是控制压缩和图片格式,这里不做介绍。

3.点击发布,获得一个Json文件和一张PNG,Json 是PNG各个图片的位置信息。

4.将上一步的资源导入Unity,然后打开TextMeshPro的图集工具 Sprite Importer。

5.将Json 和PNG 拖入下图中对应的位置。点击生成,然后存储到自己的文件夹

使用

1.将生成的图集拖入TMP的Sprite Asset中,使用图片时,在字符串中插入<sprite=你的图片id>,例如图中"<sprite=0> 图片好看吗"

2. ??? 图片咋这样??? ,发现图片大小不对,被勾选了自动转变为2的n次方。这里改成None ,图片能完整显示了

3. 还是不对,位置偏差好多,按下图2修改X和Y的偏移,最下方的Global Offset 是同时修改所有图片

完成啦,撒花~~

方案二:不打图集,可以使用任何图片

优点 当你有大量图片都可能用于图文混排,不方便打包时使用。代码没多少,随便修随便改- -
缺点 对布局适配一般,只支持了整个组件的对齐,文字只支持了左对齐

原理就是将含图片的文本拆分成几段文本和图片的组合,计算位置,使用多个TMP和图片拼接

图片暂定格式 [img] 图片ID[/img]

csharp 复制代码
using System.Collections.Generic;
using System.Text.RegularExpressions;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

public class TextImageView : MonoBehaviour
{
   
    public enum TextImageAlignmentH
    {
        Left,
        Center,
        Right
    }
    public enum TextImageAlignmentV
    {
        Top,
        Center,
        Bottom
    }
    public Transform content;
    public int FontSize;
    public float maxWidth;
    public TextMeshProUGUI tmp;
    public Image img;
    public TextImageAlignmentH alignmentH = TextImageAlignmentH.Center;
    public TextImageAlignmentV alignmentV = TextImageAlignmentV.Center;
    private float _lineHeight;
    private float _averageWidth;
    private float _maxLength;
    private List<KeyValuePair<string, int>> _contentList = new List<KeyValuePair<string, int>>();
    public string text
    {
        set { Test(value); }
    }

    public void Test(string text)
    {
        _contentList = StringToKeyValuePair(text);
        var reduceWidth = 0f;
        var height = 0f;
        tmp.fontSize = FontSize;
        tmp.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, maxWidth);
        tmp.gameObject.SetActive(true);
        tmp.text = "test";
        tmp.ForceMeshUpdate();
        _lineHeight = tmp.textInfo.lineInfo[0].lineHeight;
        tmp.gameObject.SetActive(false);
        
        content.DestroyChildren();
        for (var i = 0; i < _contentList.Count; i++)
        {
            if (_contentList[i].Value == 0)
            {
                var label = TestText(_contentList[i].Key, reduceWidth,height);
                
                for (var i1 = 0; i1 < label.textInfo.lineInfo.Length; i1++)
                {
                    var lineInfo = label.textInfo.lineInfo[i1];
                    var width = lineInfo.length;
                    if(_maxLength < width)
                    {
                        _maxLength = width;
                    }
                    
                    if(i1 == label.textInfo.lineCount - 1)
                    {
                        height = (label.transform.localPosition.y - i1*_lineHeight);
                        reduceWidth = maxWidth - width - 5;
                    }
                }

            }
            else
            {
                
                var imgData = TestImage(_contentList[i].Key, reduceWidth, height);
                reduceWidth = imgData.reduce + 5;
                if (_maxLength < (maxWidth - imgData.reduce))
                {
                    _maxLength = maxWidth - imgData.reduce;
                }
                if(imgData.changeLine)
                {
                    height -= _lineHeight;
                }
            }
            
        }
        height -= _lineHeight;
        var x = 0f;
        switch (alignmentH)
        {
            case TextImageAlignmentH.Left:
                x = 0;
                break;
            case TextImageAlignmentH.Center:
                x = (maxWidth - _maxLength) / 2;
                break;
            case TextImageAlignmentH.Right:
                x = maxWidth - _maxLength;
                break;
        }
        
        var y = 0f;
        switch (alignmentV)
        {
            case TextImageAlignmentV.Top:
                y = _lineHeight;
                break;
            case TextImageAlignmentV.Center:
                y = -height/2 +_lineHeight/2;
                break;
            case TextImageAlignmentV.Bottom:
                y = -height;
                break;
        }
        content.transform.localPosition = new Vector3(x,y,0);
    }
    // <color=#FFFFFF><sprite=1/> 计分时\n<color=#FF0000>+4 </color>倍率</color>
    private List<KeyValuePair<string, int>> StringToKeyValuePair(string text)
    {
        var result = new List<KeyValuePair<string, int>>();

        // 拆分字符串,匹配图片名和文字内容
        var splitText = Regex.Split(text, @"(\[img\]|\[/img\])");

        // 遍历拆分后的字符串
        for (var i = 0; i < splitText.Length; i++)
        {
            switch (splitText[i])
            {
                case "[img]":
                {
                    // 如果是图片名,将它加入结果列表
                    result.Add(new KeyValuePair<string, int>(splitText[i + 1], 1));
                    // 跳过处理下一个字符串
                    i++;
                }
                    continue;
                case "[/img]":
                    // 如果是 [/img] ,跳过处理下一个字符串
                    continue;
                default:
                {
                    var content = splitText[i];
                    if (string.IsNullOrEmpty(content))
                    {
                        continue;
                    }
                    result.Add(new KeyValuePair<string, int>(content, 0));
                }
                    break;
            }
        }

        return result;
    }
 
    public string testValue;
    [ContextMenu("测试")]
    
    public void Test1()
    {
        // TestText(testValue, 300,0);
        Test(testValue);
    }

    private TextMeshProUGUI TestText(string value,float reduceWidth,float height)
    {
        var obj = GameObject.Instantiate((UnityEngine.Object)tmp.gameObject) as GameObject;
        //GameObject obj = null;
        if (obj == null)
        {
            return null;
        }
        obj.SetActive(true);
        if(_averageWidth == 0)
        {
            _averageWidth = obj.GetComponent<TextMeshProUGUI>().GetPreferredValues(value).x/ value.Length;
        }
        var label = obj.GetComponent<TextMeshProUGUI>();
        label.fontSize = FontSize;
        label.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, maxWidth); 
        obj.transform.SetParent(content);
        obj.transform.localScale = Vector3.one;
        if (reduceWidth <= _averageWidth)//剩下的空间不够一个字,直接换行
        {
            obj.transform.localPosition = new Vector3(0, height - _lineHeight, 0);
        }
        else
        {
          
            value = $"<space={maxWidth - reduceWidth}>{value}"; // 创建新字符串
            obj.transform.localPosition = new Vector3(0, height, 0); 
        }
      
        
        value = DelUrlFlag(value);
        
        label.text = value;
        
        label.ForceMeshUpdate();
        return label; // 计算剩余宽度
        
    }
   
    private (bool changeLine,float reduce) TestImage(string value,float reduceWidth,float height)
    {
        
        var obj = GameObject.Instantiate((UnityEngine.Object)img.gameObject) as GameObject;
        if (obj == null)
        {
            return (false,reduceWidth);
        }
        obj.SetActive(true);
        var sp = obj.GetComponent<Image>();
        sp.SetIcon(int.Parse(value));//value是图片的ID,这里替换为自己的设置图片的方法
        var tempHeight = _lineHeight ;
        sp.SetNativeSize();
        var size = sp.rectTransform.sizeDelta;
        var rate = size.y / tempHeight;
        sp.rectTransform.sizeDelta = new Vector2(size.x / rate, tempHeight);
        var needWidth = sp.rectTransform.sizeDelta.x;
        obj.transform.SetParent(content);
        obj.transform.localScale = Vector3.one;
        if (needWidth > reduceWidth)
        {
            obj.transform.localPosition = new Vector3(-maxWidth/2 +needWidth/2, height - _lineHeight, 0);
            return (true,maxWidth -needWidth);
        }
        else
        {
            obj.transform.localPosition = new Vector3(-maxWidth/2 + (maxWidth - reduceWidth)+needWidth/2, height, 0);
            return (false,reduceWidth - needWidth);
        }
        
      
    }
    private string DelUrlFlag(string strTxt)
    {
        strTxt = strTxt.Replace("[/url]", "");
        int nStartLeftBracket = 0;
        int nStartRightBracket = 0;

        while (true)
        {
            nStartLeftBracket = strTxt.IndexOf('[', nStartLeftBracket);
            nStartRightBracket = strTxt.IndexOf(']', nStartRightBracket);

            if (nStartLeftBracket > -1 && nStartRightBracket > -1 && nStartRightBracket > nStartLeftBracket)
            {
                string sub = strTxt.Substring(nStartLeftBracket, nStartRightBracket - nStartLeftBracket + 1);
                if (sub.Contains("url"))
                {
                    strTxt = strTxt.Remove(nStartLeftBracket, nStartRightBracket - nStartLeftBracket + 1);

                    nStartLeftBracket = 0;
                    nStartRightBracket = 0;
                }
                else
                {
                    nStartLeftBracket = nStartRightBracket;
                    nStartRightBracket += 1;
                }
            }
            else
            {
                break;
            }
        }

        return strTxt;
    }
}
相关推荐
mxwin7 小时前
Unity Shader 半透明物体为什么不能写入深度缓冲?
unity·游戏引擎·shader
晚枫歌F8 小时前
三层时间轮的实现
网络·unity·游戏引擎
咸鱼永不翻身9 小时前
Lua脚本事件检查工具
unity·lua·工具
leo__52011 小时前
单载波中继系统资源分配算法MATLAB仿真程序
算法·matlab·unity
努力长头发的程序猿12 小时前
Unity使用ScriptableObject序列化资源
unity·游戏引擎
mxwin12 小时前
Unity Shader 手写基于 PBR 的 URP Lit Shader 核心光照计算
unity·游戏引擎·shader
小贺儿开发13 小时前
Unity3D 智能云端数字标牌系统
unity·阿里云·人机交互·视频·oss·广告·互动
魔士于安13 小时前
Unity windows 同步 异步 打开文件文件夹工具
游戏·unity·游戏引擎·贴图·模型
魔士于安14 小时前
unity lowpoly 风格 城市 建筑 道路 交通标志
游戏·unity·游戏引擎·贴图·模型
mxwin14 小时前
Unity GPU Shader 性能优化指南
unity·游戏引擎·shader