棋牌【读书笔记《Android游戏编程的从零开始》】11.游戏开基础(SurfaceView 游戏框架、View 和 SurfaceView 的分别)

如果一个班级只有出男胎会怎么样?

1. SurfaceView 游戏框架实例

动手?上课坐不鸣金收兵?物品摆放乱七八差?没有细腻之亲笔作品?

实例效果:就是屏幕上的文书跟着点击的地方倒,效果图如下:

可怜有或。

棋牌 1

男性胎的特质,容易发生攻击性,写作就是123,动手能力强,被动之放任的能力弱……

 

当时仿佛的篇章读了很多,所以才有人特意写《养育男孩》。男孩和女孩不同,生理结构所控制,荷尔蒙决定,那么还是男孩子的班怎么带。

步骤:

1.资足够的活动空间

新建项目“GameSurfaceView”,首先由定义一个近似”MySurfaceView”,此类继承SurfaceView,并促成android.view.SurfaceHolder.Callback 接口,代码如下

教室不足够充分,那就算多去以外,组建球队,踢球;足球班呼啦啦全达;天气晴好时,爬山。爬山他们最欣赏难爬的,手脚并为此,危险群,小呼小叫——小心骗局,但可乐在其中。这是独自出男性胎的意,没有哭,喊什么害怕,而是充满探险,乐在其中。

棋牌 2棋牌 3

教室里虽安排各种棋牌游戏,阴雨天,下棋时。还有折飞机的杀游戏,他们发明的,以及绘制各种迷宫。

package com.example.ex4_5;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;

//Callback接口用于SurfaceHolder 对SurfaceView 的状态进行监听
public class MySurfaceView extends SurfaceView implements Callback{
    //用于控制SurfaceView 的大小、格式等,并且主要用于监听SurfaceView 的状态
    private SurfaceHolder sfh;
    private Paint paint;
    private int textX=30,textY=30;
    public MySurfaceView(Context context) {
        super(context);
        //实例SurfaceView
        sfh = this.getHolder();
        //为SurfaceView添加状态监听
        sfh.addCallback(this);
        //实例一个画笔
        paint = new Paint();
        //设置字体大小
        paint.setTextSize(30);
        //设置画笔的颜色
        paint.setColor(Color.GREEN);
    }

    @Override
    //当SurfaceView 被创建完成后响应
    public void surfaceCreated(SurfaceHolder holder) {
        myDraw();
    }

    @Override
    //当SurfaceView 状态发生改变时响应
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {

    }

    @Override
    //当SurfaceView 状态被摧毁时响应
    public void surfaceDestroyed(SurfaceHolder holder) {

    }

    //SurfaceView 是通过SurfaceHolder 来修改其数据,所以即时重写View 的onDraw(Canvas canvas)函数,在SurfaceView 启动时也不会执行到,因此这里自定义绘图函数
    public void myDraw()
    {
        //获取SurfaceView 的Canvas 对象,
        //同时对获取的Canvas 画布进行加锁,防止SurfaceView 在绘制过程中被修改、摧毁等发生的状态改变
        //另外一个lockCanvas(Rect rect)函数,其中传入一个Rect矩形类的实例,用于得到一个自定义大小的画布
        Canvas canvas = sfh.lockCanvas();
        //填充背景色,即刷屏,每次在画布绘图前都对画布进行一次整体的覆盖
        canvas.drawColor(Color.BLACK);
        //绘制内容
        canvas.drawText("This is a Text !", textX, textY, paint);
        //解锁画布和提交
        sfh.unlockCanvasAndPost(canvas);
    }

    //重写触屏监听事件
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        textX = (int)event.getX();
        textY = (int)event.getY();
        myDraw();
        return super.onTouchEvent(event);
    }

}

棋牌 4

MySurfaceView

2.多动手

改MainActivity 类,让其出示自定义的SurfaceView 视图

男性胎喜欢有动手的事务,感谢学校出矣手工坊,锯、刀、挫,这是男孩子的福地。创意以及专注俱在。

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //显示自定义的SurfaceView 视图
    setContentView(new MySurfaceView(this));
}
}

男性胎要当之机智,他们喜爱动物,喜欢山野,那就引在也体贴植物,多几追问与探索。做当然笔记,做植物标本,将来又届动物标本。

配备文件被安应用程序为全屏

尚记儿子小时候会见蹲到地上看蚂蚁,一看片独钟头。我啊已好这样,所以便牵动在她们同去寻找。同时部署相关书籍,《常见植物》、《常见昆虫》,某一样龙《化石》肯定也会成为他们心爱之。

  android:theme="@android:style/Theme.NoTitleBar.Fullscreen" 

