unity3d在UGUI中显示带表情的微信昵称
本人是搬砖党,都是网上到处搜集的知识点,这里做个总结或者说笔记
1、需求:有的微信昵称是含有表情的。当你的应用对接微信SDK后,获取到的昵称放到unity中,会显示异常。我们要做的就是让包含表情的微信昵称正常显示。
如果你的需求与上面的偏差比较大,那么建议不用继续往下看了。看完不一定能有所收获。
2、普及知识:
1) 微信昵称中的表情是emoji表情,先了解它是什么?反正不是图片。eg:1(????????)2(????????)3(????)
2)需要做的映射关系是:????与某图片,不是0与某图片,除非你能说服服务端小伙伴帮你把emoji表情转换成“0”或者某种格式。所以这也是没有选择TextMeshPro的原因之一
3)不要认为一个emoji表情占用一个字符串长度。可以试试 string str = ????;Debug.Log(str);有的是2,有的是4,有的是...
3、最终效果
首先用到网友的EmojiText组件,如图
only one component,简便,一目了然,仅显示一行昵称足以
这是我修改后的最终效果,工程中的是[0]对应图片 我的是????对应图片
网友的工程git地址: https://github.com/zouchunyi/EmojiText
4、如何找出字符串中的emoji表情
网上找到一篇文章,讲解了emoj与Unicode之间的联系,代码是用java写的
https://www.cnblogs.com/hahahjx/p/4522913.html
我们可以用到其中的isEmojiCharacter函数。C#没有codePointAt函数,但是有其他办法。
System.Globalization.StringInfo si = new System.Globalization.StringInfo(str);
for (int i = 0; i < si.LengthInTextElements; i++)
{
string ele = si.SubstringByTextElements(i, 1);
}
得到ele的unicode就可以判断ele是不是表情了。有的表情占用多个长度,需要把整个表情长度找出来
5、找出了表情就好办了,把表情翻译成自己想要的名称格式就可以了。我这里把表情????翻译成"1f601"。因为我下载的1000多个emoji表情图片都是这种格式????。到此就实现了 ???? ==>1f601==>1f601.png
6、算了,不说了,不知道怎么讲清楚。直接贴代码
/// <summary>
/// codePoint是否是表情字符
/// 参考java的codePointAt
/// </summary>
private static bool isEmojiCharacter(int codePoint)
{
return (codePoint >= 0x2600 && codePoint <= 0x27BF) // 杂项符号与符号字体
|| codePoint == 0x303D
|| codePoint == 0x2049
|| codePoint == 0x203C
|| (codePoint >= 0x2000 && codePoint <= 0x200F)//
|| (codePoint >= 0x2028 && codePoint <= 0x202F)//
|| codePoint == 0x205F //
|| (codePoint >= 0x2065 && codePoint <= 0x206F)//
/* 标点符号占用区域 */
|| (codePoint >= 0x2100 && codePoint <= 0x214F)// 字母符号
|| (codePoint >= 0x2300 && codePoint <= 0x23FF)// 各种技术符号
|| (codePoint >= 0x2B00 && codePoint <= 0x2BFF)// 箭头A
|| (codePoint >= 0x2900 && codePoint <= 0x297F)// 箭头B
|| (codePoint >= 0x3200 && codePoint <= 0x32FF)// 中文符号
|| (codePoint >= 0xD800 && codePoint <= 0xDFFF)// 高低位替代符保留区域
|| (codePoint >= 0xE000 && codePoint <= 0xF8FF)// 私有保留区域
|| (codePoint >= 0xFE00 && codePoint <= 0xFE0F)// 变异选择器
|| codePoint >= 0x10000; // Plane在第二平面以上的,char都不可以存,全部都转
}
/// <summary>
/// 将中文或者表情放到list中
/// 参考java的codePoint
/// 两个表情挨着的情况暂时没处理
/// out的resultText,将表情符号替换为█,用来计算Text的cachedTextGenerator
/// </summary>
public static List<EmojiElement> getEmojiList(string str,out string resultText)
{
resultText = str;
List<EmojiElement> arr = new List<EmojiElement>();
StringInfo si = new StringInfo(str);
StringBuilder builder = new StringBuilder();
for (int i = 0; i < si.LengthInTextElements; i++)
{
string ele = si.SubstringByTextElements(i, 1);
string unicode = "0x" + toUnicode(ele);
//Debug.Log("i : " + i);
try
{
int e = Convert.ToInt32(unicode, 16);
bool isEmoji = isEmojiCharacter(e);
if (isEmoji)
{
builder.Append(ele);
}
else
{
if (!string.IsNullOrEmpty(builder.ToString()))
{
EmojiElement ee1 = new EmojiElement();
ee1.isEmoji = true;
ee1.unicode = toUnicode(builder.ToString());
ee1.originalText = builder.ToString();
resultText = resultText.Replace(ee1.originalText, "█");
arr.Add(ee1);
builder.Remove(0, builder.Length);
}
EmojiElement ee = new EmojiElement();
ee.isEmoji = false;
arr.Add(ee);
}
if(i == si.LengthInTextElements - 1)
{
if (!string.IsNullOrEmpty(builder.ToString()))
{
EmojiElement ee1 = new EmojiElement();
ee1.isEmoji = true;
ee1.unicode = toUnicode(builder.ToString());
ee1.originalText = builder.ToString();
resultText = resultText.Replace(ee1.originalText, "█");
arr.Add(ee1);
builder.Remove(0, builder.Length);
}
}
}
catch(Exception ex)
{
Debug.Log(ex.ToString());
}
}
return arr;
}
/// <summary>
/// 将表情字符串转成unicode(result eg1:1f60a(微笑) eg2:1f1e8_1f1f3(国旗表情))
/// 结果与表情图片相对应
/// </summary>
public static string toUnicode(string s)
{
var bytes = Encoding.UTF32.GetBytes(s);
var stringBuilder = new StringBuilder();
List<List<byte>> resultList = new List<List<byte>>();
List<byte> tempList = new List<byte>();
for (int i = 0, length = bytes.Length; i < length; i++)
{
tempList.Add(bytes[i]);
if (tempList.Count == 4)
{
tempList.Reverse();
resultList.Add(tempList);
tempList = new List<byte>();
}
}
for (int i = 0, length = resultList.Count; i < length; i++)
{
List<byte> elist = resultList[i];
bool isFirst = false;
for (int j = 0; j < elist.Count; j++)
{
string b = elist[j].ToString("x2");
if (b == "00" && j < 2)
continue;
if (!isFirst)
{
isFirst = true;
if (j < 2)
b = elist[j].ToString("x");
stringBuilder.Append(b);
}
else
{
stringBuilder.Append(b);
}
//Debug.Log(b);
}
if (isFirst && i != length - 1)
{
stringBuilder.Append("_");
}
}
return stringBuilder.ToString().Replace("_0000", "");
}
toUnicode函数我自己写的,肯定有更简便的。。
EmojiText也直接贴出来吧
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Text;
using System;
using System.Globalization;
public class EmojiText : Text {
private const bool EMOJI_LARGE = true;
private static Dictionary<string,EmojiInfo> EmojiIndex = null;
struct EmojiInfo
{
public float x;
public float y;
public float size;
public int len;
}
readonly UIVertex[] m_TempVerts = new UIVertex[4];
protected override void OnPopulateMesh(VertexHelper toFill)
{
if (font == null)
return;
if (EmojiIndex == null) {
EmojiIndex = new Dictionary<string, EmojiInfo>();
//load emoji data, and you can overwrite this segment code base on your project.
TextAsset emojiContent = Resources.Load<TextAsset> ("emoji");
string[] lines = emojiContent.text.Split ('\n');
for(int i = 1 ; i < lines.Length; i ++)
{
if (! string.IsNullOrEmpty (lines [i])) {
string[] strs = lines [i].Split ('\t');
EmojiInfo info;
info.x = float.Parse (strs [3]);
info.y = float.Parse (strs [4]);
info.size = float.Parse (strs [5]);
info.len = 0;
EmojiIndex[strs[0]] = info;
}
}
}
Dictionary<int,EmojiInfo> emojiDic = new Dictionary<int, EmojiInfo> ();
string resultText = string.Empty;
if (supportRichText)
{
List<EmojiElement> emojiList = EmojiUtil.getEmojiList(text,out resultText);
//Debug.Log("resultText:"+ resultText+",len:"+ resultText.Length);
//Debug.Log("emojiList:"+ emojiList.Count);
for (int i = 0, length= emojiList.Count; i < length; i++)
{
EmojiElement element = emojiList[i];
if (element.isEmoji)
{
EmojiInfo info;
string unicodeValue = element.unicode;
//Debug.Log("unicodeValue:" + unicodeValue + ",i:" + i);
if (EmojiIndex.TryGetValue(unicodeValue, out info))
{
//Debug.Log("1 unicodeValue:" + unicodeValue);
info.len = 1;
emojiDic.Add(i, info);
}
}
}
}
// We don't care if we the font Texture changes while we are doing our Update.
// The end result of cachedTextGenerator will be valid for this instance.
// Otherwise we can get issues like Case 619238.
m_DisableFontTextureRebuiltCallback = true;
Vector2 extents = rectTransform.rect.size;
var settings = GetGenerationSettings(extents);
if (supportRichText && !string.IsNullOrEmpty(resultText))
{
cachedTextGenerator.Populate(resultText, settings);
}
else
{
cachedTextGenerator.Populate(text, settings);
}
Rect inputRect = rectTransform.rect;
// get the text alignment anchor point for the text in local space
Vector2 textAnchorPivot = GetTextAnchorPivot(alignment);
Vector2 refPoint = Vector2.zero;
refPoint.x = Mathf.Lerp(inputRect.xMin, inputRect.xMax, textAnchorPivot.x);
refPoint.y = Mathf.Lerp(inputRect.yMin, inputRect.yMax, textAnchorPivot.y);
// Determine fraction of pixel to offset text mesh.
Vector2 roundingOffset = PixelAdjustPoint(refPoint) - refPoint;
// Apply the offset to the vertices
IList<UIVertex> verts = cachedTextGenerator.verts;
float unitsPerPixel = 1 / pixelsPerUnit;
//Last 4 verts are always a new line...
int vertCount = verts.Count - 4;
//Debug.Log("verts:" + verts.Count);
toFill.Clear();
if (roundingOffset != Vector2.zero)
{
for (int i = 0; i < vertCount; ++i)
{
int tempVertsIndex = i & 3;
m_TempVerts[tempVertsIndex] = verts[i];
m_TempVerts[tempVertsIndex].position *= unitsPerPixel;
m_TempVerts[tempVertsIndex].position.x += roundingOffset.x;
m_TempVerts[tempVertsIndex].position.y += roundingOffset.y;
if (tempVertsIndex == 3)
toFill.AddUIVertexQuad(m_TempVerts);
}
}
else
{
float repairDistance = 0;
float repairDistanceHalf = 0;
float repairY = 0;
if (vertCount > 0) {
repairY = verts [3].position.y;
}
for (int i = 0; i < vertCount; ++i) {
EmojiInfo info;
int index = i / 4;
if (emojiDic.TryGetValue (index, out info)) {
//compute the distance of '[' and get the distance of emoji
float charDis = (verts [i + 1].position.x - verts [i].position.x) * 3;
m_TempVerts [3] = verts [i];//1
m_TempVerts [2] = verts [i + 1];//2
m_TempVerts [1] = verts [i + 2];//3
m_TempVerts [0] = verts [i + 3];//4
//the real distance of an emoji
m_TempVerts [2].position += new Vector3 (charDis, 0, 0);
m_TempVerts [1].position += new Vector3 (charDis, 0, 0);
//make emoji has equal width and height
float fixValue = (m_TempVerts [2].position.x - m_TempVerts [3].position.x - (m_TempVerts [2].position.y - m_TempVerts [1].position.y));
m_TempVerts [2].position -= new Vector3 (fixValue, 0, 0);
m_TempVerts [1].position -= new Vector3 (fixValue, 0, 0);
float curRepairDis = 0;
if (verts [i].position.y < repairY) {// to judge current char in the same line or not
repairDistance = repairDistanceHalf;
repairDistanceHalf = 0;
repairY = verts [i + 3].position.y;
}
curRepairDis = repairDistance;
int dot = 0;//repair next line distance
for (int j = info.len - 1; j > 0; j--) {
if (verts [i + j * 4 + 3].position.y >= verts [i + 3].position.y) {
repairDistance += verts [i + j * 4 + 1].position.x - m_TempVerts [2].position.x;
break;
} else {
dot = i + 4 * j;
}
}
if (dot > 0) {
int nextChar = i + info.len * 4;
if (nextChar < verts.Count) {
repairDistanceHalf = verts [nextChar].position.x - verts [dot].position.x;
}
}
//repair its distance
for (int j = 0; j < 4; j++) {
m_TempVerts [j].position -= new Vector3 (curRepairDis, 0, 0);
}
m_TempVerts [0].position *= unitsPerPixel;
m_TempVerts [1].position *= unitsPerPixel;
m_TempVerts [2].position *= unitsPerPixel;
m_TempVerts [3].position *= unitsPerPixel;
float pixelOffset = emojiDic [index].size / 32 / 2;
m_TempVerts [0].uv1 = new Vector2 (emojiDic [index].x + pixelOffset, emojiDic [index].y + pixelOffset);
m_TempVerts [1].uv1 = new Vector2 (emojiDic [index].x - pixelOffset + emojiDic [index].size, emojiDic [index].y + pixelOffset);
m_TempVerts [2].uv1 = new Vector2 (emojiDic [index].x - pixelOffset + emojiDic [index].size, emojiDic [index].y - pixelOffset + emojiDic [index].size);
m_TempVerts [3].uv1 = new Vector2 (emojiDic [index].x + pixelOffset, emojiDic [index].y - pixelOffset + emojiDic [index].size);
toFill.AddUIVertexQuad (m_TempVerts);
i += 4 * info.len - 1;
} else {
int tempVertsIndex = i & 3;
if (tempVertsIndex == 0 && verts [i].position.y < repairY) {
repairY = verts [i + 3].position.y;
repairDistance = repairDistanceHalf;
repairDistanceHalf = 0;
}
m_TempVerts [tempVertsIndex] = verts [i];
m_TempVerts [tempVertsIndex].position -= new Vector3 (repairDistance, 0, 0);
m_TempVerts [tempVertsIndex].position *= unitsPerPixel;
if (tempVertsIndex == 3)
toFill.AddUIVertexQuad (m_TempVerts);
}
}
}
m_DisableFontTextureRebuiltCallback = false;
}
}
1000多张emoji表情图片,不知道怎么修改下载积分
https://download.****.net/download/cheng219101/10963349
这里也可以下载,不过得一个个点
https://apps.timwhitlock.info/emoji/tables/unicode
遗留问题:两个表情挨在一起,会解析成一个无法识别的符号