美文网首页
Unity中,在Text中插入表情图

Unity中,在Text中插入表情图

作者: 全新的饭 | 来源:发表于2024-02-27 10:17 被阅读0次

示意

文字内容和实际效果

  • 测试表情图:[4][5][Laugh]\n[左边三条线]\n生气[愤怒]\n[眼巴巴地看着你]可怜巴巴\n[红脸歪嘴]


    文字内容中穿插表情.png

思路

  1. 用特定的文字内容(用中括号括起来)表示要插入的表情图:[笑脸]
  2. 运行时用空白字符字符替代前述特定文字内容
  3. 在各空白字符位置处创建Image,显示相应的表情图

使用步骤

创建表情图配置资源:

  • 在Project视图中,选中所有表情图,右键创建EmojiCfg。
    所有表情图都应为正方形。


    创建cfg.png
    创建完成.png

通过EmojiText显示:

  • 将前一步创建的Cfg拖入该Text的相应字段。


    设置Cfg.png
  • 通过调用SetText(带有表情图文字标记的完整文字内容)进行显示。


    在代码中设置文字内容.png

具体代码

表情图配置资源:FanEmojiCfg.cs

using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName = "FanEmojiCfg", menuName = "FanEmojiCfg", order = 0)]
public class FanEmojiCfg : ScriptableObject
{
    [SerializeField]
    private FanEmojiCfgItem[] _items;
    public FanEmojiCfgItem[] Items => _items;
    public void SetItems(Dictionary<string, Sprite> itemDict)
    {
        var items = new List<FanEmojiCfgItem>();
        foreach (var i in itemDict.Keys)
        {
            items.Add(new FanEmojiCfgItem() { Key = i, Sprite = itemDict[i] });
        }
        _items = items.ToArray();
    }

    public Sprite GetSprite(string key)
    {
        Sprite sprite = null;
        foreach (var i in Items)
        {
            if (i.Key == key)
            {
                sprite = i.Sprite;
                break;
            }
        }

        if (sprite == null)
        {
            Debug.Log($"{nameof(FanEmojiCfg)}中不存在名为 {key} 的图,请确认!");
            sprite = Items[0].Sprite;
        }
        return sprite;
    }
}
[System.Serializable]
public class FanEmojiCfgItem
{
    public string Key;
    public Sprite Sprite;
}

创建表情图配置资源(需放在Editor下):FanEmojiCfgBuilder.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Linq;

public class FanEmojiCfgBuilder : Editor
{
    [MenuItem(@"Assets/Fan/CreateFanEmojiCfg")]
    public static void CreateFanEmojiCfg()
    {
        var objs = Selection.objects;
        Dictionary<string, Sprite> dict = new Dictionary<string, Sprite>();
        foreach (var obj in objs)
        {
            if (obj is Texture2D tex)
            {
                var sprite = Texture2DToSprite(tex);
                dict.TryAdd(sprite.name, sprite);
            }
        }

        if (dict.Count > 0)
        {
            var path = AssetDatabase.GetAssetPath(dict.Values.ToArray()[0]);
            path = System.IO.Path.Join(System.IO.Path.GetDirectoryName(path), nameof(FanEmojiCfg) + ".asset");
            var cfg = ScriptableObject.CreateInstance<FanEmojiCfg>();
            cfg.SetItems(dict);
            AssetDatabase.CreateAsset(cfg, path);
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
        }


        Sprite Texture2DToSprite(Texture2D tex)
        {
            return AssetDatabase.LoadAssetAtPath<Sprite>(AssetDatabase.GetAssetPath(tex));
        }
    }
}

改造后的Text:FanEmojiText.cs

using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.UI;

[RequireComponent(typeof(Text))]
public class FanEmojiText : MonoBehaviour
{
    private string StrPattern => "\\[[\u4e00-\u9fa5_a-zA-Z0-9]+\\]";
    private string EmptyStr => "饭";
    private string ActualEmptyStr => "ㅤ";

