Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1251629
  • 博文数量: 247
  • 博客积分: 5587
  • 博客等级: 大校
  • 技术积分: 2060
  • 用 户 组: 普通用户
  • 注册时间: 2010-02-24 13:27
文章分类
文章存档

2012年(101)

2011年(44)

2010年(102)

分类: 嵌入式

2012-09-14 15:49:48

SurfaceView双缓冲,主动刷新,线程调用,GL等特点更适合开发画面刷新比较快的游戏。即时类游戏

View适合开发一些被动更新的游戏。如棋盘类,益智类的,有人触发游戏画面在更新的游戏。

 

-----------------------------------------------------------------------------------------------------------------------------------

 

下面是是我学习中看到的两位网友博客中的讲解原文如下:

 

 

       在Android游戏当中充当主要的除了控制类外就是显示类,在J2ME中我们用Display和Canvas来实现这些,而Google Android中涉及到显示的为view类,Android游戏开发中比较重要和复杂的就是显示和游戏逻辑的处理。 

 

       这里我们说下android.view.View和android.view.SurfaceView。SurfaceView是从View基类中派生出来 的显示类,直接子类有GLSurfaceView和VideoView,可以看出GL和视频播放以及Camera摄像头一般均使用 SurfaceView,到底有哪些优势呢? SurfaceView可以控制表面的格式,比如大小,显示在屏幕中的位置,最关键是的提供了SurfaceHolder类,使用getHolder方法 获取,相关的有Canvas lockCanvas() 
Canvas lockCanvas(Rect dirty) 、void removeCallback(SurfaceHolder.Callback callback)、void unlockCanvasAndPost(Canvas canvas) 控制图形以及绘制,而在SurfaceHolder.Callback 接口回调中可以通过重写下面方法实现。 

使用的SurfaceView的时候,一般情况下要对其进行创建,销毁,改变时的情况进行监视,这就要用到 SurfaceHolder.Callback. 

 

Java代码  收藏代码
  1. class XxxView extends SurfaceView implements SurfaceHolder.Callback {   
  2.   
  3. public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){}   
  4. //看其名知其义,在surface的大小发生改变时激发   
  5. public void surfaceCreated(SurfaceHolder holder){}   
  6. //同上,在创建时激发,一般在这里调用画图的线程。   
  7. public void surfaceDestroyed(SurfaceHolder holder) {}   
  8. //同上,销毁时激发,一般在这里将画图的线程停止、释放。   
  9.   
  10. }   
 



      对于Surface相关的,Android底层还提供了GPU加速功能,所以一般实时性很强的应用中主要使用SurfaceView而不是直接从View构建,同时后来做android 3d OpenGL中的GLSurfaceView也是从该类实现 。 


     SurfaceView和View最本质的区别在于,surfaceView是在一个新起的单独线程中可以重新绘制画面而View必须在UI的主线程中更新画面。  


    那么在UI的主线程中更新画面 可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞。那么将无法响应按键,触屏等消息。 


    当 使用surfaceView 由于是在新的线程中更新画面所以不会阻塞你的UI主线程。但这也带来了另外一个问题,就是事件同步。比如你触屏了一下,你需要surfaceView中 thread处理,一般就需要有一个event queue的设计来保存touch event,这会稍稍复杂一点,因为涉及到线程同步。 

所以基于以上,根据游戏特点,一般分成两类。 

1 被动更新画面的。比如棋类,这种用view就好了。因为画面的更新是依赖于 onTouch 来更新,可以直接使用 invalidate。 因为这种情况下,这一次Touch和下一次的Touch需要的时间比较长些,不会产生影响。 

2 主动更新。比如一个人在一直跑动。这就需要一个单独的thread不停的重绘人的状态,避免阻塞main UI thread。所以显然view不合适,需要surfaceView来控制。

 

3.Android中的SurfaceView类就是双缓冲机制。因此,开发游戏时尽量使用SurfaceView而不要使用View,这样的话效率较高,而且SurfaceView的功能也更加完善。

 

 考虑以上几点,所以我一直都选用 SurfaceView 来进行游戏开发。

那么在以后源码实例中,我都会以继承surfaceView框架来进行演示。下一章将详细剖析sarfaceview ,以及附上本人写的游戏开发架构

 

原文地址: http://blog.csdn.net/xiaominghimi/archive/2010/12/21/6089594.aspx

 

----------------------------------------------------------------------------------------------------------------------------------

 

1.view


view在api中的结构

java.lang.Object

android.view.View

 

直接子类:

AnalogClock, ImageView, KeyboardView, ProgressBar, SurfaceView, TextVie, ViewGroup, ViewStub

 

间接子类:

AbsListView, AbsSeekBar, AbsSpinner, AbsoluteLayout, AdapterView, AppWidgetHostView, AutoCompleteTextView, Button, CheckBox, CheckedTextView, Chronometer, CompoundButton, DatePicker, DialerFilter, DigitalClock,EditView, ExpandableListView, ExtractEditText, FrameLayout, GLSurfaceView, Gallery, GestureOverlayView, GridView, HorizontalScrollView, ImageButton, ImageSwitcher, LinearLayout, ListView, MediaController, MultiAutoCompleteTextView, QuickContactBadge, RadioButton, RadioGroup, RatingBar, RelativeLayout, ScrollView, SeekBar, SlidingDrawer, Spinner, TabHost, TabWidget, TableLayout, TableRow, TextSwitcher, TimePicker, ToggleButton, TwoLineListItem, VideoView, ViewAnimator, ViewFlipper, ViewSwitcher, WebView, ZoomButton, ZoomControls


         由此可见View类属于Android开发绘制中的显示老大,任何与绘制有关系的控件都是它的子类。在这篇文章中我主要讲View 与SurFaceView 使用线程刷新屏幕绘制方面的知识。开发中如何去选择使用View还是SurFaceView。我相信读过我前几篇博客的朋友应该知道我在刷新屏幕的时候使 用invalidate()方法来重绘,下面我详细的说明一下Andooid刷新屏幕的几种方法。



          第一种: 在onDraw方法最后调用invalidate()方法,它会通知UI线程重绘 这样 View会重新调用onDraw方法,实现刷新屏幕。 这样写看起来代码非常简洁漂亮,但是它也同时存在一个很大的问题,它和游戏主线程是分开的 它违背了单线程模式,这样操作绘制的话是很不安全的,举个例子 比如程序先进在Activity1中 使用invalidate()方法来重绘, 然后我跳到了Activity2这时候Activity1已经finash()掉 可是Activity1中 的invalidate() 的线程还在程序中,Android的虚拟机不可能主动杀死正在运行中的线程所以这样操作是非常危险的。因为它是在UI线程中被动掉用的所以很不安全。

 

invalidate()  更新整个屏幕区域

invalidate(Rect rect) 更新Rect区域

invalidate(l, t, r, b) 更新指定矩形区域

 

