图文混排

从字面意思来理解:就是图片和文字混合在一起。不知道这样的的定义是否正确,起码我是这样理解的,在游戏开发过程中,如果单单从业务逻辑去看的话,图文混排算是比较复杂的。个人感觉也是必须会的技能。

原理

我们期待在文本的合适地方插入我们需要显示的表情。所以我们要取插入表情的位置,但是一条聊天的信息是一段文本的字符串,是一个整体,每一个字符都不是单个对象,所以要在文本字符串中取合适的位置就不能用平时的方法(ps:UILabe的本质是一张面片),好在NGUI底层给我们提供了顶点信息的集合。

 /// <summary>/// Widget's vertices (before they get transformed)./// </summary>public BetterList<Vector3> verts = new BetterList<Vector3>();

现在我们已经找到突破口,verts保存的是位置信息,那么只有我们算出表情在第几个字后面就可以取得位置信息了。具体步骤如下:

  • 1 定义表情的数据格式,
  • 2 在写入字符串时候,加上特效的标记,然后用正则表达去匹配和替换字符。
  • 3 遍历整个文本,把之前标记的位置记录下来。并且使用空格去替换标记的内容。
  • 4 创建表情对象,把第三步取到的位置信息赋值给刚刚创建的表情对象

核心代码

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;public class UIEmojiWrapper
{private bool hasAtlas = false;public UIAtlas mAtlas;private GameObject emojiPrefab;private Dictionary<string, string> emojiName = new Dictionary<string, string>();private List<Expression> listCfgData = new List<Expression>();private Queue<GameObject> freeSprite = new Queue<GameObject>();private List<GameObject> usedSprite = new List<GameObject>();private Vector3 OutOffScreen = new Vector3(10000f, 10000f, 10000f);private static UIEmojiWrapper sInstance;public static UIEmojiWrapper Instance{get{if (sInstance == null){sInstance = new UIEmojiWrapper();}return sInstance;}}public void Init(UIAtlas atlas){if (!hasAtlas){if (atlas == null){Debug.LogError("[UIEmojiWrapper Atlas is null]");return;}mAtlas = atlas;//预分配AddEmojiName();emojiPrefab = new GameObject();emojiPrefab.layer = 8;hasAtlas = true;}}public GameObject EmojiPrefab{get{if (emojiPrefab == null){emojiPrefab = new GameObject();emojiPrefab.layer = 8;}return emojiPrefab;}}//表情字典public void AddEmojiName(){List<Expression> listCfgEmo = SetlistExpression();if (listCfgEmo == null){return;}listCfgData.Clear();listCfgData = listCfgEmo;for (int i = 0; i < listCfgEmo.Count; i++){int m_key = (int)listCfgEmo[i].key;string name = listCfgEmo[i].value;string sprKey = GetConvertedInt32(m_key.ToString());emojiName.Add(sprKey, name);}}public string GetConvertedString(string inputString){string[] converted = inputString.Split('-');for (int j = 0; j < converted.Length; j++){int value = Convert.ToInt32(converted[j], 16);converted[j] = char.ConvertFromUtf32(value);}return string.Join(string.Empty, converted);}public string GetConvertedInt32(string inputString){int value = int.Parse(inputString);string[] converted = new string[1];converted[0] = char.ConvertFromUtf32(value);return string.Join(string.Empty, converted);}//从对象池取对象public string GetEmoji(string encode){string em;if (emojiName.TryGetValue(encode, out em)){return em;}return null;}public bool HasEmoji(string key){// 120000 120001 120002return emojiName.ContainsKey(key);}public List<Expression> GetListCfgData(){return listCfgData;}// 取表情数据Keypublic int GetEmojiKey(string _value){if (listCfgData.Count > 0){for (int i = 0; i < listCfgData.Count; i++){if (_value == listCfgData[i].name){return (int)listCfgData[i].key;}}}return -1;}public void OnPostFill(UIWidget widget, int bufferOffset, BetterList<Vector3> verts, BetterList<Vector2> uvs, BetterList<Color32> cols){if (widget != null){if (!widget.isVisible){UISprite spt = widget as UISprite;if (spt != null){PushEmoji(spt.gameObject);}}}}public GameObject PopEmoji(){if (freeSprite.Count <= 0){if (emojiPrefab == null)return null;GameObject tran = GameObject.Instantiate(emojiPrefab) as GameObject;freeSprite.Enqueue(tran);}GameObject sptRet = freeSprite.Dequeue();if (sptRet != null){usedSprite.Add(sptRet);}return sptRet;}public void PushEmoji(GameObject spt){//spt.transform.localPosition = OutOffScreen;// spt.transform.parent = null;freeSprite.Enqueue(spt);}public void PushEmoji(ref List<GameObject> list){for (int i = 0, cnt = list.Count; i < cnt; i++){PushEmoji(list[i]);}list.Clear();}/// <summary>/// 字符串替换方法/// </summary>/// <param name="myStr">需要替换的字符串</param>/// <param name="displaceA">需要替换的字符</param>/// <param name="displaceB">将替换为</param>/// <returns></returns>public string Displace(string myStr, string displaceA, string displaceB){string[] strArrayA = Regex.Split(myStr, displaceA);for (int i = 0; i < strArrayA.Length - 1; i++){strArrayA[i] += displaceB;}string returnStr = "";foreach (string var in strArrayA){returnStr += var;}return returnStr;}//建议用json或者其他的数据格式public static List<Expression> SetlistExpression(){List<Expression> data = new List<Expression>();data.Add(new Expression(120000, "ziya", 1, "龇牙"));data.Add(new Expression(120001, "zhouma", 1, "咒骂"));data.Add(new Expression(120002, "youxian", 1, "悠闲"));data.Add(new Expression(120003, "weixiao", 1, "微笑"));data.Add(new Expression(120004, "qiaoda", 1, "敲打"));data.Add(new Expression(120005, "nanguo", 1, "难过"));data.Add(new Expression(120006, "daxiao", 1, "大笑"));data.Add(new Expression(120008, "deng", 1, "瞪"));data.Add(new Expression(120009, "dese", 1, "得瑟"));data.Add(new Expression(120010, "dese", 1, "得意"));data.Add(new Expression(120011, "lianhong", 1, "脸红"));data.Add(new Expression(120012, "keai", 1, "可爱"));data.Add(new Expression(120013, "han", 1, "汗"));data.Add(new Expression(120014, "ganga", 1, "尴尬"));data.Add(new Expression(120015, "se", 1, "色"));data.Add(new Expression(120016, "kun", 1, "困"));data.Add(new Expression(120017, "ku", 1, "酷"));data.Add(new Expression(120018, "baiyan", 1, "白眼"));data.Add(new Expression(120019, "baoya", 1, "龅牙"));data.Add(new Expression(120020, "beida", 1, "被打"));return data;}}public class Expression
{public uint key;                // 16进制编号public string value;                // 前缀public uint sin;                // 是否序列帧public string name;              // 名称public Expression(uint key, string value, uint sin, string name){this.key = key;this.value = value;this.sin = sin;this.name = name;}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;public class UIEmojiLabel : UILabel
{private char emSpace = '\u2001';private List<GameObject> mEmojiList = new List<GameObject>();public UIAtlas emAtlas = null;protected override void Awake(){base.Awake();UIEmojiWrapper.Instance.Init(emAtlas);}protected override void OnStart(){base.OnStart();//表情统一管理器,只初始化一次}//自动转码public override void OnFill(BetterList<Vector3> verts, BetterList<Vector2> uvs, BetterList<Color32> cols){base.OnFill(verts, uvs, cols);// StartCoroutine(SetUITextThatHasEmoji(text));}public void ShowEmojiText(string strText){//strText = "图形测试 (ziya) 呵呵 (youxian)"//找到表情特殊标记()Regex reg = new Regex(@"(?is)(?<=\()[^\)]+(?=\))");MatchCollection mc = reg.Matches(strText);if (mc != null){for (int i = 0; i < mc.Count; i++){string oldStr = string.Format("({0})", mc[i].Value);   //旧字符int key = UIEmojiWrapper.Instance.GetEmojiKey(mc[i].Value);if (key > 0){string newStr = UIEmojiWrapper.Instance.GetConvertedInt32(key.ToString()); //薪字符strText = strText.Replace(oldStr, newStr);}}}text = strText;StartCoroutine(SetUITextThatHasEmoji(strText));}private struct PosStringTuple{public int pos;public string emoji;public PosStringTuple(int p, string s){this.pos = p;this.emoji = s;}}private void DestroyEmojiSpr(){if (this.mEmojiList.Count > 0){for (int i = 0; i < this.mEmojiList.Count; i++){Destroy(this.mEmojiList[i]);}this.mEmojiList.Clear();}}public IEnumerator SetUITextThatHasEmoji(string inputString){inputString = inputString.Replace("  ", "&&");inputString = inputString.Replace(" ", "&");List<PosStringTuple> emojiReplacements = new List<PosStringTuple>();StringBuilder sb = new StringBuilder();//先回收DestroyEmojiSpr();int i = 0;while (i < inputString.Length){string singleChar = inputString.Substring(i, 1);string doubleChar = "";string fourChar = "";if (i < (inputString.Length - 1)){doubleChar = inputString.Substring(i, 2);}if (i < (inputString.Length - 3)){fourChar = inputString.Substring(i, 4);}if (UIEmojiWrapper.Instance.HasEmoji(fourChar)){// Check 64 bit emojis firstsb.Append(emSpace);int blankSpaceConut = GetBlankSpaceConut(sb);emojiReplacements.Add(new PosStringTuple(sb.Length - 1, fourChar));i += 4;}else if (UIEmojiWrapper.Instance.HasEmoji(doubleChar)){sb.Append(emSpace);emojiReplacements.Add(new PosStringTuple(sb.Length - 1, doubleChar));i += 2;}else if (UIEmojiWrapper.Instance.HasEmoji(singleChar)){sb.Append(emSpace);emojiReplacements.Add(new PosStringTuple(sb.Length - 1, singleChar));i++;}else{sb.Append(inputString[i]);i++;}}this.text = sb.ToString();yield return null;for (int j = 0; j < emojiReplacements.Count; j++){int emojiIndex = emojiReplacements[j].pos;//表情替换,计算位置,大小GameObject go = GameObject.Instantiate(UIEmojiWrapper.Instance.EmojiPrefab);mEmojiList.Add(go);if (go != null){UISprite spt = go.AddComponent<UISprite>();string emoji = UIEmojiWrapper.Instance.GetEmoji(emojiReplacements[j].emoji);if (!string.IsNullOrEmpty(emoji)){spt.atlas = UIEmojiWrapper.Instance.mAtlas;spt.name = emoji;spt.spriteName = emoji + "_1";spt.width = this.ChatPrintedSize;spt.height = this.ChatPrintedSize;spt.depth = this.depth + 10;spt.transform.parent = this.transform;spt.transform.localScale = Vector3.one;Vector3 pos = new Vector3();try{pos = new Vector3(this.geometry.verts[emojiIndex * 4].x + spt.width / 2, (this.geometry.verts[emojiIndex * 4].y + spt.height / 2) - 8);spt.transform.localPosition = pos;UISpriteAnimation sprAni = go.AddComponent<UISpriteAnimation>();sprAni.namePrefix = emoji + "_";sprAni.framesPerSecond = 3;sprAni.Snap = false;sprAni.Play();isVertsBuff = false;}catch{Debug.LogError("geometry.verts == null");isVertsBuff = true;ShowEmojiText(oldText);}}}}if (!isVertsBuff){this.text = text.Replace("&&", "  ");this.text = text.Replace("&", " ");}}
}

#if !UNITY_3_5 && !UNITY_FLASH
#define DYNAMIC_FONT
#endifusing UnityEngine;
using UnityEditor;/// <summary>
/// Inspector class used to edit UILabels.
/// </summary>[CanEditMultipleObjects]
#if UNITY_3_5
[CustomEditor(typeof(UILabel))]
#else
[CustomEditor(typeof(UILabel), true)]
#endif
public class UIEmojiLabelInspector : UILabelInspector
{/// <summary>/// Draw the label's properties./// </summary>protected override bool ShouldDrawProperties(){bool isValid = base.ShouldDrawProperties();EditorGUI.BeginDisabledGroup(!isValid);NGUIEditorTools.DrawProperty("Atlas", serializedObject, "emAtlas");//表情所在的图集AtlasEditorGUI.EndDisabledGroup();return isValid;}
}

主要事项

1 上面的代码改了一下部分NGUI的接口,直接拷贝会报错,你可以自己手动加,都是很简单的接口。我也会上传一份dome
2 在Lua层调C#的时候,有机会出现第一次会报verts为空,我还没有找到原因,欢迎大神指点,我现在的解决方法是延迟执行。
如果大佬们有好的方法,也求推荐。
3 看看 最终效果是带动态的哦!!

表情聊天— 图文混排下载

Unity表情聊天(NGUI图文混排)相关推荐

  1. Swift3.0 功能二 (表情键盘与图文混排)

    随着iOS越来越多表情键盘以及图文混排的需求,本文运用Swift3.0系统的实现其功能以及封装调用方法,写的不好,如有错误望各位提出宝贵意见,多谢 项目源码地址: 相关知识点都有标识 项目源码地址 废 ...

  2. 直播间聊天item图文混排思路

    目录 1.版本需求 a.初版本 b.表情包版本 2.思路剖析 a.拼接格式 b.生成格式的操作 c.遍历所有文字,替换图片 d.网络图片的替换逻辑 e.本地图片的替换 f.本地图片快速存储到map g ...

  3. Unity TMP超链接和图文混排

    游戏内聊天内容离不开的就是超链接 比如点击物品名字 显示该物品的详细信息等等 public class Link : MonoBehaviour {public TextMeshProUGUI pro ...

  4. Unity 来搞一个图文混排吧~

    好久没有来更新了,甚至我的账号都掉了,试了半天才搞对密码. 最近也真的是很忙,甚至上周日差点加班,幸好-- 废话不多说,开始了!!! 废话 首先说明,这个大标题都是废话,可以跳过. 图文混排,这是一个 ...

  5. android直播聊天室图文混排效果,仿抖音直播聊天室换行内容TextView+ReplacementSpan...

    一.抖音聊天室文本,看似简单并非简单 抖音文本换行. 二.实现方案TextView + ReplacementSpan 方案思路 利用span原理,继承ReplacementSpan,自定义VIewS ...

  6. Unity图文混排的几种方式

    方法一:TextMesh - TextMesh是Unity原生的支持图文混排的方式. - 使用方法 在GameObject下挂上TextMesh,会自动追加上MeshRender,之后在Materia ...

  7. 为了用Unity来实现APP效果的页面,自制一个简易的图文混排系统。

    图文混排设计方案 实现一个Unity支持的简单图文混排模块,实现时需要考虑尺寸适配的限制,设计思路是自定义标签,采用微信朋友圈里的那种流式布局,一行一个标签,做一个一维的列表,方便手动编辑也方便程序实 ...

  8. 浅析微博编辑页面图文混排中遇到的问题

    在表情键盘的图文混排中,会有很多细节问题.有的时候不理解其中的原因是很难改正的.本文主要是整理我遇到的各种问题及解决方案,供大家分享.如果你以前也遇到过类似的问题可以用我的方法修正了,希望能够对博友们 ...

  9. 【游戏开发实战】Unity UGUI Text图文混排(聊天文字混表情),支持动态表情,出招吧表情帝

    文章目录 一.前言 二.最终效果 三.具体使用 1.导入表情素材 2.设置图片格式 3.生成表情图集 4.UI-EmojiFont.shader 5.材质球 四.测试 五.结束语 一.前言 点关注不迷 ...

  10. Unity Emoji表情(图文混排)2022最详细教程

    文章目录 前言 一.emoji是什么? emoji (日本在无线通信中所使用的视觉情感符号) 二.使用步骤 1.将源码文件导入自己的项目中 步骤一:将Editor目录文件EmojiAtlasBaker ...

最新文章

  1. Toast的功能和用法
  2. 三面美团Java岗,java多线程匿名内部类
  3. Git 常用命令整理(持续更新)
  4. 2019最新C语言知识整理小白进来看看??
  5. 大物实验报告-转动惯量的测定金属杨氏模量的测定
  6. 最简单的FRP内网穿透教程
  7. unity编辑器扩展——替换选择物体名字中的内容
  8. 杰奇小说系统百度地图生成插件
  9. 在 Selenium 中使用 Lambdatest 进行跨浏览器测试
  10. 车载诊断数据库ODX——ODX参数解析类型(上)
  11. scrapy爬取网站在线播放TS视频流片段并整合为MP4格式
  12. linux+agent卸载_Symantec Backup Exec 2012 Agent for Linux 卸载 - 潇湘隐者
  13. 定义c语言字符串的三种方法
  14. 安全合规/GDPR--21--我们是如何开展PTA、PIA、DPIA风险评估的
  15. 第三章:MySQL(中篇)
  16. 第22章 服务器基础知识
  17. 【OpenFOAM】snappyHexMesh
  18. 鹰式价差matlab,什么是鹰式价差策略?-投资江湖
  19. 今天看了一眼我的书架,发现清华出版社出的书真是多啊
  20. 软件工程环形复杂度计算方法有哪些_面向C程序的环形复杂度自动化计算方法...

热门文章

  1. 计算机二级证一定要优秀吗,计算机二级需要优秀吗 如何拿拿到优秀成绩
  2. Problem A: 小学生的算术题
  3. 实战 | 用 Python 选股票,据说可以多挣个20%
  4. 三相逆变器双pi控制器参数如何调节_光伏逆变器MPPT基本原理李星硕
  5. 计算机音乐三只小熊,三只小熊教案音乐
  6. 闲谈IPv6-我们在技术思维上需要作出改变(1)
  7. execl 多线程 linux,MyExcel 3.7.0 发布,屏蔽多线程处理细节
  8. week 5 session and cookie
  9. 特征选择 | MATLAB实现NCA(近邻成分分析)特征选择
  10. 基于can总线的A2L文件解析(2)