超简单的基于UGUI的图文混排

2 篇文章 0 订阅

转载请注明出处。

  • 需求

原来游戏使用的UI框架是NGUI的,那时候不需要考虑图文混排的,NGUI本身就支持图文混排,只需要我们为需要进行图文混排的图片都生成一个图集,然后设置好每个图片的占位符,这样就可以直接栽Label组件直接用这个占位符生成相应的图片在这里插入图片描述。但是UGUI原生的Text组件并不支持图文混排,所以没有办法只能是我们自己去想办法。

  • 调研

由于UGUI的Text组件不支持图文混排,所以我去查了下Unity还给我们提供了一个组件叫做TextMesh,这个组件是可以支持图文混排的,但是他是3D的组件,我们在2D的UI里面要使用这个组件会造成的主要问题是无法合批,也无法收UI排序的影响,最主要的是ScrollRect组件无法mask他,这样会导致超出ScrollRect范围的TextMesh依然会显示出来,所以这个组件就直接Pass了。另外一种做法是使用TextMeshPro第三方插件,这个插件也是官方推荐的,而且性能也不错,使用的是距离场来实现的,里面还有很多UI效果,所以是很不错的插件,但是我并不想在项目中引入过多的第三方的东西,因为TextMeshPro我们也就会用到他扩展的Text组件其他的效果基本上用不到,所以为了以后维护方便,我也放弃这种方式,最后选择自己去实现。

  • 思路

