【unity】NGUI之UIRoot屏幕分辨率自适应

http://blog.csdn.net/onerain88/article/details/11713299

NGUI在Unity3D游戏开发中非常常用,而NGUI对于每一个UI场景,都是以一个UIRoot为UI游戏对象树的根的,那么这个UIRoot是起什么作用的呢?

先简单看一下UIRoot中的基本属性

UIRoot游戏对象的属性只有4个,分别是缩放规则,手动高度,最小高度和最大高度

而正是这4个属性,将影响整个UI场景中整体的缩放比例,当设置好这4个属性之后,UIRoot游戏对象的相对缩放值(LocalScale)将会生成并且不能被直接修改(NGUI中很多属性都是不能直接被修改的,这种控制是在UIRoot脚本中,通过设置[ExecuteInEditMode]做到的,其相对缩放值是根据UIRoot的4个属性计算出来的),那么这4个属性分别是什么含义呢?

(吐槽一下,也许这里的用户体验并不足够友好,因为Manual Height和Minimum Height, Maximum Height并不会同时起作用,如果能做到在选择Scaling Style时动态的切换,使用者也许能更清楚它们之间的关系)


1.Scaling Style (缩放类型)

这是一个简单的枚举变量,包括三个枚举值

[csharp] view plaincopyprint?

  1. public enum Scaling  

  2.     {  

  3.         PixelPerfect,  

  4.         FixedSize,  

  5.         FixedSizeOnMobiles,  

  6.     }  

public enum Scaling
	{
		PixelPerfect,
		FixedSize,
		FixedSizeOnMobiles,
	}

(FixedSize和FixedSizeOnMobiles类似,并且后者只添加了对ios和android平台的判断,所以前者可以替代后者使用)

 

这里只讨论PixelPerfect和FixedSize的区别,两者都是针对于所有在此UIRoot之下的UI组件而言的,也可以认为是在此UIRoot下,整个游戏屏幕的尺寸的缩放类型!


2.Manual Height和Minimum Height, Maximum Height (手动高度和最小高度,最大高度)

Manual Height和Minimum Height, Maximum Height不会同时对此UIRoot起作用,当选择Scaling Style为PixelPerfect时,我们需要设置Minimum Height, Maximum Height;而当Scaling Style为FixedSize或FixedSizeOnMobiles时,我们需要设置Manual Height。(这就是我前面吐槽的原因)


3.使用

(1)PexelPerfect和Minimum Height, Maximum Height

这个组合主要用于我们期望所有的UI纹理都“尽量”不进行缩放,所谓“尽量”的程度,则是取决于Minimum Height和Maximum Height,Minimum Height表示当设备分别率小于此设置值时,根据此设置值对UIRoot进行缩放;Maximum Height表示当设备分辨率大于此设置值时,根据此设置值对UIRoot进行缩放(UIRoot是UI游戏对象树的根,修改UIRoot的缩放,则会影响整棵UI树的缩放)

(2)FixedSize和Manul Height

这个组合主要用于我们期望所有的UI纹理都进行“合适”的缩放,所谓“合适”缩放的原则,则是根据Manual Height设置值,当设备分辨率的高度值不同于此设置值时,则根据其比例(即Manual Height / Screen Height)对整棵UI树的进行“等比”缩放(宽度的缩放比也是此比例值),这样,我们就可以做一套资源,在不同尺寸的分辨率最好的“不变形”的适配了

(3)交集

前面两组在什么情况下等同呢?

Manual Height == Minimum Height == Maximum Height 

推导过程,呵呵~~

具体可参考UIRoot中activeHeight属性和GetPixelSizeAdjustment的计算过程


4.这也许并不够

基于以上推到,当我们以1024x768为标准分辨率做一套UI资源(也就是选择FixedSize并且Manual Height=768),似乎可以满足百分之90以上的机型了,而为什么是1024x768呢?

既然我们已经容忍在除1024x768之外的其他设备上进行等比缩放了,那为什么不是960x640呢?

计算一下1024x768的宽高比=1.33,960x640的宽高比=1.5,这就是移动设备的分辨率比例的全部了吗?

当然不是,iphone5的比例就要大于1.5,还有各种奇葩的android设备呢,比如夏新的n828就是960x540,宽高比=1.78

那为什么以1024x768为标准呢?

因为1.33的宽高比,当我们的1024x768的资源到960x640的设备上时会有什么现象?