    [SerializeField]
    private Text _text;
    [SerializeField]
    private FanEmojiCfg _emojiCfg;
    private List<GameObject> _spriteGos;
    public void SetText(string content)
    {
        StartCoroutine(SetTextCoroutine(content));
    }

    private IEnumerator SetTextCoroutine(string content)
    {
        DestroySpriteGos();
        // 提取其中的图片标签:若干个图片key-localPos
        MatchCollection matches = Regex.Matches(content, StrPattern);
        List<Sprite> sprites = new List<Sprite>();
        for (int i = 0; i < matches.Count; i++)
        {
            var str = matches[i].Value;
            var key = str.Replace("[", string.Empty).Replace("]", string.Empty);
            sprites.Add(_emojiCfg.GetSprite(key));
        }
        // 将前述所有图片标签的文字进行替换
        var actualContent = content;
        content = Regex.Replace(content, StrPattern, EmptyStr);
        actualContent = Regex.Replace(actualContent, StrPattern, ActualEmptyStr);
        // 显示文字
        content = content.Replace("\\n", "\n");
        actualContent = actualContent.Replace("\\n", "\n");
        _text.text = content;

        // LayoutRebuilder.ForceRebuildLayoutImmediate(_text.rectTransform);
        yield return new WaitForEndOfFrame();

        // 获取所有空白字符的局部坐标(按顺序)
        List<Vector3> spritesLocalPos = new List<Vector3>();
        int index = 0;
        for (int i = 0; i < content.Length; i++)
        {
            if (content[i].ToString() == EmptyStr)
            {
                spritesLocalPos.Add(CalculateCharLocalPos(index));
            }
            if (content[i].ToString() != "\n")
            {
                index++;
            }
        }
        // 显示真实文字内容
        _text.text = actualContent;
        // 创建所有图片并设置到相应位置
        CreateSpriteGos(sprites, spritesLocalPos);
    }

    private void DestroySpriteGos()
    {
        if (_spriteGos != null)
        {
            for (int i = 0; i < _spriteGos.Count; i++)
            {
                GameObject.Destroy(_spriteGos[i]);
            }
            _spriteGos = null;
        }
    }
    private void CreateSpriteGos(List<Sprite> sprites, List<Vector3> spritesLocalPos)
    {
        _spriteGos = new List<GameObject>();
        for (int i = 0; i < sprites.Count; i++)
        {
            _spriteGos.Add(CreateSpriteGo(sprites[i], spritesLocalPos[i]));
        }

        GameObject CreateSpriteGo(Sprite sprite, Vector3 localPos)
        {
            var go = new GameObject(sprite.name);
            var rectTrans = go.AddComponent<RectTransform>();
            rectTrans.sizeDelta = Vector2.one * _text.fontSize;
            var img = go.AddComponent<Image>();
            img.sprite = sprite;
            go.transform.SetParent(transform);
            go.transform.localPosition = localPos;
            go.transform.localScale = Vector3.one;
            return go;
        }
    }



    private void Reset()
    {
        _text = GetComponent<Text>();
    }

