20190108

作者: 糖_分 | 来源:发表于2019-01-08 17:41 被阅读0次

    看看这本书 -《Android游戏编程之从零开始》

    Day_1 2019/01/08


    开发之前需要熟悉

    · View(视图)
    · Canvas(画布)
    · Paint(画笔)
    · 刷屏的概念:对画布刷新来实现动态效果

    视图的含义

    · View:显示视图,内置画布,提供图形绘制函数、触屏事件、按键事件函数等
    · SurfaceView: 基于View视图进行拓展的试图类,更适用于2D游戏开发
    · GLSurfaceView:基于SurfaceView视图再次进行拓展的试图类,专用于3D游戏开发的视图

    游戏框架:

    1.View游戏框架
    2.SurfaceView游戏框架


    手机XY坐标

    XY坐标.png

    动态效果的实现方式

    1. 不断的绘制新的画布
    2. 使用一张画布,通过刷屏来让这张画布恢复到初始空白画布的状态,然后再向画布上进行绘制

    View框架

    public class MyView extends View
    {
        
        private int textX = 20;
        private int textY = 20;
        
        /**
         * 重新父类构造函数
         */
        public MyView( Context context )
        {
            super( context );
            setFocusable( true );
        }
        
        /**
         * 重写父类绘图函数
         * 只执行一遍
         */
        @Override
        protected void onDraw( Canvas canvas )
        {
            //画笔
            Paint paint = new Paint();
            //设置颜色
            paint.setColor( Color.BLACK );
            //绘制文本
            canvas.drawText( "Game" , textX , textY , paint );
            super.onDraw( canvas );
        }
        
        /**
         * 重新父类按键按下事件函数
         * keyCode:用户点击的按键
         * event:按键的动作事件队列
         */
        @Override
        public boolean onKeyDown(
                int keyCode ,
                KeyEvent event )
        {
            if( keyCode == KeyEvent.KEYCODE_DPAD_UP )
            {
                //"上"按键被点击
                textY -= 2;
            }
            else if( keyCode == KeyEvent.KEYCODE_DPAD_DOWN )
            {
                //"下"按键被点击
                textY += 2;
            }
            else if( keyCode == KeyEvent.KEYCODE_DPAD_LEFT )
            {
                //"左"按键被点击
                textX -= 2;
            }
            else if( keyCode == KeyEvent.KEYCODE_DPAD_RIGHT )
            {
                //"右"按键被点击
                textX += 2;
            }
            
            //不能在当前线程中循环调用(子线程)
            invalidate();
            //可以在子线程中循环调用使用
            //postInvalidate();
            return super.onKeyDown( keyCode , event );
        }
        
        /**
         * 重新父类按键抬起事件函数
         */
        @Override
        public boolean onKeyUp(
                int keyCode ,
                KeyEvent event )
        {
            return super.onKeyUp( keyCode , event );
        }
        
        /**
         * 重新触屏事件函数
         */
        @Override
        public boolean onTouchEvent( MotionEvent event )
        {
            //int x = ( int )event.getX();
            //int y = ( int )event.getY();
            //if( event.getAction() == MotionEvent.ACTION_DOWN )
            //{
            //  //按下手指
            //  textX = x;
            //  textY = y;
            //}
            //else if( event.getAction() == MotionEvent.ACTION_MOVE )
            //{
            //  //屏幕移动
            //  textX = x;
            //  textY = y;
            //}
            //else if( event.getAction() == MotionEvent.ACTION_UP )
            //{
            //  //抬起手指
            //  textX = x;
            //  textY = y;
            //}
            //invalidate();
            //postInvalidate();
            
            textX = ( int )event.getX();
            textY = ( int )event.getY();
            invalidate();
            //postInvalidate();
            return true;
        }
    }
    

    SurfaceView框架

    public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback
    {
        private int textX = 10;
        private int textY = 10;
        
        //用于控制SurfaceView
        private SurfaceHolder holder;
        private Paint paint;
        
        public MySurfaceView( Context context )
        {
            super( context );
            holder = this.getHolder();
            //添加监听状态
            holder.addCallback( this );
            paint = new Paint();
            paint.setColor( Color.WHITE );
        }
        
        //SurfaceHolder.Callback接口回调===================================
        
        /**
         * 当SurfaceView被创建完成后响应的函数
         */
        @Override
        public void surfaceCreated( SurfaceHolder surfaceHolder )
        {
            myDraw();
        }
        
        /**
         * 当SurfaceView状态发生改变时想要的函数
         */
        @Override
        public void surfaceChanged(
                SurfaceHolder surfaceHolder ,
                int i ,
                int i1 ,
                int i2 )
        {
            
        }
        
        /**
         * 当SurfaceView状态被摧毁时响应的函数
         */
        @Override
        public void surfaceDestroyed( SurfaceHolder surfaceHolder )
        {
            
        }
        //SurfaceHolder.Callback接口回调===================================
        
        /**
         * 自定义绘图函数
         * 不能使用onDraw来绘图
         */
        public void myDraw()
        {
            //加锁,防止绘制过程中被修改、摧毁
            //获取的都是同一张画布,多次调用时会出现绘之过的图形
            Canvas canvas = holder.lockCanvas();
            //刷屏方式1 绘制一个等同屏幕大小的图形覆盖在画布上
            canvas.drawRect( 0 , 0 , this.getWidth() , this.getHeight() , paint );
            //刷屏方式2 在画布上填充一种颜色
            canvas.drawColor( Color.BLACK );
            //刷屏方式3 指定RGB来填充
            canvas.drawRGB( 0 , 0 , 0 );
            //刷屏方式4 绘制一张等同于屏幕大小的图片覆盖在画布上-绘制背景图
            canvas.drawText( "Game" , textX , textY , paint );
            holder.unlockCanvasAndPost( canvas );
        }
        
        /**
         * 触屏监听
         */
        @Override
        public boolean onTouchEvent( MotionEvent event )
        {
            textX = ( int )event.getX();
            textY = ( int )event.getY();
            myDraw();
            //使用return super.onTouchEvent( event );会监听不到MOVE事件
            return true;
        }
    }
    

    在游戏中,基本不会等用户每次触发了按键事件、触屏事件才重新绘制画布,而是会固定一个时间去刷新画布。所以在游戏中,都会有一个线程不停的去重绘画布,实时的更新游戏元素的状态

    · 点击“BACK”按钮使当前程序切入后台,然后单击项目回到程序中,SurfaceView的状态变化为:
    surfaceDestroy->构造函数->surfaceCreated->surfaceChanged

    · 然后单击“HOME”按钮使当前程序切入后台,单击项目重新回到程序中,SurfaceView的状态变化为:
    surfaceDestroy->surfaceCreated->surfaceChanged

    注意:千万不要把线程的初始化放在surfaceCreated视图创建函数之前,而线程的启动却放在surfaceCreated视图创建的函数中,否则程序一旦被玩家点击“HOME”按钮后再返回到游戏时,会抛出异常。

    public class MySurfaceView extends SurfaceView
            implements SurfaceHolder.Callback, Runnable
    {
        //用于控制SurfaceView
        private SurfaceHolder holder;
        private Paint paint;
        
        private int textX = 10;
        private int textY = 10;
        
        private Thread thread;
        //线程消亡的标识位
        private boolean flag;
        private Canvas canvas;
        //屏幕宽高
        private int screenW;
        private int screenH;
        
        public MySurfaceView( Context context )
        {
            super( context );
            holder = this.getHolder();
            //添加监听状态
            holder.addCallback( this );
            paint = new Paint();
            paint.setColor( Color.WHITE );
            setFocusable( true );
        }
        
        //SurfaceHolder.Callback接口回调===================================
        
        /**
         * 当SurfaceView被创建完成后响应的函数
         */
        @Override
        public void surfaceCreated( SurfaceHolder surfaceHolder )
        {
            //获取宽高一定要在视图创建之后才能获取到,在此之前获取到的都是0
            screenW = this.getWidth();
            screenH = this.getHeight();
            
            flag = true;
            thread = new Thread( this );
            thread.start();
        }
        
        /**
         * 当SurfaceView状态发生改变时想要的函数
         */
        @Override
        public void surfaceChanged(
                SurfaceHolder surfaceHolder ,
                int i ,
                int i1 ,
                int i2 )
        {
            
        }
        
        /**
         * 当SurfaceView状态被摧毁时响应的函数
         */
        @Override
        public void surfaceDestroyed( SurfaceHolder surfaceHolder )
        {
            flag = false;
        }
        //SurfaceHolder.Callback接口回调===================================
        
        /**
         * 自定义绘图函数
         * 不能使用onDraw来绘图
         */
        public void myDraw()
        {
            //加锁,防止绘制过程中被修改、摧毁
            //获取的都是同一张画布,多次调用时会出现绘之过的图形
            Canvas canvas = holder.lockCanvas();
            //刷屏方式1 绘制一个等同屏幕大小的图形覆盖在画布上
            canvas.drawRect( 0 , 0 , this.getWidth() , this.getHeight() , paint );
            //刷屏方式2 在画布上填充一种颜色
            canvas.drawColor( Color.BLACK );
            //刷屏方式3 指定RGB来填充
            canvas.drawRGB( 0 , 0 , 0 );
            //刷屏方式4 绘制一张等同于屏幕大小的图片覆盖在画布上-绘制背景图
            canvas.drawText( "Game" , textX , textY , paint );
            holder.unlockCanvasAndPost( canvas );
        }
        
        /**
         * 自定义绘图函数2
         * 不能使用onDraw来绘图
         */
        public void myDraw2()
        {
            /**
             * 使用try的原因:
             * 当SurfaceView不可编辑或尚未创建时
             * holder.lockCanvas();有可能会返回null
             */
            
            try
            {
                canvas = holder.lockCanvas();
                if( canvas != null )
                {
                    //刷屏
                    canvas.drawRGB( 0 , 0 , 0 );
                    canvas.drawText( "Game" , textX , textY , paint );
                }
            }
            catch( Exception e )
            {
                //TODO:handle exception
            }
            finally
            {
                //保证在出错的情况下也能执行解锁画布
                if( canvas != null )
                {
                    holder.unlockCanvasAndPost( canvas );
                }
            }
        }
        
        /**
         * 触屏监听
         */
        @Override
        public boolean onTouchEvent( MotionEvent event )
        {
            textX = ( int )event.getX();
            textY = ( int )event.getY();
            //myDraw();
            //使用return super.onTouchEvent( event );会监听不到MOVE事件return true;
        }
        
        /**
         * 按键事件监听
         */
        @Override
        public boolean onKeyDown(
                int keyCode ,
                KeyEvent event )
        {
            return super.onKeyDown( keyCode , event );
        }
        
        /**
         * 游戏逻辑
         */
        private void logic()
        {
            
        }
        
        @Override
        public void run()
        {
            while( flag )
            {
                long start = System.currentTimeMillis();
                myDraw2();
                logic();
                long end = System.currentTimeMillis();
                //刷帧时间保持一致
                //一般游戏中刷新时间在50-100毫秒之间,也就是10-20帧左右
                try
                {
                    if( end - start < 50 )
                    {
                        Thread.sleep( 50 - ( end - start ) );
                    }
                }
                catch( InterruptedException e )
                {
                    e.printStackTrace();
                }
            }
        }
    }
    
    

    View和SurfaceView的区别

    1. 更新画布
    View:

    是由系统主UI进行更新

    postInvalidate()
    invalidate()
    

    有可能会出现UI线程被绘制函数阻塞而带来的一系列无法响应的问题

    SurfaceView

    单独线程去执行,不会出现因为UI线程阻塞而导致的无法响应按键、触屏等问题

    2.视图机制

    View:

    没有双缓冲机制

    SurfaceView:

    有双缓冲机制,更适合的游戏开发的试图类

    View和SurfaceView的选择

    游戏画面的更新属于被动更新:依赖按键与触屏事件 - 使用View视图开发
    例子:棋牌类游戏

    很多元素都是动态的,需要不断的重绘元素状态 - 使用SurfaceView视图开发
    例子:RPG、飞行射击类游戏

    GitHub项目
    156/406

    相关文章

      网友评论

          本文标题:20190108

          本文链接:https://www.haomeiwen.com/subject/wrqvrqtx.html