根据Manual Height / Screen Height的比例可知,我们需要缩放768 / 640 = 1.2倍,假设是一张1024x768的纹理,高度缩放1.2倍变为了640,宽度也要相应缩放1.2倍变为853(保证等比缩放不变形),也就是说1024x768的资源放到960x640上反而两边有了黑边,这是我们可以容忍的,我们可以做一个很大的背景或者拉伸,保证UI组件不变形即可,很多游戏都是这么做的,比如植物大战僵尸在iphone5和ipad上看到的背景视野并不一样大!

当放到夏新的机器上呢?

我们需要缩放768 / 540 = 1.4倍,宽度1024 / 1.4 = 731,这是可以的,只是看起来更怪一些,因为两边的黑边相对比例更大了(960 - 731=229的黑边区域)

而我表示android机器的分辨率奇葩到只有想不到,没有做不到的程度,也许宽高比1.7并不是终点,当遇到1.8之后,黑边的相对比例会更大。。。


5.问题又来了

假设我们的游戏类型更适合iphone手机玩,不太适合ipad,所以我希望能以960x640为标准做一套资源,可以吗?

我只能说不太可以,因为你要在设计UI组件的大小做限制了,为什么需要做限制?

假设我有一张纹理是960x640大小的,在iphone上铺满整屏,根据我们的设置(FixedSize和Manual Height=640),拿到1024x768的分辨率上,高度640 / 768 = 0.83,为了保证等比缩放,宽度960 / 0.83 = 1156,不幸的事情发生了,1156 > 1024,这个UI组件宽度超过了屏幕的宽度,被裁剪了。。。这是我们不能容忍的,或许你可以说我们尽量不做这种尺寸的UI,OK,你可以对UI尺寸加限制,但是当面对android那些奇葩的分辨率的时候,你会发现限制越来越大,这也许会让美术和策划疯掉!


6.解决方案

当我们花上一些时间去观察现在移动设备的分辨率时,虽然奇葩很多,但是还是有一些规律的,规律的在于宽高比而不在于具体尺寸,大体上划分一下宽高比在1.3,1.5,1.7的范围上的居多(基本是全部吧!)即便是再有1.2,1.8的比例也无妨。。。

NGUI为我们提供的方案只有以各种高度为衡量标准是不够的,我们应该加上一种以宽度为衡量标准的缩放类型

而对于UI资源的标准,我们选取960x640,宽高比为1.5

这样,当我们在兼容大于1.5的尺寸的时候,使用NGUI的现有方案;当我们在兼容小于1.5的尺寸的时候,使用以宽度为衡量标准

也就是说有一个类似Manual Width的属性,当小于1.5时,我们使用Manual Width / Screen Width得出整棵UI树的缩放比例!

这样做的好处是“黑边”区域不会太大,并且不需要对UI组件的大小做限制!


PS: 我表示以上言论对于“可以为适配分辨率做无数套图的资源土豪”而言可能是我想多了!

http://blog.csdn.net/asd237241291/article/details/8126619

 

原创文章如需转载请注明:转载自 脱莫柔Unity3D学习之旅 本文链接地址:Unity3D NGUI自适应屏幕分辨率

1.UIRoot:根据高度自适应屏幕分辨率。

NGUI根目录的UIRoot组件自带了根据高度自适应分辨率的功能。

Scaling Style属性可选择三种不同的缩放策略。

  1. PixelPerfect 完美像素:直接显示设定好的像素。当屏幕高度低于minimum Height时按比例缩小,当屏幕高度大于maximum Height时按比例扩大。

  2. FixedSize 按比例缩放:在设定好的基础上,直接按比例缩放。

  3. FixedSizeOnMobiles 合体版,android和ios为FixedSize方式,其它按照PixelPerfect方式。

// FixedSize时:填理想分辨率的高度
// FixedSizeofWidth时:填理想分辨率的宽度

Manual Height:先按照理想分辨率做。当Game视图(打包后的屏幕分辨率)不是这个理想分辨率的时候就会进行比例缩放。


Minimum Height:Game视图低于这个数值开始按比例缩放。
Maximum Height:Game视图高于这个数值开始按比例缩放。


这三种缩放方式全部都是按照高度计算缩放比例,完全忽略宽度。

在制作时UI比例按照最长的16:9(红色)来做,另外3:2(绿色)为内容区域。红色两边的位置在不同比例的手机上会有不同程度的别切割的情况,所以不要把游戏内容放在这一区域。



2.UIRoot:根据宽度自适应屏幕分辨率。

UIRoot已经实现了根据高度自适应的功能,但是我现的需求是要根据宽度来自适应,屏幕高度高于UI高度则留空白。


1.首先给UIRoot增加一种状态