    private Vector3 CalculateCharLocalPos(int charIndex)
    {
        return CalculateCharLocalPos(_text.canvas, _text, charIndex);
    }
    /// <summary>
    /// 计算字符坐标
    /// </summary>
    /// <param name="canvas"> Text所在的Canvas </param>
    /// <param name="text"> 要计算字符坐在的Text </param>
    /// <param name="charIndex"> 要计算字符在字符串的下标 </param>
    private Vector3 CalculateCharLocalPos(Canvas targetCanvas, Text targetText, int targetCharIndex)
    {
        Vector3 avgPos = Vector3.zero;
        string calculateStr = targetText.text;

        TextGenerator textGen = new TextGenerator(calculateStr.Length);
        Vector2 extents = targetText.GetComponent<RectTransform>().rect.size;
        textGen.Populate(calculateStr, targetText.GetGenerationSettings(extents));
        /*
        int newLine = calculateStr.Substring(0, targetCharIndex).Split('\n').Length - 1;
        int whiteSpace = calculateStr.Substring(0, targetCharIndex).Split(' ').Length - 1;
        */
        int indexOfTextQuad = targetCharIndex * 4;
        /*
        Vector3 charPos1 = textGen.verts[indexOfTextQuad].position / targetCanvas.scaleFactor;
        Vector3 charPos2 = textGen.verts[indexOfTextQuad + 1].position / targetCanvas.scaleFactor;
        Vector3 charPos3 = textGen.verts[indexOfTextQuad + 2].position / targetCanvas.scaleFactor;
        Vector3 charPos4 = textGen.verts[indexOfTextQuad + 3].position / targetCanvas.scaleFactor;
        */

        if (indexOfTextQuad < textGen.vertexCount)
        {
            avgPos = (textGen.verts[indexOfTextQuad].position +
                textGen.verts[indexOfTextQuad + 1].position +
                textGen.verts[indexOfTextQuad + 2].position +
                textGen.verts[indexOfTextQuad + 3].position) / 4f;
        }
        // 分辨率适配
        avgPos /= targetCanvas.scaleFactor;

        // 转为世界坐标
        // avgPos = targetText.transform.TransformPoint(avgPos);

        return avgPos;
    }
}

用于测试功能的代码:FanTest.cs

using UnityEngine;
using UnityEngine.UI;

public class FanTest : MonoBehaviour
{
    [SerializeField]
    private FanEmojiText _text;
    [SerializeField]
    [TextArea(3, 10)]
    private string _content;
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            _text.SetText(_content);
        }
    }
}

额外奖励:为文字内容添加背景图,背景图的大小随文字内容变化而变化

如图,需将Text设置为背景图的子节点。
背景图需添加2个组件:ContentSize Fitter、Horizontal Layout Group
为了让文字内容变多时,整体的下方位置保持不变 -> 背景图的RectTransform的Pivot.Y设置为0。


文字添加背景图.png

相关文章

  • 【狂云歌之unity_vr】unity里获取text中文字宽度并

    【狂云歌之unity_vr】unity里获取text中文字宽度并截断省略 前言  在unity的ugui中Text...

  • 2020-06-17(UGUI Text)

    Unity UGUI Text属性的使用 聊天功能中对于Text的特殊使用: 1、text 加组件content ...

  • react-native踩过的坑

    React-native 1.Text文本中插入图片:View用弹性布局 ...

  • Vue学习笔记(2)-模板语法与v-html

    文本:{{ text }} 在html中通过{{}}(双大括号)中可以把Vue对象中的数据插入到网页中。 只要Vu...

  • Odin Inspector 系列教程 --- Text Are

    Text Area Attribute:Unity自带属性,用于在inspector面板中给字符绘制一个填写区域 ...

  • Unity中,Text颜色渐变

    用法 将颜色渐变组件挂在Text所属GameObject上。(若有描边、阴影等组件,为了使这些组件的功能仍起效,需...

  • js基本语法

    插入 直接在html文件中添加 代码 其中 表示在 之间的是文本类型(text),javascript是为了告诉...

  • Asset文件格式描述

    Unity中asset的序列化有两种方式:Binary,Text。一般项目里会使用Text的方式,主要原因是可以使...

  • iOS富文本图片居中显示

    iOS 富文本中插入表情图片非常容易,可是显示的时候发现位置总是不在设想的位置,虽然经过微调也可展示到居中的位置,...

  • 如何插入图片

    富文本(Rich-text)编辑器下插入图片: 点击功能栏上的插入图片的图标,在弹出的对话框中可选择本地上传图片或...

网友评论

      本文标题:Unity中,在Text中插入表情图

      本文链接:https://www.haomeiwen.com/subject/agptzdtx.html