首页 >Unity3D频道 >【Unity杂文】 > Unity3D研究院之使用Android的硬件缩放技术优化执行效率
2014
09-24

Unity3D研究院之使用Android的硬件缩放技术优化执行效率

Andoird太头疼了,尤其有些机器就是屏幕大别的硬件条件都很烂,为了优化渲染效率可以试试安卓的硬件缩放技术,我在真机上已经测试通过,效率确实提升了很多,FPS直线上升。。

原理如下 http://android-developers.blogspot.it/2013/09/using-hardware-scaler-for-performance.html

但是需要拿到Unity android的GLSurfaceView,可是我试过了很多办法就是拿不到它,不过还好Unity 直接提供了方法。

找一个合适的地方调用如下方法即可,这个方法在IOS和Android上都支持,但是经过测试IOS没必要使用,在android上还是很有必要使用的。

Screen.SetResolution(960,640,true);

这样会强制把屏幕的分辨率指定成960X640. 但是你的目标设备的分辨率可能不是3:2的,所以你需要做一个算法,假设搭建界面的时候是960X640,此时运行在1280X760的分辨率上,1280X760 和 960X640 等比换算一下 即可。代码我就不贴了。。。使用如下代码可以取出当前手机的屏幕分辨率,根据算法算一下就行了。

int width = Screen.currentResolution.width;
int height = Screen.currentResolution.height;

还有一点需要注意,经过我的测试发现 横屏游戏时 Android切到后台在返回前台渲染区域变成竖屏了。 所以每次切到前台的时候一定要强制设置一下分辨率。

	void OnApplicationPause(bool paused)
	{
		Logger.Print("paused:", paused);
		if (!paused) {
	            Screen.SetResolution(960,640,true);
		} 
	}

还有个问题就是当调用Screen.SetResolution(960,640,true); 紧跟着就打开NGUI界面,可能会造成UI闪烁一下。我想最好可以通过unity的GLSurfaceView在java内层去设置分辨率,但是我不知道怎么取到GLSurfaceView,或者如果有朋友知道请在下面留言告诉我一下。

做一个比较简单的界面, 界面上就写上 “正在加载中..”几个大字,因为背景是黑的闪就闪吧反证我们的策划说能忍。。

	IEnumerator Start()
	{
                Screen.SetResolution(960,640,true);
		yield return new WaitForSeconds(0.1f);
		//在下面代码里面在处理设置完分辨率以后的逻辑
	}

可能我上面写 Screen.SetResolution(960,640,true); 可能会误导大家,我举个里例子。开发的时候布的界面是 960X640 ,此时运行在 1280X720上。 那么 修改后的分辨率 就是 Screen.SetResolution(1138,640,true);

因为 1138 = 1280/(720/640)但是你还要去考虑 比 960X640 大的情况 或者 比 960X640小的情况。 总之就是等比例的计算 目标分辨率。如果一个是960 另一个就按比例算, 如果一个是640 另一个也按比例算。 

设置分辨率以后不会影响到你的代码原有的任何逻辑,效率确实提升了很多,建议大家最好在android上用一下。嘿嘿。欢迎大家在下面给 @雨松MOMO 留言,大家一起讨论。。

	private int scaleWidth =0;
	private int scaleHeight =0;
	public void setDesignContentScale()
	{
#if UNITY_ANDROID
		if(scaleWidth ==0 && scaleHeight ==0)
		{
			int width = Screen.currentResolution.width;
			int height = Screen.currentResolution.height;
			int designWidth = 960;
			int designHeight = 640;
			float s1 = (float)designWidth / (float)designHeight;
			float s2 = (float)width / (float)height;
			if(s1 < s2) {
				designWidth = (int)Mathf.FloorToInt(designHeight * s2);
			} else if(s1 > s2) {
				designHeight = (int)Mathf.FloorToInt(designWidth / s2);
			}
			float contentScale = (float)designWidth/(float)width;
			if(contentScale < 1.0f) { 
				scaleWidth = designWidth;
				scaleHeight = designHeight;
			}
		}
		if(scaleWidth >0 && scaleHeight >0)
		{
			if(scaleWidth % 2 == 0) {
				scaleWidth += 1;
			} else {
				scaleWidth -= 1;					
			}
			Screen.SetResolution(scaleWidth,scaleHeight,true);
		}
#endif
	}

	void OnApplicationPause(bool paused)
	{
		if (paused) {
		} else {
			setDesignContentScale();
		}
	}

 

 

我在补充一下: 这个方法不会影响NGUI的自适应,也不会影响游戏原有的任何逻辑也不用在改别的代码,在各种Android分辨率上都可行,效率就可以提升很多。我们项目已经轰轰烈烈的使用了,唯一的缺点就是设置分辨率的时候屏幕会闪一下。可以做一个黑屏 或者 几个字“正在加载中” 。。 所以我想在java层去改, 但是拿不到GLSurfaceView 。。如果有知道的朋友请在下面留言告诉我。谢谢。

这两天测试发现有时候按下手机的关机键,然后在返回游戏发现游戏屏幕竖过来了(我们是横屏游戏)。而且是随机性的,效果如下图所示:

zXg0F

 

 

在这两篇文章中我找到了答案

http://blog.k-res.net/archives/1702.html

http://stackoverflow.com/questions/7185644/android-opengl-crazy-aspect-ratio-after-sleep