Java代码  收藏代码
  1. public void onDraw(Canvas canvas){      
  2.         DosomeThing();      
  3.         invalidate();      
  4.      

 

        第二种:使用postInvalidate();方法来刷新屏幕 ,调用后它会用handler通知UI线程重绘屏幕,我们可以 new  Thread(this).start(); 开启一个游戏的主线程 然后在主线程中通过调用postInvalidate();方法来刷新屏幕。postInvalidate();方法 调用后 系统会帮我们调用onDraw方法 ,它是在我们自己的线程中调用 通过调用它可以通知UI线程刷新屏幕 。由此可见它是主动调用UI线程的。所以建议使用postInvalidate()方法通知UI线程来刷新整个屏幕。

Java代码  收藏代码
  1. @Override    
  2.     public void run() {    
  3.         while (mIsRunning) {    
  4.         try {    
  5.             Thread.sleep(100);    
  6.                        postInvalidate();    
  7.         } catch (InterruptedException e) {    
  8.            // TODO Auto-generated catch block    
  9.             e.printStackTrace();    
  10.        }    
  11.        }    
  12.    }    
 

View中用到的双缓冲技术


        重绘的原理是 程序根据时间来刷新屏幕 如果有一帧图形还没有完全绘制结束 程序就开始刷新屏幕这样就会造成瞬间屏幕闪烁 画面很不美观,所以双缓冲的技术就诞生了。它存在的目的就是解决屏幕闪烁的问题,下面我说说在自定义View中如何实现双缓冲。

首先我们需要创建一张屏幕大小的缓冲图片,我说一下第三个参数 ARGB 分别代表的是 透明度   红色   绿色     蓝色

Bitmap.Config  ARGB_4444              ARGB  分别占四位 
Bitmap.Config  ARGB_8888              ARGB  分别占八位
Bitmap.Config  RGB_565                没有透明度(A)   R占5位   G 占6位   B占5位   

一般情况下我们使用ARGB_8888 因为它的效果是最好了 当然它也是最占内存的。

 

Java代码  收藏代码
  1. mBufferBitmap = Bitmap.createBitmap(mScreenWidth,mScreenHeight,Config.ARGB_8888);    
 

创建一个缓冲的画布,将内容绘制在缓冲区mBufferBitmap中

 

 

Java代码  收藏代码
  1. Canvas mCanvas = new Canvas();    
  2. mCanvas.setBitmap(mBufferBitmap);    
 

最后一次性的把缓冲区mBufferBitmap绘制在屏幕上,怎么样 简单吧 呵呵。

 

 

 

Java代码  收藏代码
  1. @Override    
  2. protected void onDraw(Canvas canvas) {    
  3.      /**这里先把所有须要绘制的资源绘制到mBufferBitmap上**/    
  4.      /**绘制地图**/    
  5.      DrawMap(mCanvas,mPaint,mBitmap);    
  6.      /**绘制动画**/    
  7.      RenderAnimation(mCanvas);    
  8.      /**更新动画**/    
  9.      UpdateAnimation();    
  10.         
  11.         
  12.     if(isBorderCollision) {    
  13.      DrawCollision(mCanvas,"与边界发生碰撞");    
  14.     }    
  15.         
  16.     if(isAcotrCollision) {    
  17.     DrawCollision(mCanvas,"与实体层发生碰撞");    
  18.     }    
  19.    if(isPersonCollision) {    
  20.     DrawCollision(mCanvas,"与NPC发生碰撞");    
  21.    }    
  22.         
  23.     /**最后通过canvas一次性的把mBufferBitmap绘制到屏幕上**/    
  24.     canvas.drawBitmap(mBufferBitmap, 0,0, mPaint);    
  25.     super.onDraw(canvas);    
  26. }    
 

 由此可见view属于被动刷新, 因为我们做的任何刷新的操作实际上都是通知UI线程去刷新。所以在做一些只有通过玩家操作以后才会刷新屏幕的游戏 并非自动刷新的游戏 可以使用view来操作。



2.SurfaceView


        从API中可以看出SurfaceView属于View的子类 它是专门为制作游戏而产生的,它的功能非常强大,最重要的是它支持OpenGL ES库,2D和3D的效果都可以实现。创建SurfaceView的时候需要实现SurfaceHolder.Callback接口,它可以用来监听 SurfaceView的状态,SurfaceView的改变 SurfaceView的创建 SurfaceView 销毁  我们可以在相应的方法中做一些比如初始化的操作 或者 清空的操作等等。

       使用SurfaceView构建游戏框架它的绘制原理是绘制前先锁定画布 然后等都绘制结束以后 在对画布进行解锁 最后在把画布内容显示到屏幕上。

          


代码中是如何实现SurfaceView


首先需要实现 Callback 接口 与Runnable接口

 

Java代码  收藏代码
  1. public class AnimView extends SurfaceView implements Callback,Runnable  
 

获取当前mSurfaceHolder 并且把它加到CallBack回调函数中

 

Java代码  收藏代码
  1. SurfaceHolder  mSurfaceHolder = getHolder();    
  2.   mSurfaceHolder.addCallback(this);    
 

      通过callBack接口监听SurfaceView的状态, 在它被创建的时候开启游戏的主线程,结束的时候销毁。这里说一下在View的构造函数中是拿不到view有关的任何信息的,因为它还没有构建好。 所以通过这个监听我们可以在surfaceCreated()中拿到当前view的属性 比如view的宽高 等等,所以callBack接口还是非常有用处的。

Java代码  收藏代码
  1. @Override  
  2. public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2,  
  3.     int arg3) {  
  4.     // surfaceView的大小发生改变的时候  
  5.       
  6. }  
  7.   
  8. @Override  
  9. public void surfaceCreated(SurfaceHolder arg0) {  
  10.     /**启动游戏主线程**/  
  11.     mIsRunning = true;  
  12.     mThread = new Thread(this);  
  13.     mThread.start();  
  14. }  
  15.   
  16. @Override  
  17. public void surfaceDestroyed(SurfaceHolder arg0) {  
  18.  // surfaceView销毁的时候  
  19.     mIsRunning = false;  
  20. }  
 

      在游戏主线程循环中在绘制开始 先拿到画布canvas 并使用mSurfaceHolder.lockCanvas()锁定画布,等绘制结束以后 使用mSurfaceHolder.unlockCanvasAndPost(mCanvas)解锁画布,  解锁画布以后画布上的内容才会显示到屏幕上。

 

Java代码  收藏代码
  1. try {  
  2.                 long startTime=System.currentTimeMillis();  
  3.                 canvas=sfh.lockCanvas();  
  4.                 logic();  
  5.                 draw(canvas, paint);  
  6.                 long endTime=System.currentTimeMillis();  
  7.                 long useTime=endTime-startTime;  
  8.                 //固定屏幕刷新的时间为50ms  
  9.                 if(useTime<50){  
  10.                     Thread.sleep(50-useTime);  
  11.                 }  
  12.             } catch (Exception e) {  
  13.                 Log.e("Error""刷屏线程出错了"+e);  
  14.             }finally{  
  15.                 if(canvas!=null){  
  16.                     sfh.unlockCanvasAndPost(canvas);  
  17.                 }  
  18.             }  
 

 

     由此可见SurfaceView 属于主动刷新 ,重绘过程完全是在我们自己的线程中完成 , 由于游戏中肯定会执行各种绚丽的动画效果如果使用被动刷新的View就有可能就会阻塞UI线程,所以SurfaceView 更适合做游戏。

阅读(4133) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~