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;
    }
}
相关推荐
向宇it17 小时前
【零基础入门unity游戏开发——2D篇】SortingGroup(排序分组)组件
开发语言·unity·c#·游戏引擎·材质
omegayy2 天前
Unity 2022.3.x部分Android设备播放视频黑屏问题
android·unity·视频播放·黑屏
与火星的孩子对话2 天前
Unity3D开发AI桌面精灵/宠物系列 【三】 语音识别 ASR 技术、语音转文本多平台 - 支持科大讯飞、百度等 C# 开发
人工智能·unity·c#·游戏引擎·语音识别·宠物
向宇it2 天前
【零基础入门unity游戏开发——2D篇】2D 游戏场景地形编辑器——TileMap的使用介绍
开发语言·游戏·unity·c#·编辑器·游戏引擎
牙膏上的小苏打23333 天前
Unity Surround开关后导致获取主显示器分辨率错误
unity·主屏幕
Unity大海3 天前
诠视科技Unity SDK开发环境配置、项目设置、apk打包。
科技·unity·游戏引擎
浅陌sss3 天前
Unity中 粒子系统使用整理(一)
unity·游戏引擎
维度攻城狮3 天前
实现在Unity3D中仿真汽车,而且还能使用ros2控制
python·unity·docker·汽车·ros2·rviz2
为你写首诗ge3 天前
【Unity网络编程知识】FTP学习
网络·unity
神码编程3 天前
【Unity】 HTFramework框架(六十四)SaveDataRuntime运行时保存组件参数、预制体
unity·编辑器·游戏引擎