解决这个问题需要在java这一层来配合,需要改一下你的UnityPlayerNativeActivity.java

	private int width,height;
	// Setup activity layout
	@Override protected void onCreate (Bundle savedInstanceState)
	{
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		super.onCreate(savedInstanceState);

		getWindow().takeSurface(null);
		setTheme(android.R.style.Theme_NoTitleBar_Fullscreen);
		getWindow().setFormat(PixelFormat.RGB_565);

		mUnityPlayer = new UnityPlayer(this);
		if (mUnityPlayer.getSettings ().getBoolean ("hide_status_bar", true))
			getWindow ().setFlags (WindowManager.LayoutParams.FLAG_FULLSCREEN,
			                       WindowManager.LayoutParams.FLAG_FULLSCREEN);

		setContentView(mUnityPlayer);
		mUnityPlayer.requestFocus();
		
		//保存一下游戏屏幕的宽和高
		DisplayMetrics dm = getResources().getDisplayMetrics();
		width = dm.widthPixels;
		height = dm.heightPixels;
		
	}

还是在这个类里。

	// This ensures the layout will be correct.
	@Override public void onConfigurationChanged(Configuration newConfig)
	{
		super.onConfigurationChanged(newConfig);
		//在这里强制设置一下屏幕的宽和高
		mUnityPlayer.getView().getLayoutParams().width = width;
		mUnityPlayer.getView().getLayoutParams().height = height;
		Log.v("unity", width+" " + height );
		
		mUnityPlayer.configurationChanged(newConfig);
		
	}

好了,目前我用这个方法已经完美解决了这个问题,大家如果还有什么好得经验分享,欢迎在下面给我留言。

 

最后编辑:
作者:雨松MOMO
专注移动互联网,Unity3D游戏开发
捐 赠写博客不易,如果您想请我喝一杯星巴克的话?就进来看吧!

Unity3D研究院之使用Android的硬件缩放技术优化执行效率》有 53 条评论

  1. ron说:

    Unity自带VR上这代码就不管用了,Screen.SetResolution

  2. 谷歌出了GLSurfaceView.java的源码 不知道对你有用没

  3. 丛虫说:

    momo大神,我用这个这个设置,但是我用NGUI做的点击空白区域关闭界面就关不了。我是在空白区域加了个碰撞盒实现的。

  4. Gem说:

    松哥,我执行 mUnityPlayer = new UnityPlayer(this)就直接闪退了.. 怎么破呢…还有. 我是想获取手机虚拟键盘的高度.. 能不能指点下呢..

  5. 溜溜说:

    这种情况,有什么必先的方法没有

  6. SevenH说:

    雨松大,我尝试在UnityPlayerNativeActivity.java中利用反射调用mUnityPlayer.setScreenSize()方法,成功设置了surfaceview的分辨率,不会闪,但是问题是触摸范围却出问题了,1080p的自己,切换到640,整个触摸范围被缩小到左上角一小块

  7. **标**说:

    高手,请问怎么知道是调用unity自己的GLSurfaceView?有这个类不同的import吗?知道这个GLSurfaceView 类大概在哪里吗?

  8. 张洁勇说:

    请问在JAVA层修改的话,是不是在使用Android插件的情况下需要修改?

  9. Rico说:

    再请问下,效率提升大概能有多少呢?

  10. 小宝说:

    很牛逼的样子。以后可能会用上。关注!!

  11. Rico说:

    直接用下面那段安卓代码就可以了吧

  12. Rico说:

    UI闪烁那个也解决了吗?

  13. 难道界面没有被拉伸么? 我试的时候 界面全被拉伸了啊 很虚的

  14. 杨子龙说:

    momo 我在游戏刚启动后也就是Unity的图标消失后void Start () { int rew = Screen.currentResolution.width; int reh = Screen.currentResolution.height; Screen.SetResolution(rew / (reh / 640), 640, true);}添加的这样的代码,运行后发现显示会有问题,目标分辨率是 1920 * 1080小米3,这是怎么回事呢

  15. 杨子龙说:

    原来是算错了,晕死

  16. 杨子龙说:

    momo 我在游戏刚启动后也就是Unity的图标消失后void Start () { int rew = Screen.currentResolution.width; int reh = Screen.currentResolution.height; Screen.SetResolution(rew / (reh / 640), 640, true); }添加的这样的代码,运行后发现显示会有问题,目标分辨率是 1920 * 1080小米3,这是怎么回事呢

  17. itian说:

    强制设置Screen.SetResolution(960, 640, true)后 实际屏幕比它大或小了 会是什么情况? 全屏自动拉伸填充满整个屏幕?

  18. ybt_yoyo说:

    960*640 等比缩放后,1140*760 或者 1280 * 854,明显针对1280*720分辨率的只能用1140*760,这么说的话要上下留黑边。理解正确吗? 文中所指的算法是什么?

    • 开发的时候布的界面是 960X640 ,此时运行在 1280X720上。 那么 修改后的分辨率 就是 Screen.SetResolution(1138,640,true);1138 = 1280/(720/640)但是你还要去考虑 比 960X640 大的情况 或者 比 960X640小的情况。不会有黑边的, 因为你改的是分辨率。。并不是显示区域。。。

留下一个回复

你的email不会被公开。 必填项已用 * 标注。