[csharp] view plaincopyprint?在CODE上查看代码片派生到我的代码片

  1. public enum Scaling  

  2. {  

  3.     PixelPerfect,  

  4.     FixedSize,  

  5.     FixedSizeOnMobiles,  

  6.         /// <summary>  

  7.         /// 根据宽度适配  

  8.         /// </summary>  

  9.         FixedSizeofWidth,  

  10. }  

public enum Scaling
{
	PixelPerfect,
	FixedSize,
	FixedSizeOnMobiles,
        /// <summary>
        /// 根据宽度适配
        /// </summary>
        FixedSizeofWidth,
}

2.实现还是需要FixedSize的算法,所以需要修改两个判断语句

修改1:

[csharp] view plaincopyprint?在CODE上查看代码片派生到我的代码片

  1. public float GetPixelSizeAdjustment (int height)  

  2. {  

  3.     height = Mathf.Max(2, height);  

  4.         //修改1  

  5.         if (scalingStyle == Scaling.FixedSize || scalingStyle == Scaling.FixedSizeofWidth)  

  6.         return (float)manualHeight / height;  

  7.  

  8. #if UNITY_IPHONE || UNITY_ANDROID  

  9.     if (scalingStyle == Scaling.FixedSizeOnMobiles)  

  10.         return (float)manualHeight / height;  

  11. #endif  

  12.     if (height < minimumHeight) return (float)minimumHeight / height;  

  13.     if (height > maximumHeight) return (float)maximumHeight / height;  

  14.     return 1f;  

  15. }  

public float GetPixelSizeAdjustment (int height)
{
	height = Mathf.Max(2, height);
        //修改1
        if (scalingStyle == Scaling.FixedSize || scalingStyle == Scaling.FixedSizeofWidth)
		return (float)manualHeight / height;

#if UNITY_IPHONE || UNITY_ANDROID
	if (scalingStyle == Scaling.FixedSizeOnMobiles)
		return (float)manualHeight / height;
#endif
	if (height < minimumHeight) return (float)minimumHeight / height;
	if (height > maximumHeight) return (float)maximumHeight / height;
	return 1f;
}

修改2:

[csharp] view plaincopyprint?在CODE上查看代码片派生到我的代码片

  1. public int activeHeight  

  2. :{  

  3.     get  

  4.     {  

  5.         int height = Mathf.Max(2, Screen.height);  

  6.         //修改2  

  7.         if (scalingStyle == Scaling.FixedSize || scalingStyle == Scaling.FixedSizeofWidth)   

  8.             return manualHeight;  

  9. #if UNITY_IPHONE || UNITY_ANDROID  

  10.         if (scalingStyle == Scaling.FixedSizeOnMobiles)  

  11.             return manualHeight;  

  12. #endif  

  13.         if (height < minimumHeight) return minimumHeight;  

  14.         if (height > maximumHeight) return maximumHeight;  

  15.         return height;  

  16.     }  

  17. }  

public int activeHeight
:{
	get
	{
		int height = Mathf.Max(2, Screen.height);
		//修改2
		if (scalingStyle == Scaling.FixedSize || scalingStyle == Scaling.FixedSizeofWidth) 
			return manualHeight;
#if UNITY_IPHONE || UNITY_ANDROID
		if (scalingStyle == Scaling.FixedSizeOnMobiles)
			return manualHeight;
#endif
		if (height < minimumHeight) return minimumHeight;
		if (height > maximumHeight) return maximumHeight;
		return height;
	}
}


3.增加按宽度自适应算法

[csharp] view plaincopyprint?在CODE上查看代码片派生到我的代码片

  1. void Update ()  

  2. {  

  3. #if UNITY_EDITOR  

  4.     if (!Application.isPlaying && gameObject.layer != 0)  

  5.         UnityEditor.EditorPrefs.SetInt("NGUI Layer", gameObject.layer);  

  6. #endif  

  7.     if (mTrans != null)  

  8.     {  

  9.         float calcActiveHeight = activeHeight;  

  10.   

  11.         if (calcActiveHeight > 0f )  

  12.         {  

  13.             float size = 2f / calcActiveHeight;  

  14.   

  15.             //看这里,看这里,看这里  

  16.             if (scalingStyle == Scaling.FixedSizeofWidth)  

  17.                     {  

  18.                 float radio = (float)Screen.width / Screen.height;  

  19.                 size = size * radio;  

  20.             }  

  21.   

  22.             Vector3 ls = mTrans.localScale;  

  23.   

  24.             if (!(Mathf.Abs(ls.x - size) <= float.Epsilon) ||  

  25.                 !(Mathf.Abs(ls.y - size) <= float.Epsilon) ||  

  26.                 !(Mathf.Abs(ls.z - size) <= float.Epsilon))  

  27.             {  

  28.                 mTrans.localScale = new Vector3(size, size, size);  

  29.             }  

  30.         }  

  31.     }  

  32. }  

