`

NGUI所见即所得之UISprite & UILabel

 
阅读更多

       NGUI所见即所得之UISprite & UILabel

        UISprite UILabel是NGUI最基础的组件,是UIWidget的子类,之前写过NGUI所见即所得之UIWidget , UIGeometry & UIDrawCall UIWidget,UIGeometry & UIDrawCall是NGUI的UI组件绘制的底层实现,UISprite,UILabel就把要绘制的材料——顶点,纹理,纹理UV,颜色值等传给底层,底层负责协调绘制渲染。

 

UISprite

        NGUI3.0.3d版本已经将UISprite,UIFillSprite,UISliceSprite,UITiledSprite整合在一个UISprite中,用Type来区分:

public enum Type
	{
		Simple,
		Sliced,
		Tiled,
		Filled,
	}

        UISprite主要是重写(override)OnFill函数——把Vertices,UVs和Colors添加进UIGeometry中。在分析OnFill之前,先看下drawingDimensions的实现:

        /// <summary>
	/// Sprite's dimensions used for drawing. X = left, Y = bottom, Z = right, W = top.
	/// This function automatically adds 1 pixel on the edge if the sprite's dimensions are not even.
	/// It's used to achieve pixel-perfect sprites even when an odd dimension sprite happens to be centered.
	/// </summary>

	Vector4 drawingDimensions
	{
		get
		{
			if (mSprite == null)
			{
				return new Vector4(0f, 0f, mWidth, mHeight);
			}

			int padLeft = mSprite.paddingLeft;
			int padBottom = mSprite.paddingBottom;
			int padRight = mSprite.paddingRight;
			int padTop = mSprite.paddingTop;

			Vector2 pv = pivotOffset;

			int w = mSprite.width + mSprite.paddingLeft + mSprite.paddingRight;
			int h = mSprite.height + mSprite.paddingBottom + mSprite.paddingTop;

			if ((w & 1) == 1) ++padRight;
			if ((h & 1) == 1) ++padTop;

			float invW = 1f / w;
			float invH = 1f / h;
			Vector4 v = new Vector4(padLeft * invW, padBottom * invH, (w - padRight) * invW, (h - padTop) * invH);

			v.x -= pv.x;
			v.y -= pv.y;
			v.z -= pv.x;
			v.w -= pv.y;

			v.x *= mWidth;
			v.y *= mHeight;
			v.z *= mWidth;
			v.w *= mHeight;

			return v;
		}
	}

        其实drawingDimensions可以看成纹理贴图的x,y轴的区间,也就是说纹理在x轴区间为(v.x, v.z),y轴区间为(v.y,v.w)的矩形。注释中,说道如果drawingDimension不是偶数,则会添加一个像素,这个处理导致UISprite的类型是Tiled,有可能出现间隙,网上也有人通过设置一个像素的Border来解决这个问题,当然最本质就是纹理贴图像素是偶数的。

 

        下面给出OnFill函数,在代码中给出注释:

 

public override void OnFill (BetterList<Vector3> verts, BetterList<Vector2> uvs, BetterList<Color32> cols)
	{
		Texture tex = mainTexture;

		if (tex != null)
		{
			if (mSprite == null) mSprite = atlas.GetSprite(spriteName);
			if (mSprite == null) return;

			mOuterUV.Set(mSprite.x, mSprite.y, mSprite.width, mSprite.height);
			mInnerUV.Set(mSprite.x + mSprite.borderLeft, mSprite.y + mSprite.borderTop,
				mSprite.width - mSprite.borderLeft - mSprite.borderRight,
				mSprite.height - mSprite.borderBottom - mSprite.borderTop);

			mOuterUV = NGUIMath.ConvertToTexCoords(mOuterUV, tex.width, tex.height);      // Convert from top-left based pixel coordinates to bottom-left based UV coordinates.
			mInnerUV = NGUIMath.ConvertToTexCoords(mInnerUV, tex.width, tex.height);
		}

		switch (type)
		{
			case Type.Simple:
			SimpleFill(verts, uvs, cols);
			break;

			case Type.Sliced:
			SlicedFill(verts, uvs, cols);
			break;

			case Type.Filled:
			FilledFill(verts, uvs, cols);
			break;

			case Type.Tiled:
			TiledFill(verts, uvs, cols);
			break;
		}
	}

      SimpleFill最简单了,直接添加Verts,UVs,Colors,SimpleFill就只有四个顶点绘制贴图,SliceFill,TiledFill,FiledFill都是转换为SimpleFill的情况来绘制的。

 

protected void SimpleFill (BetterList<Vector3> verts, BetterList<Vector2> uvs, BetterList<Color32> cols)
	{
		Vector2 uv0 = new Vector2(mOuterUV.xMin, mOuterUV.yMin);
		Vector2 uv1 = new Vector2(mOuterUV.xMax, mOuterUV.yMax);

		Vector4 v = drawingDimensions;

		verts.Add(new Vector3(v.x, v.y));
		verts.Add(new Vector3(v.x, v.w));
		verts.Add(new Vector3(v.z, v.w));
		verts.Add(new Vector3(v.z, v.y));

		uvs.Add(uv0);
		uvs.Add(new Vector2(uv0.x, uv1.y));
		uvs.Add(uv1);
		uvs.Add(new Vector2(uv1.x, uv0.y));

		Color colF = color;
		colF.a *= mPanel.alpha;
		Color32 col = atlas.premultipliedAlpha ? NGUITools.ApplyPMA(colF) : colF;
		
		cols.Add(col);
		cols.Add(col);
		cols.Add(col);
		cols.Add(col);
	}

      SliceFill其实就是把矩形纹理切成九宫格,就是九个SimpleFill,贴出注释,详细看源码:

                                           Sliced sprite fill function is more complicated as it generates 9 quads instead of 1.

      TiledFill可以简单的看成:(组件面积/纹理贴图面积 )个SimpleFill

       FilledFill代码是很复杂,但是其实也是把扇形切割成四边形来SimpleFill

       这种解决问题的方法很值得借鉴,记得高中的时候解数学难题一般都是将复杂问题分解成多个简单的问题,SimpleFill相当于是一个子问题,Slice,Tiled,Filed都是转化为Simple的情况来解决,这个点推到动态规划算法的方程一样,如果对问题理解好了,自然就迎刃而解。

 

 UILabel

       UILabel的样式越来越多了,和UISprite一样一个脚本充当多种角色(UISprite,UIFilledSprite,UISlicedSprite),由于UIFont有使用ttf 的Font的动态字体,还有使用BMFont等软件编辑生成的Bitmap字体,所以成员变量比较多。mFont就是当前UILabel使用的字体,如果是动态字体的话,trueTypeFont(ttf的缩写)就是Font,字体的大小就是mFont的defaultSize。

       supportEncoding: 是否支持颜色和换行

       大多数属性改变都会调用到ProcessText():

         /// <summary>
	/// Process the raw text, called when something changes.
	/// </summary>

	void ProcessText (bool legacyMode)
	{
		if (!isValid) return;

		mChanged = true;
		hasChanged = false;

		int fs = fontSize;  //字体大小
		float ps = pixelSize;   //UIFont的像素大小
		float invSize = 1f / ps;
		
		mPrintedSize = Mathf.Abs(legacyMode ? Mathf.RoundToInt(cachedTransform.localScale.x) : fs);
		
		float lw = legacyMode ? (mMaxLineWidth != 0 ? mMaxLineWidth * invSize : 1000000) : width * invSize;
		float lh = legacyMode ? (mMaxLineHeight != 0 ? mMaxLineHeight * invSize : 1000000) : height * invSize;

		if (mPrintedSize > 0)
		{
			for (;;)
			{
				mScale = (float)mPrintedSize / fs;  //计算出放缩比

				bool fits = true;

				int pw = (mOverflow == Overflow.ResizeFreely) ? 100000 : Mathf.RoundToInt(lw / mScale);
				int ph = (mOverflow == Overflow.ResizeFreely || mOverflow == Overflow.ResizeHeight) ?
					100000 : Mathf.RoundToInt(lh / mScale);

				if (lw > 0f || lh > 0f)
				{
					if (mFont != null) fits = mFont.WrapText(mText, fs, out mProcessedText, pw, ph, mMaxLineCount, mEncoding, mSymbols);
#if DYNAMIC_FONT
					else fits = NGUIText.WrapText(mText, mTrueTypeFont, fs, mFontStyle, pw, ph, mMaxLineCount, mEncoding, out mProcessedText);
#endif
				}
				else mProcessedText = mText;

				// Remember the final printed size
				if (!string.IsNullOrEmpty(mProcessedText))
				{
					if (mFont != null) mCalculatedSize = mFont.CalculatePrintedSize(mProcessedText, fs, mEncoding, mSymbols);
#if DYNAMIC_FONT
					else mCalculatedSize = NGUIText.CalculatePrintedSize(mProcessedText, mTrueTypeFont, fs, mFontStyle, mEncoding);
#endif
				}
				else mCalculatedSize = Vector2.zero;
                                //根据不同overflowMethod调整文字显示
				if (mOverflow == Overflow.ResizeFreely)
				{
					mWidth = Mathf.RoundToInt(mCalculatedSize.x * ps);
					mHeight = Mathf.RoundToInt(mCalculatedSize.y * ps);
				}
				else if (mOverflow == Overflow.ResizeHeight)
				{
					mHeight = Mathf.RoundToInt(mCalculatedSize.y * ps);
				}
                                
				else if (mOverflow == Overflow.ShrinkContent && !fits)
				{
					if (--mPrintedSize > 1) continue;
				}

				// Upgrade to the new system
				if (legacyMode)
				{
					width = Mathf.RoundToInt(mCalculatedSize.x * ps);
					height = Mathf.RoundToInt(mCalculatedSize.y * ps);
					cachedTransform.localScale = Vector3.one;
				}
				break;
			}
		}
		else
		{
			cachedTransform.localScale = Vector3.one;
			mProcessedText = "";
			mScale = 1f;
		}
	}

       这里涉及的变量比较多,做下简单的列举:

int fs = fontSize;  //字体大小
float ps = pixelSize;  //一个像素的大小
float invSize = 1f/ps;     可以认为unity单位1的距离有多少个像素
mPritedSize;  可以认为等同 fs,即字体大小
float lw 和 float lh;   //长和宽各有多少个像素
mScale;   就是transfrom的放缩比
int pw 和 int ph;   //在不同overflowMethod下的像素个数,考虑放缩mScale

mCalculatedSize;   //文字一共占用多少个像素面积
mWidth = mCalculatedSize * ps;   

两个函数:
mFont.WrapText;   //根据当前参数调整文字显示
mCalculatedSize;    //计算当前显示的文字的像素面积

          ProcessText的目的就是通过当前设置的参数去调整文字的显示以及长宽等……

          UIWidget的子类都有一个必可少的函数OnFill,因为整个函数是虚函数,函数的作用在前面都以及讲过,这里主要是调用UIFont的Print函数对Verts,UVs,Colors进行填充,就不贴代码了,有需求可以仔细研究下……

 

文本缩放样式:

public enum Overflow
{
	ShrinkContent,
	ClampContent,
	ResizeFreely,
	ResizeHeight,
}

public enum Crispness
{
	Never,
	OnDesktop,
	Always,
}

       UILabel现在支持四种文本样式,这里做下简单的解释:

                 1.ShrinkContent,总是显示所有文字,根据当前的width和height进行缩放

                 2.ClampContent,一看到Clamp就想起Clamp函数,也就是不管文本多少个字,根据当前的width和height来显示,超出部分不显示

                 3.ResizeFreely,会对当前的文字长度和行数来调整,UILabel的width和height

                 4.ResizeHeight,如果UILabel的width不够就会调整height值,进行多行显示,即保持宽度不变,更加文本长度调整height

      除了第一种会对文字的大小进行放缩,其他三个样式都不会对文字本身的大小进行调整。』

 

                                                                                                                                                                                              增补于 2013,12,23 下午 16:38

 

『Bug修复:                                                                                                                                                             

      更新了NGUI到版本3.0.7f3,发现使用动态字体时,多行文字总是向上增长(而且只有动态字体才出现,Bitmap没有这个问题,后面测试了才发现的),然后只有比对UILabel和NGUIText的代码:

			v0.x = x + mTempChar.vert.xMin;
			v0.y = y + mTempChar.vert.yMax - baseline;

 只要细致的,都能看出有问题,x和y都是绝对值(正),所以如果x方向是 + ,那么 y方向就应该是 -。

       然后又去看了之前的版本发现:

                        v0.x =  (x + mTempChar.vert.xMin);
			v0.y = -(y - mTempChar.vert.yMax + baseline);

 看到这里就应该恍然大悟了,NGUI的技术太不认真了哈。

这样也推荐下文件比较工具Beyound Compare软件,还是很容易两个文件不同的地方。』

                                                                                              最近没有时间写博客了,只有利用写时间进行修修补补                     增补于:2014,1,3:14:00

小结:

        其实,UISprite很简单,不难,只是之前一直对Filed ,Slice,Tiled的实现很好奇,感觉很神奇,当然D.S.Qiu还有很多没有弄明白的:

                1.UVs是的作用是?

                2.在哪里指定材质Material或纹理

                3.Unity单位和屏幕分辨率以及像素大小的关系,现在感觉挺混乱的。

      

还是尽早去琢磨UIPanel吧。

        如果您对D.S.Qiu有任何建议或意见可以在文章后面评论,或者发邮件(gd.s.qiu@gmail.com)交流,您的鼓励和支持是我前进的动力,希望能有更多更好的分享。

        转载请在文首注明出处:http://dsqiu.iteye.com/blog/1968270

更多精彩请关注D.S.Qiu的博客和微博(ID:静水逐风)

 

 

        本来按到原来本来想另起一篇写UILabel的,但是感觉目前的写不到很长和耐人寻味的篇幅,所以只有接在这篇写下去,改了个题目。

        然后上面的疑问,也有了点理解:1.Verts是渲染的位置的顶点,而且是相对于UIPanel,UVs就是纹理上的坐标,如果说Verts是画板,UVs就是颜色板上的颜色;2.Material就是UIAtlas和UIFont中的Material。

        2013.11.5 凌晨   增补

 

 

 

 

 

1
0
分享到:
评论
6 楼 DSQiu 2014-01-16  
AWNUXCVBN 写道
DSQiu 写道
AWNUXCVBN 写道
DSQiu 写道
AWNUXCVBN 写道
请问使用动态字体产生大量文字UILabel Unity3d会卡顿该怎么解决啊?

你的NGUI版本是多少?如果是3.0以后的,你要把depth 改成联系的,这样所有UILabel就共有一个DC了,就不会了

额,还是2.6几的……肿么办?

卡顿主要是DC太高了,你优化下这方面,跟动态字体没啥关系……

求教,我的是pc程序state显示DC只有100左右,我在场景里动态生成6个左右的panel,每个panel上的label里大概有100字左右,内容不同,生成时赋值,不知道这些步奏中哪里会卡……

DC 100 太恐怖了,我们整个游戏一般都只有几十个
5 楼 AWNUXCVBN 2014-01-16  
DSQiu 写道
AWNUXCVBN 写道
DSQiu 写道
AWNUXCVBN 写道
请问使用动态字体产生大量文字UILabel Unity3d会卡顿该怎么解决啊?

你的NGUI版本是多少?如果是3.0以后的,你要把depth 改成联系的,这样所有UILabel就共有一个DC了,就不会了

额,还是2.6几的……肿么办?

卡顿主要是DC太高了,你优化下这方面,跟动态字体没啥关系……

求教,我的是pc程序state显示DC只有100左右,我在场景里动态生成6个左右的panel,每个panel上的label里大概有100字左右,内容不同,生成时赋值,不知道这些步奏中哪里会卡……
4 楼 DSQiu 2014-01-16  
AWNUXCVBN 写道
DSQiu 写道
AWNUXCVBN 写道
请问使用动态字体产生大量文字UILabel Unity3d会卡顿该怎么解决啊?

你的NGUI版本是多少?如果是3.0以后的,你要把depth 改成联系的,这样所有UILabel就共有一个DC了,就不会了

额,还是2.6几的……肿么办?

卡顿主要是DC太高了,你优化下这方面,跟动态字体没啥关系……
3 楼 AWNUXCVBN 2014-01-16  
DSQiu 写道
AWNUXCVBN 写道
请问使用动态字体产生大量文字UILabel Unity3d会卡顿该怎么解决啊?

你的NGUI版本是多少?如果是3.0以后的,你要把depth 改成联系的,这样所有UILabel就共有一个DC了,就不会了

额,还是2.6几的……肿么办?
2 楼 DSQiu 2014-01-16  
AWNUXCVBN 写道
请问使用动态字体产生大量文字UILabel Unity3d会卡顿该怎么解决啊?

你的NGUI版本是多少?如果是3.0以后的,你要把depth 改成联系的,这样所有UILabel就共有一个DC了,就不会了
1 楼 AWNUXCVBN 2014-01-16  
请问使用动态字体产生大量文字UILabel Unity3d会卡顿该怎么解决啊?

相关推荐

    查找NGUI中UIlabel的路径

    查找NGUI中UIlabel的路径,自己写的,要用下载吧。

    HTML Engine for NGUI & Unity GUI unity3d

    它将呈现的HTML UILabel和UISprite。该软件包还包含一张NGUI分布版本。 2.添加动画图像支持NGUI的由UIAnimationSprite。 3.改变'DIV'标签'旋转'。 4.添加'身份证'归属'P','自旋','IMG'和'一个'标签。所以NGUI的...

    [Shader]对NGUI的UISprite和UITexture进行裁剪

    NGUI 对Sprite和Texture做遮罩的。具体可以看: http://blog.csdn.net/kakashi8841/article/details/44851885

    NGUI Tutorial Create a Button & Download

    NGUI Tutorial Create a Button

    NGUI插件3.11.2版本

    在场景视图中看到的就是在游戏视图中得到的(所见即所得)。 基于组件的、模块化的特性:要让你的界面控件做什么,只需为其附加相应的行为,而不需要编码。 全面支持iOS/Android和Flash。 灵活的事件系统。 可以让...

    NGUI_3.11.3.unitypackage

    在场景视图中看到的就是在游戏视图中得到的(所见即所得)。 基于组件的、模块化的特性:要让你的界面控件做什么,只需为其附加相应的行为,而不需要编码。 全面支持iOS/Android和Flash。 灵活的事件系统。 可以让...

    NGUI3.11.4

    在场景视图中看到的就是在游戏视图中得到的(所见即所得)。 基于组件的、模块化的特性:要让你的界面控件做什么,只需为其附加相应的行为,而不需要编码。 全面支持iOS/Android和Flash。 灵活的事件系统。 可以让...

    NGUI Next-Gen UI 2020.1.5

    - 编辑器集成,所见即所得 - 本地化、数据绑定、委托、事件 - 支持所有平台 - 制作进行 1 次绘制调用的 UI - 随附完整的 C# 源代码 - 已广泛优化 - 专门团队支持 2020.1.5 - NEW: You can now specify per-symbol ...

    CustomGUI.unitypackage

    使用unity原生GUI封装,来达到UGUI,NGUI所见即所得的效果和部分功能,目的是由此来了解高级UI的原理。

    NGUI插件大全

    在场景视图中看到的就是在游戏视图中得到的(所见即所得)。 基于组件的、模块化的特性:要让你的界面控件做什么,只需为其附加相应的行为,而不需要编码。 全面支持iOS/Android和Flash。 灵活的事件系统。 可以让...

    NGUI 3.5.9

    在场景视图中看到的就是在游戏视图中得到的(所见即所得)。  基于组件的、模块化的特性:要让你的界面控件做什么,只需为其附加相应的行为,而不需要编码。  全面支持iOS/Android和Flash。  灵活的事件系统。...

    NGUI 2020.1.5.unitypackage

    NGUI 是一款非常强大的 UI 系统和事件通知框架。...- 编辑器集成,所见即所得 - 本地化、数据绑定、委托、事件 - 支持所有平台 - 制作进行 1 次绘制调用的 UI - 随附完整的 C# 源代码 - 已广泛优化 - 专门团队支持

    最新版本的NGUI插件NGUI Next-Gen UI 覆盖unity多个版本

    NGUI Next-Gen UI是一款功能强大、灵活性高的UI插件,是当前最新版本的NGUI插件。它可以覆盖Unity的多个版本,包括Unity 5、Unity 2017和Unity 2018等。与其他UI插件相比,NGUI Next-Gen UI具有高效的性能和优秀的...

    NGUI3.5.9 Unity3d UI开发神器

    在场景视图中看到的就是在游戏视图中得到的(所见即所得)。  基于组件的、模块化的特性:要让你的界面控件做什么,只需为其附加相应的行为,而不需要编码。  全面支持iOS/Android和Flash。  灵活的事件系统。...

    NGUI 2019_3_0.unitypackage.zip

    NGUI是一个非常强大的UI系统和事件通知框架。 特征 -编辑器集成,所见即所得 -本地化,数据绑定,委托,事件 -支持所有平台 -进行1次抽签的UI -随附完整的C#源代码 -广泛优化 -专用支持

    Unity插件 NGUI各种版本合集

    本包中共有六个版本的NGUI,大家可以自己选择版本。 NGUI Next-Gen UI 3.6.0.unitypackage NGUI Next-Gen UI 3.12.1(u5.6.5).unitypackage NGUI Next-Gen UI 2019.3.0.unitypackage NGUI Next-Gen UI v2018.3.0....

    HTML Engine for NGUI & Unity GUI

    HTML Engine for NGUI & Unity GUI

    NGUI Next-Gen UI v3.6.0

    最新版unity3d扩展插件:NGUI Next-Gen UI3.6.0 运行Unity3D,解压后此压缩包,在菜单Assets中选择自定义导入。 如果无法导入时,请检查导入目录中是否存在中文字符。...所见即所得的集成编辑器,支持所有平台。

    NGUI3.6.4 最新版本NGUI ngui

    大名鼎鼎的U3D插件NGUI,移动开发做界面都用它.最新官方版本,包含API文档.

    NGUI v3.11.2

    NGUI v3.11.2,适合Unity2017 NGUI 是一款非常强大的...- 编辑器集成,所见即所得 - 本地化、数据绑定、委托、事件 - 支持所有平台 - 制作进行 1 次绘制调用的 UI - 随附完整的 C# 源代码 - 已广泛优化 - 专门团队支持

Global site tag (gtag.js) - Google Analytics