有帮助的帖子:
http://www.cnblogs.com/LuoYer/archive/2011/01/06/1929098.html
开发环境:Ubuntu + Eclipse
目标版本:Android 2.2
接触Android不久,前些日子打算弄个小游戏练练手,先在Google了一番,发现大家说用SurfaceView来弄游戏比较好,理由是绘制在独立的线程进行,不会因为主线程的阻塞而导致绘制更新的不及时,于是乎便用SurfaceView开始我的练手了
首先碰到的问题是SurfaceView的处理流程,比如按下BACK、HOME和POWER键后系统是如何反应的,因为游戏要在不同的情况下做出合理的响应,如退出、暂停等,总结如下:
- BACK:activity调用onPause()->onDestroy(),然后SurfaceView调用surfaceDestroyed(),再次启动应用程序,则应用重新启动
- HOME:activity调用onPause(),同时SurfaceView调用surfaceDestroyed();再次启动应用程序,activity调用onResume(),SurfaceView调用surfaceCreated()->surfaceChanged(),并且应用数据保留
- POWER:activity调用onPause();亮屏,activity调用onResume()
- 启动另外一个activity时:和熄屏的处理是相同的
游戏每桢的绘制是先背景后素材。经过测试发现,SurfaceHolder在16位的情况下,大部分机器(我手头的测试机,差不多各个档次的机器都有覆盖)满桢运行,但是只有魅族M9和华为U8800的显示效果可以接收,其他机器显示时背景的水波纹非常惨。然后就改用32位的SurfaceHolder,效果就好了很多,但是绘制效率就变的很惨,除了像GALAXY2这样当时的神机可以跑个50FPS,其他机器就非常抱歉了。那么有没有什么办法,既可以保证质量同时也保证效率呢?答案是肯定的
我发现绘制背景是非常耗时的,而提高绘制质量的初衷也是为了保证背景的效果,那么只要解决背景的绘制质量和效率,问题也就都解决了。SurfaceView的绘制是在一个独立线程中进行,绘制使用的Canvas不是窗口Canvas,这样我们把背景交给窗口去绘制,SurfaceView在每桢绘制时只要擦掉绘制区域,然后只绘制游戏素材就可以了,经实验发现这个办法是可行的,解决了SurfaceView每桢要重绘背景的问题。那么绘制质量如何呢?
//擦背景
canvas.drawColor(Color.TRANSPARENT,Mode.CLEAR);
如果针对不同的DPI,准备多套背景,绘制质量是可以保证的,但是如果我只想使用一套高清背景图适应所有分辨率呢?在默认情况下,分辨率不匹配的机器背景绘制质量会降低,我们知道系统在处理不同DPI资源时有做缩放,正是这个缩放处理使背景质量降低,而缩放的触发条件就是当前密度和目标密度是否一致,那么可以手动设置目标密度来避免系统进行缩放,见代码:
private Bitmap decodeResource(Resources resources, int id) {
TypedValue value = new TypedValue();
resources.openRawResource(id, value);
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inTargetDensity = value.density;
return BitmapFactory.decodeResource(resources, id, opts);
}
拿到了没有缩放过的资源,接着自己进行缩放:
private Bitmap createScaledBitmap(Bitmap src, int dstWidth, int dstHeight) {
Rect dst = new Rect(0, 0, dstWidth, dstHeight);
if (mBgScale == null) {
mBgScale = Bitmap.createBitmap(dstWidth, dstHeight,
Config.ARGB_8888);
}
if (mBgCanvas == null) {
mBgCanvas = new Canvas(mBgScale);
// 这里的抗锯齿是必须的,否则质量就缩水了
mBgCanvas.setDrawFilter(sDrawFilter);
}
mBgCanvas.drawBitmap(src, null, dst, null);
return mBgScale;
}
至此绘制的效率和质量都得到了保证
另外可能大家会问怎么保证背景的绘制一定是在SurfaceView的Canvas下面呢,具体我没有研究过源码,不清楚其Canvas的实现机制,这里系统提供了一个接口来让SurfaceView的Canvas处于Z序的顶层,我自己猜测Canvas大概就像minigui的窗口吧,每个Canvas提供了一个独立的绘制buf,而View系统就是在每个窗口的绘制Buf上搭建起来的一套绘制流程,然后不同的绘制层可以叠加。当然,只是猜测啊
setZOrderOnTop(true);
经过这次练手,觉得用SurfaceView做游戏还是比较麻烦的,效果也不好,且不具移植性。硬件渲染才是王道,找个跨平台的游戏引擎来弄,即保证了移植速度,也保证了游戏质量。4.0以后的Android允许使用硬件渲染View系统,效果还是十分明显的,但从移植性来说,我还是倾向于第三方引擎