使用SurfaceView开发遇到的一些问题

有帮助的帖子:

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系统,效果还是十分明显的,但从移植性来说,我还是倾向于第三方引擎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值