我们都知道Text组件之所以会生成文字/图片都是因为他会生成网格,然后通过uv去Font里面根据相应的文字的坐标等参数,在一张字体贴图上去采样这个文字,然后显示出来,而显示图片原理也是一样,只是去采样的图集不一样而已,其次就是如何区分是要显示文本还是显示图片,这时候就需要一个占位符来告诉我们这个地方要生成图片,例如这样[#ID=***]这样的格式,最后一点就是类似gif这种动图要如何显示的问题,其实我们可以把gif理解为帧动画,每帧改变采样的uv,让uv有规律的改变,这样就能做到像gif那样动起来了,我们只需要告诉这个动画需要显示多少帧就行了,然后为每个表情生成一个配置文件,类似于下面的格式
ID UV_X UV_Y Frame
[#ID=1] 0,0 0,0.25 2
而且是直接继承自Text组件,所以不需要担心排序,等相关,我们只需要负责告诉Text这个地方需要一个Emoji,然后赋值上UV和Vertex就行了。

  • 实现

这里是根据单个图片的名字生成json文件的配置,并生成相应的图集,把所有的图片合成一张大图

private static Dictionary<string, EmojiInfo> GenerateConfig(Dictionary<string, List<Texture2D>> rSpriteDic)
    {
        Dictionary<string, EmojiInfo> rEmojiInfoDic = new Dictionary<string, EmojiInfo>();
        foreach (var sprite in rSpriteDic)
        {
            EmojiInfo rInfo = new EmojiInfo();
            rInfo.mKey = sprite.Key;
            rInfo.mFrame = sprite.Value.Count;
            rEmojiInfoDic.Add(sprite.Key, rInfo);
        }
        return rEmojiInfoDic;
    }
    private static Dictionary<string, List<Texture2D>> GetALLSprites()
    {
        Dictionary<string, List<Texture2D>> rSpritesDic = new Dictionary<string, List<Texture2D>>();
        string[]rGUID= AssetDatabase.FindAssets("t:texture", new string[] { "Assets/Textures/EmojiSprites" });
        for (int i = 0; i < rGUID.Length; i++)
        {
            string rPath= AssetDatabase.GUIDToAssetPath(rGUID[i]);
            var rTex= AssetDatabase.LoadAssetAtPath<Texture2D>(rPath);
            string[]rNameSplit= rTex.name.Split('_');
            string rKey= string.Format("[image={0}]",rNameSplit[0]);
            if (rSpritesDic.ContainsKey(rKey))
                rSpritesDic[rKey].Add(rTex);
            else
            {
                List<Texture2D> rSprites = new List<Texture2D>();
                rSprites.Add(rTex);
                rSpritesDic.Add(rKey, rSprites);
            }
        }
        return rSpritesDic;
    }

然后就是重写Text的OnPopulateMesh方法

//利用正则表达式找到符合约定的占位符
			MatchCollection rMatches=Regex.Matches(text,"\\[image=[a-z0-9A-Z]+\\]");
			StringBuilder rTempString=new StringBuilder();
			for (int i = 0; i < rMatches.Count; i++)
			{
				EmojiInfo rInfo;
				if (mEmojiInfos.TryGetValue(rMatches[i].Value,out rInfo))
				{
					//因为会把“[]”去掉,[]是不需要生成顶点的
					rFindEmojis.Add(rMatches[i].Index-i*2,rInfo);
					//从上一个匹配的位置截取到下一个匹配的位置
					rTempString.Append(text.Substring(rLastIndex,rMatches[i].Index-rLastIndex));
					//然后把[Image=**]替换成一个中文字符
					rTempString.Append(mReplaceString);
					//记录下索引的位置
					rLastIndex=rMatches[i].Index+rMatches[i].Length;
				}
			}
			if (rLastIndex<text.Length)
				rTempString.Append(text.Substring(rLastIndex,text.Length));
			//这里是直接复制的UGUI的Text生成定点的代码
			Vector2 extent=rectTransform.rect.size;
			var settings= GetGenerationSettings(extent);
			cachedTextGenerator.Populate(rTempString.ToString(), settings);

然后是利用生成的顶点来设置采样的UV


						float charDis = (verts[i + 1].position.x - verts[i].position.x);
						//之所以是先把3=i是为了让图片正着显示
        				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
        				m_TempVerts[2].position += new Vector3(charDis, 0, 0);
        				m_TempVerts[1].position += new Vector3(charDis, 0, 0);

        				//让emoji长宽相等
        				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);

        				m_TempVerts[0].position *= unitsPerPixel;
        				m_TempVerts[1].position *= unitsPerPixel;
        				m_TempVerts[2].position *= unitsPerPixel;
        				m_TempVerts[3].position *= unitsPerPixel;

        				//计算Emoji的UV,利用uv0传递帧数,uv1是emoji的纹理坐标
        				m_TempVerts[0].uv1 = new Vector2(float.Parse(rInfo.mUV_X), float.Parse(rInfo.mUV_Y));
        				m_TempVerts[1].uv1 = new Vector2(float.Parse(rInfo.mUV_X + (32f/1024f)), float.Parse(rInfo.mUV_Y));
        				m_TempVerts[2].uv1 = new Vector2(float.Parse(rInfo.mUV_X + (32f/1024f)), float.Parse(rInfo.mUV_Y+ (32f/1024f)));
        				m_TempVerts[3].uv1 = new Vector2(float.Parse((rInfo.mUV_X)), float.Parse(rInfo.mUV_Y + (32f/1024f)));
        				m_TempVerts[0].uv0 = new Vector2(rInfo.mFrame, 0);
        				m_TempVerts[1].uv0 = new Vector2(rInfo.mFrame, 0);
        				m_TempVerts[2].uv0 = new Vector2(rInfo.mFrame, 0);
        				m_TempVerts[3].uv0 = new Vector2(rInfo.mFrame, 0);

        				rHelper.AddUIVertexQuad(m_TempVerts);
						i+=3;

在这里插入图片描述

实际效果就如上图所示。其中我这边是固定死的每张表情是32X32大小的,所以如果想要改成更大或者更小的可以自己在代码里设置。完整的项目demo放在https://github.com/LongTimeEnjoy/ExtensionUI
觉得还不错的朋友star一下哦

ref
https://blog.uwa4d.com/archives/Sparkle_UGUI.html

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值