Android游戏开发基础.docx
《Android游戏开发基础.docx》由会员分享,可在线阅读,更多相关《Android游戏开发基础.docx(17页珍藏版)》请在冰点文库上搜索。
Android游戏开发基础
Android游戏开发基础
作者:
宋云
前言:
阅读本文之前,最少要有HelloWorld的经验,至少知道Android软件开发的过程,知道代码都要布置在什么地方。
本文并不是十分细致,但是作为查阅也足够用了,对于没有开发经验的朋友阅读本文要多做练习。
游戏开发之路任重而道远,作为个人游戏开发者,其中的艰辛更是比团队开发要辛苦的多。
同志们一定要坚持下去,前方会有那个柳暗花明的世外桃源。
目录
一、绘制矩形1
二、拖拽矩形3
三、控制时间4
四、双缓冲5
五、显示BITMAP7
六、播放声音8
七、全屏与自动调整资源9
八、存储数据11
8.1SharedPreferences11
8.2SQLite11
一、绘制矩形
绘制任务最简单的就是在界面上绘制一个矩形。
首先了解View类。
该类上可以进行任意绘制。
继承该类重写OnDraw(Canvascanvas)函数即可实现每次更新界面时的绘制内容。
例如下面这个最简单的继承View类GameView:
该类最最简单的任务就是绘制了一个红色的矩形。
对于不熟悉Android资源使用的人这里解释下颜色资源。
在values目录下新建colors.xml既是颜色资源。
颜色数据的格式支持#RGB、#ARGB、#RRGGBB、#AARRGGBB。
下面的例子就是一个红色的声明。
同时会在R文件中自动生成color.red。
在使用颜色时用getResources().getColor(R.color.red)获取。
显然这是有些麻烦的,倒不如专门声明一个颜色类管理颜色,每个颜色值都是#AARRGGBB格式,因为在标准的函数中接受的都是这种格式。
下面是View类的调用和更新,以下是主Activity类的代码:
这个类也是写的很简单,就是初始化了一个GameView并设置成为主容器试图。
如果要更新视图则要添加一个线程,来发送更新消息。
线程类代码如下,同样是最简单的一个线程只做了一件事就是发送更新消息。
在OnCreate()函数中增加初始线程并运行的代码即可:
newThread(newGameThread(gv)).start()
运行结果如下,可以在OnDraw函数中添加一些代码,例如渐变颜色red+=0x00000011以验证更新时成功。
二、拖拽矩形
可以在View的子类中添加OnTouchEvent(MotionEventevent)方法编写触摸屏事件。
Event有如下方法需要使用到:
getAction():
获取Action编码以判断当前是什么事件类型,详细可以查阅API文档。
其编码中还含有第几个触摸索引信息(多点触摸),其中MotionEvent.ACTION_DOWN,:
对应鼠标事件的按下动作;MotionEvent.ACTION_UP;相当于鼠标抬起动作;
MotionEvent.ACTION_MOVE:
拖拽动作。
贴别的是与MotionEvent.ACTION_MASK(其值为0xff,255)进行与运算可以提取出只含有动作信息的编码。
getPointerCount():
获取指针索引,自然就是依靠这个方法在软件层实现的多点触摸控制。
getX(intindex),getY(intindex):
方法获取索引为index的指针当前动作的位置。
现在就可以实现拖拽矩形的代码了,首先得将矩形的RECT定义成全局变量,之后利用offset移动矩形,在更新绘制时即可更新重绘。
一下是OnTouchEvent的代码:
其中maskAction方法是将action与ACTION_MASK进行与运算,返回运算结果。
三、控制时间
时间的控制在游戏中极为重要,下面对实现Runnable接口的类填写控制时间代码。
System.currentTimeMillis():
方法获取系统时间,以毫秒为单位。
经过试验在虚拟机中平均一次sleep需要10毫秒,因此基础睡眠时间定为10毫秒为基准。
重绘时间定为5个基准单位,数据更新时间定为10个基准单位,每次睡眠获取时间差,减掉时间差之后<=0即实现动作。
具体代码也非常简单,如下:
整个代码主要体现一种思想,绘制和数据更新一定要分开来做,各自实现其功能。
确定基准单位时间,并且尽可能的减小时间误差。
四、双缓冲
双换成技术是游戏开发中必然会用到的技术,使游戏成像流畅的进行。
使用SurfaceView类可以很容易的实现这个功能。
下面将之前写的GameView类和GameRunnalbe类合并成一个类——GameSurfaceView类。
继承SurfaceView类实现接口SurfaceHolder.Callback,Runnable,一个接口用来处理系统消息,另一个接口用来处理线程。
SurfaceHolder用来处理所有的系统消息和绘制,缓冲Canvas也需要用它来实现。
在构造函数中实例化一个SurfaceHolder类,并且添加回调函数,以处理系统消息。
三个系统消息,需要添加了回调函数才会被调用,分别处理改变大小、创建和销毁时的动作。
与GameRunnalbe类的run方法和变量都是一样的,唯一不一样的地方时给GameView发送更新消息改成了,直接调用绘制函数draw(),并且通过变量mLoop来控制线程的终止。
开构造函数中出事成true,销毁函数中置为false。
绘制函数,与GameView中的基本一样,不同的地方时需要锁定画布、清屏、锁屏显示。
由SurfaceHolder实现了双缓存。
onTouchEvent与GameView中的一样,这里就不重复了。
现在已经实现了双缓存,并且SurfaceView更灵活。
五、显示BITMAP
下面来介绍Canvas显示Bitmap。
显示一个Bitmap的方法有很多,这里介绍最简单的一种。
Canvas的drawBitmap方法。
在绘制之前需要先获取Bitmap资源,过程是从Drawable资源中获取BitmapDrawable再获取该Drawable的Bitmap。
这几行代码获取了7张Bitmap,其中R.drawable.g1这些东西是添加图片的时候程序自动生成的,每一个ID对应一个图片资源。
这7个图片连在一起就可以一个小动画,例如设置一个全局变量p,更新数据时p++显示的时候显示bmp[p]即可。
Canvas的drawBitmap方法有一下几种:
voiddrawBitmap(Bitmapbitmap,floatleft,floattop,Paintpaint):
最基本的绘制方法,需要绘制位置的左上角坐标和要绘制的bitmap但是paint可以为null。
voiddrawBitmap(Bitmapbitmap,Rectsrc,RectFdst,Paintpaint)
voiddrawBitmap(Bitmapbitmap,Rectsrc,Rectdst,Paintpaint):
这两个方法类似需要绘制的bitmap;需要绘制bitmap上的哪个矩形区域src的内容;绘制到canvas的哪个矩形位置dst,其中RectF表示浮点型的矩形,同样paint可以为null。
voiddrawBitmap(Bitmapbitmap,Matrixmatrix,Paintpaint):
这个方法适合绘制需要经常变动的对象,之前介绍的三种方法都只适合绘制没有太多变化的图片,如果一个图片需要经常旋转,缩放等操作用这个方法在合适不过。
setTranslate(x,y)可以设置平移的位置,preTranslate(x,y)累加平移位置。
同样有preRotate,setRotate,preScale,setScale等。
至于一下三种方法:
voiddrawBitmap(int[]colors,intoffset,intstride,intx,inty,intwidth,intheight,booleanhasAlpha,Paintpaint)
voiddrawBitmap(int[]colors,intoffset,intstride,floatx,floaty,intwidth,intheight,booleanhasAlpha,Paintpaint)
voiddrawBitmapMesh(Bitmapbitmap,intmeshWidth,intmeshHeight,float[]verts,intvertOffset,int[]colors,intcolorOffset,Paintpaint)
有兴趣的读者可以在
一下代码既是在(0,320)位置绘制bmp[p]图像,这样就显示出了一个动画。
同时这样产生一个问题——浪费内存,读取图片的时候利用BitmapFactory的decodeStream方法获取bitmap要比从drawable中获取bitmap要节省更多的内存空间。
代码如下:
InputStreamis=this.getResources().openRawResource(id);
BitmapFactory.Optionsoptions=newBitmapFactory.Options();
options.inJustDecodeBounds=false;
//options.inSampleSize=10;//width,hight设为原来的十分一
Bitmapbmp=BitmapFactory.decodeStream(is,null,options);
回收bitmap资源bitmap.recycle();
再提示系统回收资源system.gc();
六、播放声音
播放多媒体都可以用MediaPlayer实现,比较方便的实现办法是在资源中新建目录raw,例如:
里面有一个声音文件down.wav,加入了声音文件R文件中自动生成起ID,只需要用MediaPlayer的create方法创建一个MediaPlayer对象,播放即可:
三行代码实现播放声音,其中seLooping是设置其是否循环。
但是在真正使用声音资源的时候,这样是极其低效的。
我们可以构造一个常用声音的声音池,预先创建好声音对象,只需要一个函数play(intid)就播放声音,并且节省资源。
七、全屏与自动调整资源
//无标题
requestWindowFeature(Window.FEATURE_NO_TITLE);
//全屏模式
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
//横屏//setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
全屏时很好设置的,在Activity的初始化中加入上面代码就可以了。
要让程序界面保持一个方向,不随手机方向转动而变化的处理办法:
要在AndroidManifest.xml里面配置一下。
加入这一行android:
screenOrientation="landscape"。
例如(landscape是横向,portrait是纵向):
Java代码:
xmlversion="1.0"encoding="utf-8"?
>
android="
package="com.ray.linkit"
android:
versionCode="1"
android:
versionName="1.0">
icon="@drawable/icon"android:
label="@string/app_name">
label="@string/app_name"
android:
name=".Defense_TetrisActivity"
android:
screenOrientation="portrait"
android:
configChanges="orientation|keyboardHidden">
minSdkVersion="3"/>
另外,android中每次屏幕的切换动会重启Activity,首先要在配置Activity的时候进行如下的配置:
android:
configChanges="keyboardHidden|orientation",另外需要重写Activity的onConfigurationChanged方法就不会再重启了。
实现方式如下,不需要做太多的内容:
@Override
publicvoidonConfigurationChanged(ConfigurationnewConfig){
super.onConfigurationChanged(newConfig);
}
有时为了使用与任何大小的屏幕,先获取屏幕信息:
floatw=this.getWindowManager().getDefaultDisplay().getWidth();
floath=this.getWindowManager().getDefaultDisplay().getHeight();
再计算缩放比例:
scaleX=w/STAND_WIDTH;
scaleY=h/STAND_HEIGHT;
绘制的时候Matrix先设置scale,再对transX,transY缩放平移即可。
但是这样做有些麻烦,可以用Android程序所自提供的功能,准备三种像素的图片素材,在不同分辨率的屏幕上运行,程序会自动选择使用哪种素材,在AndroidManifest.xml中加入如下配置:
android:
largeScreens="true"
android:
normalScreens="true"
android:
smallScreens="true"
android:
resizeable="true"
android:
anyDensity="true">
android:
largeScreens="true"表示支持大屏幕(480X800);
android:
largeScreens="true"表示支持中等屏幕(320X480);
android:
largeScreens="true"表示支持小屏幕(240X320);
android:
resizeable="true"表示可重新调整尺寸;
android:
anyDensity="true">表示支持任何分辨率;
资源目录下的结构如下:
我们在-hdpi的目录下放大分辨率下需要的资源,-mdpi下方中分辨率需要的资源,-ldpi目录下放低分辨率需要的资源,这样程序就可以自动调整所需要的素材了。
资源用系统自动调整了,那么绘制的偏移坐标,需要我们自己设定,每一个物体的坐标设置三个,高中低三种分辨率的偏移坐标,之后在程序初始化的时候查询一下屏幕分辨率以确定使用哪个坐标。
八、存储数据
存储数据也是游戏中最基本的功能,介绍完数据的存储,那么制作一个游戏所需要的所有基础就都介绍完了。
剩下的就是程序设计,算法,仿真物理等。
这些东西就都不是能够速成的了,需要耐心的从基础练起。
8.1SharedPreferences
SharedPreferences主要用来记录简单的参数,和一些简单的变量。
以私有方式获得
SharedPreferencessettings=getPreferences(Activity.MODE_PRIVATE);
获得数据(如不存在则返回默认值):
intnum=Defense_TetrisActivity.settings.getInt("num",0);
编辑数据:
Editoredit=Defense_TetrisActivity.settings.edit();
edit.putInt("num",num);
mit();//执行保存
8.2SQLite
将数据存储在数据库中既方便管理又方便维护,大的小的数据都可以放到数据库中存储。
1.创建和打开数据库
openOrCreateDatabase方法实现创建或打开一个数据库,存在则打开不存在则创建一个数据库,返回SQLiteDatabase失败则抛出FileNotFoundException异常,例如:
sqliteDB=this.openOrCreateDatabase(“Example_DB”,MODE_PRIVATE,null);
创建了一个不存在的或打开了一个存在的Example_DB数据库。
2.创建表
execSQL方法执行一个创建表的SQL语句来创建表,例如:
StringCREATE_TABLE=
“CREATETABLEtable1(_idINTEGERPRIMARYKEY,numINTEGER,dataTEXT)”
sqliteDB.execSQL(CREATE_TALBE);
创建了一个名为table1的表,有3个字段,_id为主键;num为整型数据;data为字符段。
3.向表中添加一条数据
ContentValues相当于一个Map,存储了一条数据,例如:
ContentValuescv=newContentValues();
cv.put(TABLE_NUM,1);
cv.put(TABLE_DATA,’测试数据库数据’);
sqliteDB.insert(TABLE_NAME,null,cv);
或者使用execSQL执行插入数据SQL语句。
4.从表中删除数据
sqliteDB.delete(TABLE_NAME,’WHERE_id=0’,null)
删除了TABLE_NAME表中_id等于0的数据,或者使用execSQL执行删除表中数据的SQL语句。
或者使用execSQL执行删除表中数据的SQL语句。
5.修改表中的数据
ContectValuescv=newContentValues();
cv.put(TALBE_NUM,3);
cv.put(TABLE_DATA,”修改后的数据”);
sqliteDB.update(“table1”,cv,”num=0”,null);
将table1表中num=0的数据修改。
或者使用execSQL。
6.关闭数据库
sqliteDB.close();
7.删除表
SqliteDB.execSQL(“DROPTABLEtable1”);
8.删除数据库
This.deleteDatabase(DATABASE_NAME)
9.查询表
Cursorcur=sqliteDB.rawQuery(“SELECT*FROMtable1”,null);
if(cur!
=null){
if(cur.moveToFirst()){
do{
intnumColum=cur.getColumIndex(“num”);
intnum=cur.getInt(numColum);
}while(cur.moveToNext());
}
}
}