void Update ()
{
#if UNITY_EDITOR
	if (!Application.isPlaying && gameObject.layer != 0)
		UnityEditor.EditorPrefs.SetInt("NGUI Layer", gameObject.layer);
#endif
	if (mTrans != null)
	{
		float calcActiveHeight = activeHeight;

		if (calcActiveHeight > 0f )
		{
			float size = 2f / calcActiveHeight;

			//看这里,看这里,看这里
			if (scalingStyle == Scaling.FixedSizeofWidth)
	                {
				float radio = (float)Screen.width / Screen.height;
				size = size * radio;
			}

			Vector3 ls = mTrans.localScale;

			if (!(Mathf.Abs(ls.x - size) <= float.Epsilon) ||
			    !(Mathf.Abs(ls.y - size) <= float.Epsilon) ||
			    !(Mathf.Abs(ls.z - size) <= float.Epsilon))
			{
				mTrans.localScale = new Vector3(size, size, size);
			}
		}
	}
}


3.UIStretch:根据宽度自适应屏幕分辨率。(NGUI3.0.7版本后不再支持)

这个是早期NGUI实现自适应分别率的一种方法,新版本中加入UIRoot自适应的方法后,这个脚本就不在被官方推荐使用了。


这个脚本自带的Style除了按高度自适应的功能之外,按宽度自适应是要拉伸图像的,并不能满足我们的要求。

最符合我们的要求的就是BasedOnHeight,那我们就按照这个功能修改一个BasedOnWidth出来,之前的博客中写过这个功能,现在这篇文章直接替换了之前的,所以我还是贴出修改的内容吧。

首先在Style枚举中增加一个BasedOnWidth,类型

[csharp] view plaincopyprint?在CODE上查看代码片派生到我的代码片

  1. public enum Style  

  2. {  

  3.     None,  

  4.     Horizontal,  

  5.     Vertical,  

  6.     Both,  

  7.     BasedOnHeight,  

  8.         BasedOnWidth,  

  9.     FillKeepingRatio,   

  10.     FitInternalKeepingRatio  

  11. }  

public enum Style
{
	None,
	Horizontal,
	Vertical,
	Both,
	BasedOnHeight,
        BasedOnWidth,
	FillKeepingRatio, 
	FitInternalKeepingRatio
}

Update方法中增加一个if分支。

[csharp] view plaincopyprint?在CODE上查看代码片派生到我的代码片

  1. if (style == Style.BasedOnHeight)  

  2. {  

  3.     localScale.x = relativeSize.x * rectHeight;  

  4.     localScale.y = relativeSize.y * rectHeight;  

  5. }else if (style == Style.BasedOnWidth)  

  6. {  

  7.         localScale.x = relativeSize.x * rectWidth;  

  8.         localScale.y = relativeSize.y * rectWidth;   

  9. }  

  10. else if (style == Style.FillKeepingRatio)  

  11. {……}  

	if (style == Style.BasedOnHeight)
	{
		localScale.x = relativeSize.x * rectHeight;
		localScale.y = relativeSize.y * rectHeight;
	}else if (style == Style.BasedOnWidth)
	{
	        localScale.x = relativeSize.x * rectWidth;
	        localScale.y = relativeSize.y * rectWidth; 
	}
	else if (style == Style.FillKeepingRatio)
	{……}

这个脚本是通过拉伸scale实现,所以这个脚本要放在你需要拉伸的UI上(如果你只需要一个背景图片自适应屏幕分辨率,那就把这个脚本添加到这个背景图片中,如果要一个panel内所有元素都自适应,那就放在这个panel上。如果想让所有的UI全部自适应分辨率,那就放在NGUI的cameta上。)

ui Camera属性需要选择渲染当前UI的摄像机。

使用步骤:

1.把Game视图设定一个最理想的宽度(以后按照这个比例缩放。)。

2.按需求选择一个放置UIStretch的物体,然后添加这个组件。并将ui cameta赋值。

3.将ui cameta的Size修改为当前屏幕的宽度。(这个物体的Scale的X、Y已经被UIStrech设置为屏幕宽度,此值不能被修改。)

4.这个时候改变窗口宽度,只有该物体Scale 的X、Y已被自动修改,UI视图已自动适应~!

你可能感兴趣的:(屏幕自适应)