`

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会卡顿该怎么解决啊?

相关推荐

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

    本篇文章将详细讲解如何利用Shader技术对NGUI的UISprite和UITexture进行裁剪,以实现遮罩效果。 首先,我们需要理解Shader的基本概念。Shader是图形处理器(GPU)上运行的程序,用于控制场景中的像素表现。在Unity...

    查找NGUI中UIlabel的路径

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

    HTML Engine for NGUI & Unity GUI unity3d

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

    NGUI Tutorial Create a Button & Download

    在本教程中,我们将深入探讨如何...NGUI的强大之处在于其高度的可定制性和灵活性,无论是对于初学者还是有经验的开发者来说都是一个值得学习的工具。在未来的游戏开发过程中,掌握NGUI将有助于提升UI设计的质量和效率。

    Unity3d NGUI 图文并排

    NGUI(Next-Generation User Interface)是Unity3d早期的一个流行UI系统,它允许开发者创建复杂的用户界面,并提供丰富的交互功能。在Unity3d中实现“图文并排”是UI设计中的常见需求,特别是在创建聊天应用或者教程...

    NGUI插件3.11.2版本

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

    NGUI_3.11.3.unitypackage

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

    NGUI3.11.4

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

    NGUI 2019_3_0.unitypackage.zip

    开发者可以在Unity编辑器中直接设计和调整UI元素,实现所见即所得的效果。这种设计方式极大地提高了开发效率,使得UI设计和游戏逻辑的开发可以同步进行,减少了开发者在不同工具间切换的时间成本。 二、本地化与...

    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的原理。

    UILabel富文本

    在iOS开发中,UILabel是用于显示单行或多行非编辑性文本的标准控件。然而,UILabel默认只支持基本的文本格式,如字体、颜色和对齐方式。但是,通过使用富文本(Rich Text),我们可以让UILabel展示更加复杂和丰富的...

    NGUI离线文档

    除此之外,NGUI还包括了UILabel,用于显示文本,可以设置字体、颜色、对齐方式等属性。UIImage用于展示图片,支持精灵(Sprite)和纹理(Texture)两种模式。UIButton则是用户交互的核心,可以绑定点击事件,实现...

    NGUI插件大全

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

    NGUI3.6.4 最新版本NGUI ngui

    《NGUI 3.6.4:打造高效Unity3D移动界面的利器》 NGUI,全称为Next-Gen UI,是Unity3D游戏引擎中的一款广泛应用的界面系统插件,尤其在移动游戏开发领域备受青睐。其3.6.4版本作为官方发布的最新版,不仅集成了之前...

    NGUI 3.5.9

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

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

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

    Unity NGUI制作的连线方法

    在这个"Unity NGUI制作的连线方法"的Demo中,我们将探讨如何通过Unity和NGUI来实现两点之间的连线效果,特别是使用UISprite来拼接成一条虚线。这个技术常应用于游戏的副本界面,为玩家提供路径指示或者状态连接等...

    NGUI 3.0.0工具手册

    《NGUI 3.0.0工具手册》是专为Unity开发者设计的一份详细指南,主要涵盖NGUI这一用户界面系统的新版本3.0.0的相关知识。NGUI是一款强大的UI解决方案,它允许开发者在Unity引擎中创建复杂、高性能且交互性强的用户...

    NGUI图文混排demo

    NGUI图文混排的实现是其核心特性之一,它允许开发者在界面上灵活地组合文字和图像,创造出各种复杂的布局效果。下面将详细探讨NGUI图文混排的相关知识点。 1. **NGUI概述**: NGUI(Natural GUI)是由Tasharen ...

Global site tag (gtag.js) - Google Analytics