Android游戏开发之SurfaceView与游戏开发

Android中SurfaceView,是不使用游戏引擎,开发一款游戏的经常使用view控件。java

SurfaceView经常被用来显示那些更新速度比较快(这个速度一般人眼没法识别)的图像,经常被用来显示照相机的当前效果,视频的播放,游戏界面的播放。canvas

SurfaceView的建立:缓存

继承SurfaceView就可使用构造方法建立了。dom

所要重新写的方法也只不过一个构造方法,这里使用的构造经常有两种,一种使用只有一个参数的构造方法,参数为context,若是使用这样的构造方法,则建立SurfaceView的Activity不能加载layout文件夹中的xml文件,只能加载new出来的view如ide

public class GameMainActivity extends Activity {
    public static GameView Game;
    public static AssetManager assets;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        Assets.mContentResolver=getContentResolver();
       
        Game=new GameView(GameMainActivity.this);
        setContentView(Game);
      
       assets=getAssets();
    }
}

这里的GameView继承自SurfaceView

若是使用两个参数的构造方法:布局

Context context, AttributeSet attrs
则能够将本身View类当作Activity布局文件中的一个空间,当布局文件显示时会自动调用这个构造方法,第二参数用于返回相应的指令给View。

可是这里建议使用第一个方法构造,由于在游戏中的界面跳转与外部Activity的跳转逻辑彻底不一样,因此在布局文件中加载会有必定的弊端。可是好处是这样可使用Android封装好的控件(如button)。this


SurfaceView有一个特色:spa

他并非当Activity建立 就会能够调用的,里面的那些控件,并非真的控件,当Activity建立完成就可使用了,而是当SurfaceView建立,并出如今屏幕上以后才能够开始绘画(即伪控件的加载)。线程

这时就须要一个监听器监听SurfaceView的建立,改变和销毁。code

使用监听器

SurfaceHolder.Callback
这里要实现这个接口,重写其中的三个主要方法:

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) 
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) 
public void surfaceDestroyed(SurfaceHolder holder) 
这三个方法表明了SurfaceView生命周期,注意只有在这三个声明周期中才能够进行其中的操做,在Activity中的操做要与之区分。

当建立时自动调用createed和changed方法,在home键,back键返回时调用destroyed方法,可是注意home键返回再回来依次调用created方法,和changed方法,可是back键返回再回来就会调用构造方法和created方法,changed方法。
设置监听时要先初始化SurfaceView的管家类SurfaceHolder,经过在View中调用父类SurfaceView的方法getholder就能够简单的获取管家。
再使用holder.addCallBack()进行设置监听声明周期

SurfaceView的绘画,通常在线程中进行绘画操做。
绘画以前要获得View的主画布,经过holder来实现,这时体现了若是线程在View类中就能够很好地获得主画布,因此GameView一般能够实现Runnable接口。
Canvas的获得使用holder.lockCanvas()的返回值获得,获得主画布后就可使用canvas中的各类绘画方法进行绘画了。
若是想不断地更新,就要使用循环,也就是死循环进行一遍一遍地render和update进行绘画,与逻辑更新,这里就是游戏主循环。

SurfaceView的事件监听使用OnTouchListener和MotionEvent,这里因为不能使用Android的诸多控件,只能使用这种触控事件。诸多组件也是画上去的再经过坐标的对比,进行事件的响应。
这里因为游戏代码所涉及的类诸多,只展现一个Demo,一个画板的实现(没有适配不一样机型)
public class MyView extends SurfaceView implements Runnable,SurfaceHolder.Callback,View.OnTouchListener {
    Context main;
    SurfaceHolder holder;//主holder
    Canvas canvas;//主画布
    Paint p;
    Thread t;
    Bitmap buffer=null;//二级缓存
    Path mpath;//触控中的轨迹
    float startX;
    float startY;
    Rect window;
    Rect bufferRect;
    boolean run=false;

    public MyView(Context context) {
        super(context);
        init(context);
        setOnTouchListener(this);
    }

    private void init(Context context){
        holder=getHolder();
        holder.addCallback(this);//勿忘addcallback
        this.main=context;
        p=new Paint();
        p.setAntiAlias(true);//消除锯齿
        p.setStyle(Paint.Style.STROKE);//设置画笔风格
        p.setAlpha(255);//画笔的不透明度
        p.setStrokeWidth((float)2);//设置笔触宽度
        p.setColor(Color.WHITE);
        mpath=new Path();
        buffer= Bitmap.createBitmap(1440,2256, Bitmap.Config.ARGB_8888);//很重要建立二级缓存的必要方法

        t=new Thread(this);

    }
    @Override
    public void run() {
    while(run){
        canvas=holder.lockCanvas();
        window=canvas.getClipBounds();
        Canvas c=new Canvas(buffer);
        c.drawRect(window,p);
        p.setColor(Color.rgb(new Random().nextInt(255),new Random().nextInt(255),new Random().nextInt(255)));
        c.drawPath(mpath,p);
        bufferRect=new Rect(0,0,buffer.getWidth(),buffer.getHeight());
        canvas.drawBitmap(buffer,bufferRect,window,new Paint());
        holder.unlockCanvasAndPost(canvas);
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();

        }
    }
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
     run=true;

        Log.i("Created", "surfaceCreated: ");
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        Log.i("Changed", "surfaceChanged: ");
         t=new Thread(this);
         t.start();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        run=false;
        try {
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Log.i("Destroyed", "surfaceDestroyed: ");
    }
    private void onTouchDown(MotionEvent event){
        startX=event.getX();
        startY=event.getY();
        mpath.reset();
        mpath.moveTo(startX,startY);

    }
    private void onTouchMove(MotionEvent event){
        float touchX=event.getX();
        float touchY=event.getY();

        float dx=Math.abs(touchX-startX);//移动的距离

        float dy =Math.abs(touchY-startX);//移动的距离


        if(dx>3||dy>3){
            float cX=(touchX+startX)/2;
            float cY=(touchY+startY)/2;
            mpath.quadTo(startX, startY, cX, cY);//绘制贝塞尔曲线

            startX=touchX;
            startY=touchY;//改变开始绘制的点
        }//这里巧妙地进行筛选过于短的移动
    }
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
          this.onTouchDown(event);
                break;
            case MotionEvent.ACTION_MOVE:
          this.onTouchMove(event);
                break;
            case MotionEvent.ACTION_UP:

                break;
        }
        return true;
    }

}