0评论

UGUI实现简单图文混排

文章来自https://blog.csdn.net/tc3819171/article/details/84504014 2019-02-22 257浏览

想免费获取内部独家PPT资料库?观看行业大牛直播?点击加入腾讯游戏学院游戏开发行业精英群711501594

NGUI支持图文混排,只需要我们为需要进行图文混排的图片都生成一个图集,然后设置好每个图片的占位符,这样就可以直接栽Label组件直接用这个占位符生成相应的图片,但是UGUI原生的Text组件并不支持图文混排,所以下面要分享的是使用UGUI来实现图文混排功能,想知道的就来看看吧。

思路

我们都知道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。