一旦有原则,乐高编程引入教室,更会是异常受男性胎辈的迎,这为是培训他们专注力的极致好的措施。

切实说明可查代码。

棋牌 5

 

棋牌 6

2.SurfaceView 视图添加线程

3.书的多元化

每当戏被,基本上不会见等于及用户每次触发了按键事件、触屏事件才去重绘画布,而是会固定一个年华错开刷新画布:比如游戏被的倒计时、动态的花卉、流水等等,这些游戏元素并无会见及玩家互动,但是这些元素还是动态的。所以戏支付被会生一个线程不鸣金收兵的去重绘画布,实时的翻新游戏元素的状态。
当游戏中除了画布给玩家最直白的动态展现外,也会时有发生好多逻辑需要不停的夺创新,比如怪物的AI(人工智能)、游戏受钱的创新等等。

男性胎喜爱探险,喜欢漫画,喜欢大。养育儿子的时段故意的多购大类图书,因为私心里不欣赏异学文,想把他引至理工的途中,所以买了汪洋宽广类图书。这样的做法也为是相符了男孩的秉性。

脚让点实例中之SurfaceView 视图添加线程,用于不停歇的重绘画布以及未鸣金收兵地实施打逻辑。

发端不久,果不其然,发现有点男孩等,翻看最多之凡本身带的这些科普书,不极端好看的固化(他是听觉型孩子,喜欢放故事)“什么是啊”却是一样如约属一依照地圈。“可怕的正确”系列也是他俩翻译看尽多的,《博物》杂志那是好让欢迎。“手斧男孩”不用推荐,他们本来就是会见招来了去。这些开以永无岛教室的时节几乎无人问津,在她们这里实在是产生矣用武之地。

实例效果如下:

假设起场地,那么下同样步,我们而筹建教室的实验角,试管烧杯酒精灯配共——一只要自己家的家中实验室。

棋牌 7

那么文学书籍怎么收拾?引导。三只月,从桥梁书到了《明朝那些事》、“遗产”三部曲,“哈利波特”更是受宠。阅读量非常了,文字吗即渐渐细腻起来了。

改后,MySurfaceView 类代码如下:

棋牌 8

棋牌 9棋牌 10

无处不在的开卷

package com.example.ex4_5;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;

//Callback接口用于SurfaceHolder 对SurfaceView 的状态进行监听
public class MySurfaceView extends SurfaceView implements Callback, Runnable {
    // 用于控制SurfaceView 的大小、格式等,并且主要用于监听SurfaceView 的状态
    private SurfaceHolder sfh;
    // 声明一个画笔
    private Paint paint;
    // 文本坐标
    private int textX = 30, textY = 30;
    // 声明一个线程
    private Thread th;
    // 线程消亡的标识符
    private boolean flag;
    // 声明一个画布
    private Canvas canvas;
    // 声明屏幕的宽高
    private int screenW, screenH;

    /**
     * SurfaceView 初始化函数
     * 
     * @param context
     */
    public MySurfaceView(Context context) {
        super(context);
        // 实例SurfaceView
        sfh = this.getHolder();
        // 为SurfaceView添加状态监听
        sfh.addCallback(this);
        // 实例一个画笔
        paint = new Paint();
        // 设置字体大小
        paint.setTextSize(20);
        // 设置画笔的颜色
        paint.setColor(Color.WHITE);
        // 设置焦点
        setFocusable(true);
    }

