第四章 Android游戏开发之图形界面
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
第四章 Android游戏开发之图形界面
4.1 图片
图片是游戏的必备元素之一。
4.1.1 drawable对象
游戏所使用到的图片资源,我们都把它放置到res文件夹中的drawable中,当增加了drawable对象后,Android SDK会为该图片
资源在R清单文件中创建一个索引项,该索引的名字为
R.drawable.fileName。
生成了该资源的索引后,在XML资源文件中就可以通过@drawable/fileName来访问该图片资源,也可以在代码中
R.drawable.fileName来访问该drawable对象。
而
R.drawable.fileName只是一个int类型的常量,它只是代表了
drawable对象的ID,程序代码当中需要获取到实际的drawable对象,
需要调用Resource的getDrawable(int id)方法来获取。
4.1.2 Bitmap
Bitmap即位图,Bitmap类中欧那个提供了一些静态方法来创建新的Bitmap对象,如下是常用的方法。
对象,如:BitmapDrawable drawable = new BitmapDrawable(bitmap);获取BitmapDrawable所包装的Bitmap对象可以调用该类的getBitmap()方法,如Bitmap bitmap = drawable.getBitmap();。
BitmapFactory是一个Bitamap的工具类,它共提供了多种方法以便于从不同的数据源来解析和创建Bitmap对象,如下。
drawable目录中,而程序代码中通过该图片对应的资源ID来获取封装该图片的drawable对象即可。
但手机系统的内存是有限的,如果不停的创建Bitmap对象,就会导致内存不够用的情况,所以Bitmap有各两个非常重要的方法来判断图片资源的是否回收和强制Bitmap进行回收。
一下都是一些Bitmap常用到的方法。
4.2 绘图
了解了图片后,游戏当中最重要的一部分操作就是图片的绘制了。
本节主要来介绍Android当中的绘图。
Android当中的绘图组件都是继承自View组件,并且重写onDraw(Canvas canvas)方法即可。
关于View前面的章节当中已经讲解过了,本节主要来看看涉及到绘图onDraw方法当中的Canvas对象,以及相关的Paint类的使用。
4.2.1 Canvas类
Canvas可以理解为一张画布,而该画布是属于它所在的View 对象的。
Canvas当中提供了一系列的方法用于绘制,如下。
4.2.2 Paint类
从Canvas的绘制方法可以看到,所有的绘制方法都需要一个Paint参数,Paint是Canvas上的画笔,Paint主要用于设置绘制的一些属性,比如画笔的颜色,画笔的笔触粗细,填充的风格等。
如下是Paint类中的常用方法。
4.2.3 绘制实例
上面介绍了Android中的各种绘制,本节中我们通过实例来具体了解绘制过程。
1.创建一个名为TestCanvas的项目。
2.将要绘制的图片资源拷贝到res当中drawable路径中。
3.打开string.xml文件设置要绘制的字符串,如下。
4.新建一个类继承View,实现onDraw方法如下。
5.在Activity类中显示MyView对象,如下。
6.最后运行显示结果如下图。
4.3 View与 SurfaceView
前面的介绍中绘图主要使用的是View组件,但实际的游戏开发中,我们一般使用的都是SurfaceView,使用SurfaceView的原因是因为View 当中没有双缓冲的机制,当程序需要更新View上的图像时,程序必须重绘View上显示的整张图片,所以通过自定义View来实现绘图尤其是游戏中的绘图效率并不高,而使用Android当中提供的SurfaceView来进行游戏的绘制比View更加出色,所以一般推荐使用SurfaceView,SurfaceView 是从View基类中派生出来的显示类,所以View当中所有的SurfaceView 当中也有。
SurfaceView可以控制表面的格式,比如大小,显示在屏幕中的位置,最关键是的提供了SurfaceHolder类,使用getHolder方法获取,相关的有Canvas lockCanvas()、Canvas lockCanvas(Rect dirty) 、void removeCallback(SurfaceHolder.Callback callback)、void unlockCanvasAndPost(Canvas canvas) 控制图形以及绘制,而在SurfaceHolder.Callback 接口回调中可以通过重写下面方法实现。
使用的SurfaceView的时候,一般情况下要对其进行创建,销毁,改变时的情况进行监视,这就要用到 SurfaceHolder.Callback。
class xView extends SurfaceView implements SurfaceHolder.Callback {
//在surface的大小发生改变时激发
public void surfaceChanged(SurfaceHolder holder,int
format,int width,int height)
{
}
//在创建时激发,一般在这里调用画图的线程。
public void surfaceCreated(SurfaceHolder holder)
{
}
//销毁时激发,一般在这里将画图的线程停止、释放。
public void surfaceDestroyed(SurfaceHolder holder)
{
}
}
对于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来控制。
Android中的SurfaceView类就是双缓冲机制。
因此,开发游戏时尽量使用SurfaceView而不要使用View,这样的话效率较高,而且SurfaceView 的功能也更加完善。
考虑以上几点,所以一般选用 SurfaceView 来进行游戏开发。
4.3.1 View以及双缓冲实现
View中常用的方法如下。
View
当中如何实现双缓冲。
1.创建一个名为TestView的项目。
2.创建一个MyView的类继承View类,内容如下。
3.在Activity中设置显示MyView对象,如下。
4.运行如下图。
从上述的例子代码中可以看出,双缓冲即先在内存中创建一幅图片,所有的绘制是绘制在该内存图片当中,在屏幕上的时候只负责显示内存当中图片,这样绘制显然优于从外部设备直接绘制到屏幕上的图片,同时可以减少屏幕闪烁等问题。
接下来看一个View的应用实例,实现游戏的菜单。
1.创建一个名为TestMenu的项目。
2.将要使用的图片资源导入到res的drawable当中。
3.实现一个MyView的类继承View,并实现代码如下。
4.在Activity中,显示MyView对象。
5.运行效果如下图。
该案例当中用触屏时间来判断按下的是哪个菜单选项。
4.3.2 SurfaceView中的绘制
SurfaceView是View的子类,但SurfaceView自带双缓冲,它一般与SurfaceHolder结合使用,SurfaceHolder用于向与之关联的SurfaceView上绘图,SurfaceView的getHolder()函数可以获取SurfaceHolder对象,SurfaceView就在SurfaceHolder对象内。
虽然SurfaceView保存了当前窗口的像素数据,但是在使用过程中是不直接和SurfaceView打交道的,由SurfaceHolder的Canvas lockCanvas()或则Canvas lockCanvas()函数来获取Canvas对象,通过在Canvas上绘制内容来修改SurfaceView中的数据。
如果SurfaceView不可编辑或则尚未创建调用该函数会返回null,在unlockCanvas() 和 lockCanvas()中SurfaceView的内容是不缓存的,所以需要完全重绘SurfaceView的内容,为了提高效率只重绘变化的部分则可以调用lockCanvas(Rect rect)函数来指定一个rect 区域,这样该区域外的内容会缓存起来。
在调用lockCanvas函数获取Canvas后,SurfaceView会获取SurfaceView的一个同步锁直到调用unlockCanvasAndPost(Canvas canvas)函数才释放该锁,这里的同步机制保证在SurfaceView绘制过程中不会被改变。
SurfaceHolder提供的获取Canvas对象的方法。
但第一个方法之将Rect所指定的区域提取出来进行更新,这种方式可以提高画面的更新速度,而最后一个方法,当调用之后该方法前的绘制内容还存储于缓冲之中,下一次lockCanvas的时候,该方法锁定的区域可能会遮挡住前面的内容,所以如果不想被遮挡,可以选择重新lockCanvas一次,而lock的区域为0,再释放,保证下次lock 的时候被遮挡。
实际的游戏开发当中,我们会为SurfaceHolder添加一个
一次创建后会立即调用该函数。
程序可以在该函数中做些和绘制界面相关的初始化工作比如获取到屏幕的宽高等,一般情况下都是在另外的线程来绘制界面,所以不要在这个函数中绘制SurfaceView,而屏幕的宽高等初始化也只能在该方法或者该方法调用后方能初始化,因为当我们继承callback接口后,会重写它的surfaceChanged()、surfaceCreated()、surfaceDestroyed()方法,而这几个方法是在当surfaceCreated()被执行的时候,真正的view 才被创建,所以如果在之前去获取那么获取到的值也就是0 ,是因为在surfaceCreated()方法执行以前执行,view没有的时候我屏幕宽高肯定是0。
下面通过实例了解如何在SurfaceView中进行绘制。
1.创建一个名为TestSurfaceView的项目。
2.将要绘制的图片资源导入到drawable当中。
3.创建一个MySurfaceView类继承SurfaceView并实现
Runnable接口和CallBack接口,内容如下。
4.在Activity当中设置显示。
5.显示结果如图。
4.3.3 SurfaceView中绘制动画
动画也就是会动的画面,它利用的是人的视觉暂留的现象,使静止的画面连续播放,感觉像是在动。
游戏中常常会有大量的动画效果,而动画的最基本的前提是要在足够快的时间内显示和更换一幅幅画面,而画面必须按照一定的顺序来绘制,从一张图到下一张图片之间的变化越小效果越好。
图片播放的频率称为FPS(Frame Per Second),即每秒播放的帧数。
动画编程的实现在Android当中有多种,但实现的基本原理一致,即在一定时间内在屏幕上绘制出一些列画面帧。
下面通过爆炸动画的实例来了解如何绘制动画。
1.创建一个名为TestAnimation的项目。
2.将要绘制的图片资源导入到drawable当中。
3.创建一个MyView类继承SurfaceView并实现Callback和
Runnable接口,代码如下。
4.在Activity中显示MyView对象,如下。
5.运行结果如下。
4.3.4 SurfaceView中的按键处理
Android当中为所有的View组件都提供了一些事件处理的方法,以下View当中提供的回调机制的事件处理方法。
例来了解按键事件的处理,以下实例是通过按键控制飞机的移动。
1.先创建一个名为TestGame的项目。
2.将要绘制的图片资源导入到drawable当中。
3.创建一个Player类,表示被控制的玩家飞机。
4.创建一个MyView类继承SurfaceView并实现Callback和
Runnable接口,代码如下。
5.在Activity类当中设置如下。
6.运行结果如下。
4.4 本章小结
本章讲解了游戏当中View、SurfaceView、Canvas、Paint等类的使用,以及动画的实现和按键事件的处理。
通过SurfaceView来显示游戏界面,Canvas、Paint结合使用来绘制图形。