    /**
     * SurfaceView 视图创建,响应此函数
     */
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        screenW = this.getWidth();
        screenH = this.getHeight();
        flag = true;
        // 实例线程
        th = new Thread(this);
        // 启动线程
        th.start();
    }

    /**
     * SurfaceView 视图状态发生改变时,响应此函数
     */
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {

    }

    /**
     * SurfaceView 视图消亡时,响应此函数
     */
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        flag = false;
    }

    /**
     * 游戏绘图
     */
    public void myDraw() {
        try {
            canvas = sfh.lockCanvas();
            if (canvas != null) {
                // ————利用绘制矩形的方式刷屏
                // canvas.drawRect(0, 0, this.getWidth(), this.getHeight(),
                // paint);
                // ————利用填充画布,刷屏
                // canvas.drawColor(Color.BLACK);
                // ————利用填充画布指定的颜色分量,刷屏
                canvas.drawRGB(0, 0, 0);
                canvas.drawText("啦啦啦,德玛西亚!", textX, textY, paint);
            }
        } catch (Exception e) {
            // TODO: handle exception
        } finally {
            if (canvas != null) {
                sfh.unlockCanvasAndPost(canvas);
            }
        }
    }

    /**
     * 触屏事件监听
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        textX = (int) event.getX();
        textY = (int) event.getY();
        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();
            myDraw();
            logic();
            long end = System.currentTimeMillis();
            try {
                if (end - start < 50) {
                    Thread.sleep(50 - (end - start));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

4.开立一个更盛又耐心的条件

MySurfaceView

阳胎十分轻因不停歇,若是过分强调纪律坐姿,对他们而言往往是只损害。男孩听的瞩目大多不够,课堂上而欲他们注意,除了课程设计之外,重要之地方,提前提醒,让男女等的眸子还看在老师,然后再说效果会哼过多。

代码说明:

于器乐以及一些异学科的读书,进展缓慢的男孩子更要更多耐心。比如,T巴乌学习最好慢,因为模仿得款越发恐惧,就变得极度排斥。当您无是批评,而是简单示范,足够耐心等待,一所有整个练习,鼓励,他就从未了毛骨悚然。虽然放缓,花的辰特别丰富,但是慢慢会吹下来了,孩子就是不再排斥,现在呢克积极以出来练习了。

(1)线程标识位

对于再次易互相指责以及口诛笔伐的男孩子的话,爱攻击是天性,互相指责更多之是后天环境受到习得的如出一辙栽应本着模式,怕给处以,所以把事推到别人身上。一旦孩子辈了解这条件遭到,不是惩罚,学会从解决问题之角度出发考虑问题,那么互相推诿指责之景就会大大减少。谁还见面举行不是的,做错不吓人,承担相应自然结果,然后改正。大家互相帮助提醒,班级氛围形成,那么孩子吧就是见面改变自己的回复模式。

棋牌 11棋牌 12

X是独出接触攻击性的子女,因为安全感不够,他好打起厚厚的墙。四年级同学来说X跟人家起外号,当教师了解的时节,X就哭着喊,他们先说自家的,我从没说他俩,之后嗓门越来越大。这样的哭丧掩饰的凡担惊受怕,如果本身承认了,那么会不会见惨遭严厉的办。这时候老师的批评指责只见面加重他的这种模式,老师扮个心理医师,等他平静下来,表示晓,然后帮他一块分析,不同之答问后果是什么,以及如何是还好地解决方法。男胎辈当然就偏于理性思维,几不善下便见面习得重新理智的缓解问题的道。

在代码中“boolean flag;”语句声明一个布尔值,它主要用于以下两点:

①便于消亡线程

一个线程一旦启动,就会执行run() 函数,run() 函数执行结束后,线程也伴随着消亡。由于游戏开发中使用的线程一般都会在run() 函数中使用一个while 死循环,在这个循环中会调用绘图和逻辑函数,使得不断的刷新画布和更新逻辑;那么如果游戏暂停或者游戏结束时,为了便于销毁线程在此设置一个标识位来控制。

②防止重复创建线程及程序异常

为什么会重复创建线程,首先从Android 系统的手机说起。熟悉或者接触过 Android 系统的人都知道,Android 手机上一般都有“Back(返回)”与“Home(小房子)”按键。不管当前手机运行了什么程序,只要单击“Back”或者“Home”按键的时候,默认会将当前的程序切入到系统后台运行(程序中没有截获这两个按钮的前提下);也正因为如此,会造成MySurfaceView 视图的状态发生改变。

首先单击“Back” 按钮使当前程序切入后台,然后单击项目重新回到程序中,SurfaceView 的状态变化为:surfaceDestroyed -> 构造函数 -> surfaceCreated -> surfaceChanged 。
然后单击“Home” 按钮使当前程序切入后台,然后单击项目重新回到程序中,SurfaceView 的状态变化为:surfaceDestroyed -> surfaceCreated -> surfaceChanged 。
通过 SurfaceView 的状态变化可以明显看到,当点击“Back” 按钮并重新进入程序的过程要比点击“Home” 按钮多执行了一个构造函数。也就是说当点击“Back” 返回按键时,SurfaceView 视图会被重新加载 。
正因为这个原因,如果线程的初始化是在构造函数或者在构造函数之前,那么线程也要放在视图构造函数中进行。
千万不要把线程的初始化放在 surfaceCreated 视图创建函数之前,而线程的启动却放在 surfaceCreated 视图创建的函数中,否则程序一旦被玩家点击“Home”按键后再重新回到游戏时,程序会抛出异常。

异常是因为线程已经启动造成的,原因很简单,因为程序被“Home” 键切入后台再从后台恢复时,会直接进入 surfaceCreated 视图中创建函数,又执行了一遍线程启动!

能够想到的解决方法是,可以将线程的初始化和启动都放在视图的构造函数中,或者都放在视图创建的函数中。但是这里又出现新的问题,如果将线程的初始化和启动都放在视图的构造函数中,那么当程序被“Back”键切入后台再从后台恢复时,线程的数量会增多,反复多次,就会反复多出对应的线程。

那么,如果将flag这个线程标识位在视图摧毁时让其值改为false ,从而使当前这个线程的run 方法执行完毕,以达到摧毁线程的目的,但是如果点击“Home”键呢?当程序恢复的时候,程序就不执行线程了,也就是说重绘和逻辑函数都不再执行!
所以最完美的做法是,线程的初始化与线程的启动都写在视图的surfaceCreated 创建函数中,并且将线程标识位在视图摧毁时将其值改变为 false 。这样既可避免“线程已启动”的异常,还可以避免点击Back 按键无限增加线程数的问题。

小学阶段的男孩,当我们会欣赏她们之特质,给予重多耐心与赞助,创设一个再次多鼓励和更宽松的环境,你晤面意识他们身上更多创造性的火舌,是那么可爱。他们呢会尤其担当,阳刚的气弥漫,教室里便始终会发生勃勃生机。

Introductions

棋牌 13

(2)获取视图的富和高

自网友所拍(黄山冬日)

棋牌 14棋牌 15

在SurfaceView 视图中获取视图的宽和高的方法:
this.getWidth(); 获取视图宽度
this.getHeight(); 获取视图高度

在 SurfaceView 视图中获取视图的宽高,一定要在视图创建之后才可以获取到,也就是在 surfaceCreated 函数之后获取,在此函数执行之前获取到的永远是零,因为当前视图还没有创建,是没有宽高值的。

Introductions

(3)绘图函数 try 一下

棋牌 16棋牌 17

因为当 SurfaceView 不可编辑或尚未创建时,调用 lockCanvas() 函数会返回null; Canvas 进行绘图时也会出现不可预知的问题,所以要对绘制函数中进行 try...catch 处理;既然 lockCanvas() 函数有可能获取为 null, 那么为了避免其他使用 canvas 实例进行绘制的函数报错,在使用 Canvas 开始绘制时,需要对其进行判定是否为 null 。

Introductions

(4)提交画布必须放在 finally 中

棋牌 18棋牌 19

绘图的时候可能会出现不可预知的 Bug, 虽然使用 try 语句包起来了,不会导致程序崩溃;但是一旦在提交画布之前出错,那么解锁提交画布函数则无法被执行到,这样会导致下次通过 lockCanvas() 来获取 Canvas 时程序抛出异常,原因是因为画布上次没有解锁提交!所以画布将解锁提交的函数应放入 finally 语句块中。
还要注意,虽然这样保证了每次能正常提交解锁画布,但是提交解锁之前要保证画布不为空的前提,所以还需判断 Canvas 是否为空,这样一来就完美了。

Introductions

(5)刷帧时间尽量保持一致

棋牌 20棋牌 21

虽然在线程循环中,设置了休眠时间,但是这样并不完善!比如在当前项目中, run 的 while 循环中除了调用绘图函数还一直调用处理游戏逻辑的 logic() 函数,虽然在当前项目的逻辑函数中并没有写任何的代码,但是假设这个逻辑函数 logic()中写了几千行的逻辑,那么系统在处理逻辑时,时间的开销是否和上次的相同,这是无法预料的,但是可以尽可能地让其时间差值趋于相同。假设游戏线程的休眠时间为X毫秒,一般线程的休眠写法为:
Thread.sleep(X);

优化写法步骤如下:

步骤1 首先通过系统函数获取到一个时间戳:
long start = System.currentTimeMillis();
//在线程中的绘图、逻辑等函数

步骤2 处理以上所有函数之后,再次通过系统函数获取到一个时间戳:
long end = System.currentTimeMillis();

步骤3 通过这两个时间戳的差值,就可以知道这些函数所消耗的时间:如果(end - start) > X, 那线程就完全没有必要去休眠;如果(end - start) < X, 那线程的休眠时间应该为 X - (end - start) 。
线程休眠应更改为以下写法:
 if ((end - start) < X) {
 Thread.sleep(X - (end - start));
 }
 一般游戏中刷新时间在50~100毫秒之间,也就是每秒10~20帧左右;当然还要视具体情况和项目而定。

Introductions

 

方的MySurfaceView
类继承surfaceview类,并且用回调callback接口和线程runnable接口。那么这里大概的说生Callback接口和SurfaceHolder
类的意图;

callback接口:

如继续SurfaceView类并落实SurfaceHolder.Callback接口就好实现一个自定义的SurfaceView了,SurfaceHolder.Callback在底部的Surface状态发生变化的早晚通知View,SurfaceHolder.Callback具有如下的接口:

surfaceCreated(SurfaceHolder
holder):当Surface第一不成创后会见马上调用该函数。程序可以当该函数惨遭做来与制图界面相关的初始化工作,一般情况下还是于另外的线程来绘制界面,所以不要在此函数中绘制Surface。

surfaceChanged(SurfaceHolder holder, int format, int width,int
height):当Surface的状态(大小与格式)发生变化的上会调用该函数,在surfaceCreated调用后该函数至少会被调用一不行。

SurfaceHolder 类:

她是一个用于控制surface的接口,它提供了控制surface
的深浅,格式,上面的像素,即监视其转移的。

SurfaceView的getHolder()函数可以落SurfaceHolder对象,Surface
就在SurfaceHolder对象内。虽然Surface保存了即窗口的像素数量,但是当以过程被凡是匪直接跟Surface打交道的,由SurfaceHolder的Canvas
lockCanvas()或虽然Canvas
lockCanvas()函数来取Canvas对象,通过当Canvas上绘制内容来修改Surface中之数。如果Surface不可编辑或则从未创建调用该函数会返回null,在
unlockCanvas() 和
lockCanvas()中Surface的内容是勿缓存的,所以待了重绘Surface的情节,为了提高效率只重绘变化之一些则可以调用lockCanvas(Rect
rect)函数来指定一个rect区域,这样该区域客之情会缓存起来。在调用lockCanvas函数获取Canvas后,SurfaceView会获取Surface的一个合伙锁直到调用unlockCanvasAndPost(Canvas
canvas)函数才假释该锁,这里的同步机制确保在Surface绘制过程中莫会见为改变(被损毁、修改)。

本例没有于该surfaceview的初始化函数中以那个 ScreenW 与 ScreenH
进行赋值,这里而特别注意,如果你在初始化调用ScreenW =
this.getWidth();和ScreenH = this.getHeight();那么您以取得充分失望的值
全部吧0;原因是暨接口Callback接口机制有关,当继承callback接口会重新写她的surfaceChanged()、surfaceCreated()、surfaceDestroyed(),这几乎只函数当surfaceCreated()被实践之时光,真正的view才被创造,也就是说之前获得的价也0
,是为初始化会在surfaceCreated()方法执行以前执行,view没有底时光我们错过得屏幕宽高定是0,所以这边要注意及时或多或少;
 
此将draw的代码都try起来,主要是为了当画的内容中使丢来十分了,那么为能够于finally中执该操作。这样当代码抛来怪的时候不会见招致Surface出去不雷同的状态。  

3.View 和 SurfaceView 的区别

1.翻新画布
当 View 视图中对此画布的重新绘制,是经过调用 View 提供的
postInvalidate() 与 invalidate()
这点儿独函数来推行的,也就是说画布是由系统主 UI 进行更新。那么当系统主 UI
线程更新画布时可能会见引发部分问题;比如更新画面的年华而过长,就见面导致主
UI
线程被绘制函数阻塞,这样一来虽然会吸引无法响应按键、触屏等信息之题目。
SurfaceView
视图中对此画布的重绘是由一个新的独线程去履行拍卖,所以未会见冒出因为主 UI
线程阻塞而致无法响应按键、触屏信息相当题材

2.视图机制
Android 中之View 视图是未曾双缓冲机制的,而 SurfaceView
视图却出!也得以概括了解吧, SurfaceView 视图就是一个出于 View
拓展下的更加吻合游戏支付的视图类。
View 与 SurfaceView 都各发生那独到之处:
遵同放缓棋牌类游戏,此类型游戏画面的更新属于被动更新;因为画布的重绘主要是指以及按键与触屏事件(当玩家有矣操作之后画布才要开展创新),所以此类游戏选
View 视图进行付出比较适合,而且也缩减了为采用 SurfaceView
需单独从一个初的线程来不断更新画布所带的运作开销。
唯独如是积极创新画布的一日游类,比如RPG、飞行射击等门类的游乐受,很多因素还是动态的,需要不停重绘元素状态,这时再使
View 显然就是无适宜了。
因而究竟出娱乐采用啊种视图更加的恰当,这统统在游戏项目、风格与需要。
完来说, SurfaceView 更加切合游戏支付,因为它们亦可适应再多之玩耍类。

发表评论

电子邮件地址不会被公开。 必填项已用*标注