Android游戏开发教程两篇

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Android游戏开发教程两篇
篇一:Android游戏开发
classCwjViewextendsView{
privateGestureDetectormGD;
publicCwjView(Contextcontext,AttributeSetattrs){
super(context,attrs);
mGD=newGestureDetector(context,newGestureDetector.Simp leOnGestureListener(){
publicbooleanonFling(MotionEvente1,Mot ionEvente2,floatvelocityX,floatvelocityY){
intdx=(int)(e2.getX()-e1.getX( ));//计算滑动的距离
if(Math.abs(dx)>MAJOR_MOVE&&Ma th.abs(velocityX)>Math.abs(velocityY)){//降噪处理,必须有较大的动作才识别
if(velocityX>0){
//向右边
}else{
//向左边
}
returntrue; }else{
returnfalse; //当然可以处理velocityY处理向上和向下的动作……
正文:
目录:
Android游戏开发一长按Button原理
Android游戏开发二View和SurfaceView
Android游戏开发三View类详解
Android游戏开发四Canvas和Paint实例
Android游戏开发五Path和Typeface
Android游戏开发六自定义View
Android游戏开发七自定义SurfaceView
Android游戏开发八SurfaceView类实例
Android游戏开发九VideoView类剖析
Android游戏开发十位图旋转
Android游戏开发11View中手势识别
Android游戏开发12Sensor重力感应
Android游戏开发13Sensor感应示例
Android游戏开发14游戏开发实战一
Android游戏开发15按键中断处理
Android游戏开发16异步音乐播放
Android游戏开发17图像渐变特效
Android游戏开发18SoundPool类
Android游戏开发19分辨率大全
Android游戏开发20双按事件捕获
Android游戏开发一长按Button原理
今天Android123开始新的Android游戏开发系列,主要从控制方法(按键、轨迹球、触屏、重力感应、摄像头、话筒气流、光线亮度)、图形View(高效绘图技术如双缓冲)、音效(游戏音乐)以及最后的OpenGLES(Java层)和NDK的OpenGL 和J2ME游戏移植到Android方法,当然还有一些游戏实现惯用方法,比如地图编辑器,在AndroidOpenGL如何使用MD2文件,个部分讲述下Android游戏开发的过程最终实现一个比较完整的游戏引擎。

相信大家都清楚AndroidMarket 下载量比较好的都是游戏,未来手机网游的发展相信Android使用的Java在这方面有比iPhone有更低的入门门槛。

对于很多游戏使用屏幕控制一般需要考虑长按事件,比如在动作类的游戏中需要长按发射武器,结合AndroidButton模型,我们实现一个带图片的Button 的长按,为了更清晰的显示原理,Android开发网这里使用ImageButton作为基类
publicclassRepeatingImageButtonextendsImageButton{
privatelongmStartTime;//记录长按开始
privateintmRepeatCount;//重复次数计数
privateRepeatListenermListener;
privatelongmInterval=500;//Timer触发间隔,即每0.5秒算一次按下publicRepeatingImageButton(Contextcontext){
this(context,null);
}
publicRepeatingImageButton(Contextcontext,AttributeSetattrs){ this(context,attrs,android.R.attr.imageButtonStyle);
}
publicRepeatingImageButton(Contextcontext,AttributeSetattrs,in tdefStyle){
super(context,attrs,defStyle);
setFocusable(true);//允许获得焦点
setLongClickable(true);//启用长按事件
}
publicvoidsetRepeatListener(RepeatListenerl,longinterval){//实现重复按下事件listener
mListener=l;
mInterval=interval;
}
@Override
publicbooleanperformLongClick(){
mStartTime=SystemClock.elapsedRealtime();
mRepeatCount=0;
post(mRepeater);
returntrue;
}
@Override
publicbooleanonTouchEvent(MotionEventevent){
if(event.getAction()==MotionEvent.ACTION_UP){ //
本方法原理同onKeyUp的一样,这里处理屏幕事件,下面的onKeyUp处理Android 手机上的物理按键事件
removeCallbacks(mRepeater);
if(mStartTime!=0){
doRepeat(true);
mStartTime=0;
}
}
returnsuper.onTouchEvent(event);
}
//处理导航键事件的中键或轨迹球按下事件
@Override
publicbooleanonKeyDown(intkeyCode,KeyEventevent){
switch(keyCode){
caseKeyEvent.KEYCODE_DPAD_CENTER:
caseKeyEvent.KEYCODE_ENTER:
super.onKeyDown(keyCode,event);
returntrue;
}
returnsuper.onKeyDown(keyCode,event);
}
//当按键弹起通知长按结束
@Override
publicbooleanonKeyUp(intkeyCode,KeyEventevent){
switch(keyCode){
caseKeyEvent.KEYCODE_DPAD_CENTER:
caseKeyEvent.KEYCODE_ENTER:
removeCallbacks(mRepeater);//取消重复listener 捕获
if(mStartTime!=0){
doRepeat(true);//如果长按事件累计时间不为0则说明长按了
mStartTime=0; //重置长按计时器
}
}
returnsuper.onKeyUp(keyCode,event);
}
privateRunnablemRepeater=newRunnable(){ //在线程中判断重复publicvoidrun(){
doRepeat(false);
if(isPressed()){
postDelayed(this,mInterval);//计算长按后延迟下一次累加
}
}
};
private voiddoRepeat(booleanlast){
longnow=SystemClock.elapsedRealtime();
if(mListener!=null){
mListener.onRepeat(this,now-mStartTime,last?-1 :mRepeatCount++);
}
}
下面是重复ButtonListener接口的定义,调用时在Button中先使用setRepeatListener()方法实现RepeatListener接口
publicinterfaceRepeatListener{
voidonRepeat(Viewv,longduration,intrepeatcount );//参数一为用户传入的Button对象,参数二为延迟的毫秒数,第三位重复次数回调。

}
}
Android游戏开发二View和SurfaceView
在Android游戏当中充当主要的除了控制类外就是显示类,在J2ME中我们用Display和Canvas来实现这些,而GoogleAndroid中涉及到显示的为view类,Android游戏开发中比较重要和复杂的就是显示和游戏逻辑的处理。

这里我们说下android.view.View和android.view.SurfaceView。

SurfaceView是从View 基类中派生出来的显示类,直接子类有GLSurfaceView和VideoView,可以看出GL和视频播放以及Camera摄像头一般均使用SurfaceView,到底有哪些优势呢?SurfaceView可以控制表面的格式,比如大小,显示在屏幕中的位置,最关键是的提供了SurfaceHolder类,使用getHolder方法获取,相关的有Canvas lockCanvas()
Canvas lockCanvas(Rectdirty) 、
void removeCallback(SurfaceHolder.Callbackcallback)、
void unlockCanvasAndPost(Canvascanvas)控制图形以及绘制,而在SurfaceHolder.Callback接口回调中可以通过下面三个抽象类可以自己定义具体的实现,比如第一个更改格式和显示画面。

abstractvoid surfaceChanged(SurfaceHolderholder,intformat,intwidth, intheight)
abstractvoid surfaceCreated(SurfaceHolderholder)
abstractvoid surfaceDestroyed(SurfaceHolderholder)
对于Surface相关的,Android底层还提供了GPU加速功能,所以一般实时性很强的应用中主要使用SurfaceView而不是直接从View构建,同时Android123未来后面说到的OpenGL中的GLSurfaceView也是从该类实现。

Android游戏开发三View类详解
在Android游戏开发二中我们讲到了View和SurfaceView的区别,今天Android123从View类开始着重的介绍Android图形显示基类的相关方法和注意点。

自定义View的常用方法:
onFinishInflate()当View中所有的子控件均被映射成xml后触发onMeasure(int,int)确定所有子元素的大小
onLayout(boolean,int,int,int,int)当View分配所有的子元素的大小和位置时触发
onSizeChanged(int,int,int,int)当view的大小发生变化时触发
onDraw(Canvas)view渲染内容的细节
onKeyDown(int,KeyEvent)有按键按下后触发
onKeyUp(int,KeyEvent)有按键按下后弹起时触发
onTrackballEvent(MotionEvent)轨迹球事件
onTouchEvent(MotionEvent)触屏事件
onFocusChanged(boolean,int,Rect) 当View获取或失去焦点时触发
onWindowFocusChanged(boolean)当窗口包含的view获取或失去焦点时触发onAttachedToWindow()当view被附着到一个窗口时触发onDetachedFromWindow()当view离开附着的窗口时触发,Android123提示该方法和onAttachedToWindow()是相反的。

onWindowVisibilityChanged(int)当窗口中包含的可见的view发生变化时触发以上是View实现的一些基本接口的回调方法,一般我们需要处理画布的显示时,重写onDraw(Canvas)用的的是最多的:
@Override
protectedvoidonDraw(Canvascanvas){
//这里我们直接使用canvas对象处理当前的画布,比如说使用Paint来选择要填充的颜色
PaintpaintBackground=newPaint();
paintBackground.setColor(getResources().getColor(R.color.xxx)); //从Res中找到名为xxx的color颜色定义
canvas.drawRect(0,0,getWidth(),getHeight(),paintBackground);//设置当前画布的背景颜色为paintBackground中定义的颜色,以0,0作为为起点,以当前画布的宽度和高度为重点即整块画布来填充。

具体的请查看Android123未来讲到的Canvas和Paint,在Canvas中我们可以实现画路径,图形,区域,线。

而Paint作为绘画方式的对象可以设置颜色,大小,甚至字体的类型等等。

}
当然还有就是处理窗口还原状态问题(一般用于横竖屏切换),除了在Activity中可以调用外,开发游戏时我们尽量在View中使用类似
@Override
protectedParcelableonSaveInstanceState(){
Parcelablep=super.onSaveInstanceState();
Bundlebundle=newBundle();
bundle.putInt("x",pX);
bundle.putInt("y",pY);
bundle.putParcelable("android123_state",p);
returnbundle;
}
@Override
protectedvoidonRestoreInstanceState(Parcelablestate){
Bundlebundle=(Bundle)state;
dosomething(bundle.getInt("x"),bundle.getInt("y"));//获取刚才存储的x和y信息
super.onRestoreInstanceState(bundle.getParcelable("android 123_state"));
return;
}
在View中如果需要强制调用绘制方法onDraw,可以使用invalidate()方法,它有很多重载版本,同时在线程中的postInvailidate()方法将在Android游戏开发六中的自定义View完整篇讲到。

Android游戏开发四Canvas和Paint实例
昨天我们在Android游戏开发三 View详解中提到了onDraw方法,有关详细的实现我们今天主要说下Android的Canvas和Paint对象的使用实例。

Canvas类主要实现了屏幕的绘制过程,其中包含了很多实用的方法,比如绘制一条路径、区域、贴图、画点、画线、渲染文本,下面是Canvas类常用的方法,当然Android开发网提示大家很多方法有不同的重载版本,参数更灵活。

voiddrawRect(RectFrect,Paintpaint)//绘制区域,参数一为RectF一个区域
voiddrawPath(Pathpath,Paintpaint)//绘制一个路径,参数一为Path路径对象
void drawBitmap(Bitmapbitmap,Rectsrc,Rectdst,Paintpaint) //贴图,参数一就是我们常规的Bitmap对象,参数二是源区域(Android123提示这里是bitmap),参数三是目标区域(应该在canvas的位置和大小),参数四是Paint画刷对象,因为用到了缩放和拉伸的可能,当原始Rect不等于目标Rect 时性能将会有大幅损失。

void drawLine(floatstartX,floatstartY,floatstopX,floatstopY,Paint paint) //画线,参数一起始点的x轴位置,参数二起始点的y轴位置,参数三终点的x轴水平位置,参数四y轴垂直位置,最后一个参数为Paint画刷对象。

void drawPoint(floatx,floaty,Paintpaint)//画点,参数一水平x轴,参数二垂直y轴,第三个参数为Paint对象。

voiddrawText(Stringtext,floatx,floaty,Paintpaint) //渲染文本,Canvas类除了上面的还可以描绘文字,参数一是String类型的文本,参数二x 轴,参数三y轴,参数四是Paint对象。

void drawTextOnPath(Stringtext,Pathpath,floathOffset,floatvOffset ,Paintpaint)//在路径上绘制文本,相对于上面第二个参数是Path路径对象从上面来看我们可以看出Canvas绘制类比较简单同时很灵活,实现一般的方法通常没有问题,同时可以叠加的处理设计出一些效果,不过细心的网友可能发现最后一个参数均为Paint对象。

如果我们把Canvas当做绘画师来看,那么Paint就是我们绘画的工具,比如画笔、画刷、颜料等等。

Paint类常用方法:
void setARGB(inta,intr,intg,intb) 设置Paint对象颜色,参数一为alpha透明通道
void setAlpha(inta) 设置alpha不透明度,范围为0~255
void setAntiAlias(booleanaa) //是否抗锯齿
void setColor(intcolor) //设置颜色,这里Android内部定义的有Color 类包含了一些常见颜色定义
.
void setFakeBoldText(booleanfakeBoldText) //设置伪粗体文本
void setLinearText(booleanlinearText) //设置线性文本
PathEffect setPathEffect(PathEffecteffect) //设置路径效果Rasterizer setRasterizer(Rasterizerrasterizer)//设置光栅化
Shader setShader(Shadershader) //设置阴影
void setTextAlign(Paint.Alignalign) //设置文本对齐
void setTextScaleX(floatscaleX) //设置文本缩放倍数,1.0f为原始void setTextSize(floattextSize) //设置字体大小
Typeface setTypeface(Typefacetypeface) //设置字体,Typeface包含了字体的类型,粗细,还有倾斜、颜色等。

void setUnderlineText(booleanunderlineText) //设置下划线
最终Canvas和Paint在onDraw中直接使用
@Override
protectedvoidonDraw(Canvascanvas){
PaintpaintRed=newPaint();
paintRed.setColor(Color.Red);
canvas.drawPoint(11,3,paintRed);//在坐标11,3上画一个红点
}
下一次Android123将会具体讲到强大的Path路径,和字体Typeface相关的使用。

Android游戏开发(五)Path和Typeface
今天我们继续处理上次Android游戏开发(四)Canvas和Paint实例中提到的Path路径和Typeface字体两个类。

对于Android游戏开发或者说2D绘图中来讲Path路径可以用强大这个词来形容。

在Photoshop中我们可能还记得使用钢笔工具绘制路径的方法。

Path路径类在位于android.graphics.Path中,Path 的构造方法比较简单,如下
Pathcwj=newPath(); //构造方法
复制代码
下面我们画一个封闭的原型路径,我们使用Path类的addCircle方法
cwj.addCircle(10,10,50,Direction.CW);//参数一为x轴水平位置,参数二为y轴垂直位置,第三个参数为圆形的半径,最后是绘制的方向,CW为顺时针方向,而CCW是逆时针方向
复制代码
结合Android上次提到的Canvas类中的绘制方法drawPath和drawTextOnPath,我们继续可以在onDraw中加入。

canvas.drawPath(cwj,paintPath);//Android123提示大家这里paintPath为路径的画刷颜色,可以见下文完整的源代码。

canvas.drawTextOnPath("Android123-CWJ",cwj,0,15,paintText);//将文字
绘制到路径中去,
复制代码
有关drawTextOnPath的参数如下:
方法原型
publicvoiddrawTextOnPath(Stringtext,Pathpath,floathOffset,floatvOffs et,Paintpaint)
复制代码
参数列表
text 为需要在路径上绘制的文字内容。

path将文字绘制到哪个路径。

hOffset 距离路径开始的距离
vOffset 离路径的上下高度,这里Android开发网提示大家,该参数类型为float浮点型,除了精度为8位小数外,可以为正或负,当为正时文字在路径的圈里面,为负时在路径的圈外面。

paint 最后仍然是一个Paint对象用于制定Text本文的颜色、字体、大小等属性。

下面是我们的onDraw方法中如何绘制路径的演示代码为:
@Override
protectedvoidonDraw(Canvascanvas){
PaintpaintPath=newPaint();
PaintpaintText=newPaint();
paintPath.setColor(Color.Red);//路径的画刷为红色
paintText.setColor(Color.Blue);//路径上的文字为蓝色
PathpathCWJ=newPath();
pathCWJ.addCircle(10,10,50,Direction.CW);//半径为50px,绘制的方向CW为顺时针
canvas.drawPath(pathCWJ,paintPath);
canvas.drawTextOnPath("Android123-CWJ",pathCWJ,0,15,paintText);/ /在路径上绘制文字
}
复制代码
有关路径类常用的方法如下:
void addArc(RectFoval,floatstartAngle,floatsweepAngle) //为路径添加一个多边形
void addCircle(floatx,floaty,floatradius,Path.Directiondir) //给path添加圆圈
void addOval(RectFoval,Path.Directiondir) //添加椭圆形
void addRect(RectFrect,Path.Directiondir) //添加一个区域
void addRoundRect(RectFrect,float[]radii,Path.Directiondir) //添加一个圆角区域
boolean isEmpty() //判断路径是否为空
void transform(Matrixmatrix) //应用矩阵变换
void transform(Matrixmatrix,Pathdst) //应用矩阵变换并将结果放到新的路径中,即第二个参数。

复制代码
有关路径的高级效果大家可以使用PathEffect类,有关路径的更多实例Android123将在今后的游戏开发实战中讲解道。

Typeface字体类
平时我们在TextView中需要设置显示的字体可以通过TextView中的setTypeface方法来指定一个Typeface对象,因为Android的字体类比较简单,我们列出所有成员方法
staticTypeface create(Typefacefamily,intstyle) //静态方法,参数一为字体类型这里是Typeface的静态定义,如宋体,参数二风格,如粗体,斜体
staticTypeface create(StringfamilyName,intstyle) //静态方法,参数一为字体名的字符串,参数二为风格同上,这里我们推荐使用上面的方法。

staticTypeface createFromAsset(AssetManagermgr,Stringpath) //
静态方法,参数一为AssetManager对象,主要用于从APK的assets文件夹中取出字体,参数二为相对于Android工程下的assets文件夹中的外挂字体文件的路径。

staticTypeface createFromFile(Filepath) //静态方法,从文件系统构造一个字体,这里参数可以是sdcard中的某个字体文件
staticTypeface createFromFile(Stringpath) //静态方法,从指定路径中构造字体
staticTypeface defaultFromStyle(intstyle)//静态方法,返回默认的字体风格
int getStyle() //获取当前字体风格
finalboolean isBold() //判断当前是否为粗体
finalboolean isItalic() //判断当前风格是否为斜体
复制代码
本类的常量静态定义,首先为字体类型名称
TypefaceDEFAULT
TypefaceDEFAULT_BOLD
TypefaceMONOSPACE
TypefaceSANS_SERIF
TypefaceSERIF
字体风格名称
intBOLD
intBOLD_ITALIC
intITALIC
intNORMAL
明天我们将在Android游戏开发六自定义View一文中具体讲解onDraw以及什么时候会触发绘制方法,来实现我们自定义或子类化控件。

Android游戏开发六自定义View
有关Android的自定义View的框架今天我们一起讨论下,对于常规的游戏,我们在View中需要处理以下几种问题:1.控制事件2.刷新View3.绘制View
1.对于控制事件今天我们只处理按键事件onKeyDown,以后的文章中将会讲到屏幕触控的具体处理onTouchEvent以及Sensor重力感应等方法。

2.刷新view的方法这里主要有invalidate(intl,intt,intr,intb) 刷新局部,四个参数分别为左、上、右、下。

整个view刷新invalidate(),刷新一个矩形区域invalidate(Rectdirty),刷新一个特性Drawable,invalidateDrawable(Drawabledrawable),执行invalidate类的方法将会设置view为无效,最终导致onDraw方法被重新调用。

由于今天的view比较简单,Android123提示大家如果在线程中刷新,除了使用handler方式外,可以在Thread中直接使用postInvalidate方法来实现。

3.绘制View主要是onDraw()中通过形参canvas来处理,相关的绘制主要有drawRect、drawLine、drawPath等等。

view方法内部还重写了很多接口,其回调方法可以帮助我们判断出view的位置和大小,比如
onMeasure(int,int)Calledtodeterminethesizerequirementsforthisviewand allofitschildren. 、
onLayout(boolean,int,int,int,int)Calledwhenthisviewshouldassignasize andpositiontoallofitschildren和
onSizeChanged(int,int,int,int)Calledwhenthesizeofthisviewhaschanged.具体的作用,大家可以用Logcat获取当view变化时每个形参的变动。

下面cwjView是我们为今后游戏设计的一个简单自定义View框架,我们可以看到在Android平台自定义view还是很简单的,同时Java支持多继承可以帮助我们不断的完善复杂的问题。

publicclasscwjViewextendsView{
publiccwjView(Contextcontext){
super(context);
setFocusable(true);//允许获得焦点
setFocusableInTouchMode(true);//获取焦点时允许触控
}
@Override
protectedParcelableonSaveInstanceState(){ //处理窗口保存事件ParcelablepSaved=super.onSaveInstanceState();
Bundlebundle=newBundle();
//dosomething
returnbundle;
}
@Override
protectedvoidonRestoreInstanceState(Parcelablestate){ //处理窗口还原事件
Bundlebundle=(Bundle)state;
//dosomething
super.onRestoreInstanceState(bundle.getParcelable("cwj"));
return;
}
@Override
protectedvoidonSizeChanged(intw,inth,intoldw,intoldh)//处理窗口大小变化事件
{
super.onSizeChanged(w,h,oldw,oldh);
}
@Override
protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec)
{
super.onMeasure(widthMeasureSpec,heightMeasureSpec);//如果不让父类处理记住调用setMeasuredDimension
}
@Override
protectedvoidonLayout(booleanchanged,intleft,inttop,intright,int bottom)
{
super.onLayout(changed,left,top,ight,bottom);
}
@Override
protectedvoidonDraw(Canvascanvas){
Paintbg=newPaint();
bg.setColor(Color.Red);
canvas.drawRect(0,0,getWidth()/2,getHeight()/2,bg);//将view的左上角四分之一填充为红色
}
@Override
publicbooleanonTouchEvent(MotionEventevent){
returnsuper.onTouchEvent(event);//让父类处理屏幕触控事件
}
@Override
publicbooleanonKeyDown(intkeyCode,KeyEventevent){//处理按键事件,响应的轨迹球事件为publicbooleanonTrackballEvent(MotionEventevent)
switch(keyCode){
caseKeyEvent.KEYCODE_DPAD_UP:
break;
caseKeyEvent.KEYCODE_DPAD_DOWN:
break;
caseKeyEvent.KEYCODE_DPAD_LEFT:
break;
caseKeyEvent.KEYCODE_DPAD_RIGHT:
break;
caseKeyEvent.KEYCODE_DPAD_CENTER://处理中键按下
break;
default:
returnsuper.onKeyDown(keyCode,event);
}
returntrue;
}
}
上面我们可以看到onMeasure使用的是父类的处理方法,如果我们需要解决自定义View的大小,可以尝试下面的方法
@Override
protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec)
{
height=View.MeasureSpec.getSize(heightMeasureSpec);
width=View.MeasureSpec.getSize(widthMeasureSpec);
setMeasuredDimension(width,height); //这里面是原始的大小,需要重新计算可以修改本行
//dosomething
}
Android游戏开发七自定义SurfaceView
今天我们说下未来的Android游戏引擎模板架构问题,对于游戏我们还是选择SurfaceView,相关的原因Android123已经在Android游戏开发二 View和SurfaceView中说的很清楚了,这里我们直接继承SurfaceView,实现SurfaceHolder.Callback接口,处理surfaceCreated、surfaceChanged以及surfaceDestroyed方法,这里我们并没有把按键控制传入,最终游戏的控制方面仍然由View内部类处理比较好,有关SurfaceView的具体我们可以参见Android开源项目的Camera中有关画面捕捉以及VideoView的控件实现大家可以清晰了解最终的用意。

publicclasscwjViewextendsSurfaceViewimplementsSurfaceHolder.Callback {
publiccwjView(Contextcontext,AttributeSetattrs){
super(context,attrs);
SurfaceHolderholder=getHolder();
holder.addCallback(this);
setFocusable(true);
}
publicvoidsurfaceChanged(SurfaceHolderholder,intformat,intwidth, intheight){
}
publicvoidsurfaceCreated(SurfaceHolderholder){
}
publicvoidsurfaceDestroyed(SurfaceHolderholder){
}
@Override
publicvoidonWindowFocusChanged(booleanhasWindowFocus){
}
}
Android游戏开发八SurfaceView类实例
有关SurfaceView我们将通过三个系统自带的例子来深入掌握Android绘图必会的SurfaceView,今天我们以SDK中的Sample游戏lunarlander中的LunarView具体实现,Android123建议大家导入该游戏工程到你的Eclipse然后自己编译先玩一下这个游戏,然后再看代码比较好理解。

classLunarViewextendsSurfaceViewimplementsSurfaceHolder.Callback{ classLunarThreadextendsThread{
/*
*Difficultysettingconstants
*/
publicstaticfinalintDIFFICULTY_EASY=0;
publicstaticfinalintDIFFICULTY_HARD=1;
publicstaticfinalintDIFFICULTY_MEDIUM=2;
/*
*Physicsconstants
*/
publicstaticfinalintPHYS_DOWN_ACCEL_SEC=35;
publicstaticfinalintPHYS_FIRE_ACCEL_SEC=80;
publicstaticfinalintPHYS_FUEL_INIT=60;
publicstaticfinalintPHYS_FUEL_MAX=100;
publicstaticfinalintPHYS_FUEL_SEC=10;
publicstaticfinalintPHYS_SLEW_SEC=120;//degrees/second rotate
publicstaticfinalintPHYS_SPEED_HYPERSPACE=180;
publicstaticfinalintPHYS_SPEED_INIT=30;
publicstaticfinalintPHYS_SPEED_MAX=120;
/*
*State-trackingconstants
*/
publicstaticfinalintSTATE_LOSE=1;
publicstaticfinalintSTATE_PAUSE=2;
publicstaticfinalintSTATE_READY=3;
publicstaticfinalintSTATE_RUNNING=4;
publicstaticfinalintSTATE_WIN=5;
/*
*Goalconditionconstants
*/
publicstaticfinalintTARGET_ANGLE=18;//>thisanglemeansc rash
publicstaticfinalintTARGET_BOTTOM_PADDING=17;//pxbelow gear
publicstaticfinalintTARGET_PAD_HEIGHT=8;//howhighabove ground
publicstaticfinalintTARGET_SPEED=28;//>thisspeedmeansc rash
publicstaticfinaldoubleTARGET_WIDTH=1.6;//widthoftarge t
/*
*UIconstants(i.e.thespeed&fuelbars)
*/
publicstaticfinalintUI_BAR=100;//widthofthebar(s)
publicstaticfinalintUI_BAR_HEIGHT=10;//heightofthebar( s)
privatestaticfinalStringKEY_DIFFICULTY="mDifficulty";
privatestaticfinalStringKEY_DX="mDX";
privatestaticfinalStringKEY_DY="mDY";
privatestaticfinalStringKEY_FUEL="mFuel";
privatestaticfinalStringKEY_GOAL_ANGLE="mGoalAngle";
privatestaticfinalStringKEY_GOAL_SPEED="mGoalSpeed";
privatestaticfinalStringKEY_GOAL_WIDTH="mGoalWidth";
privatestaticfinalStringKEY_GOAL_X="mGoalX";
privatestaticfinalStringKEY_HEADING="mHeading";
privatestaticfinalStringKEY_LANDER_HEIGHT="mLanderHeig ht";
privatestaticfinalStringKEY_LANDER_WIDTH="mLanderWidth ";
privatestaticfinalStringKEY_WINS="mWinsInARow";
privatestaticfinalStringKEY_X="mX";
privatestaticfinalStringKEY_Y="mY";
/*
*Member(state)fields
*/
/**Thedrawabletouseasthebackgroundoftheanimationcanvas */
privateBitmapmBackgroundImage;
/**
*Currentheightofthesurface/canvas.
*
*@see#setSurfaceSize
*/
privateintmCanvasHeight=1;
/**
*Currentwidthofthesurface/canvas.
*
*@see#setSurfaceSize
*/
privateintmCanvasWidth=1;
/**WhattodrawfortheLanderwhenithascrashed*/
privateDrawablemCrashedImage;
/**
*Currentdifficulty--amountoffuel,allowedangle,etc.De faultis
*MEDIUM.
*/
privateintmDifficulty;
/**Velocitydx.*/
privatedoublemDX;
/**Velocitydy.*/
privatedoublemDY;
/**Istheengineburning?*/
privatebooleanmEngineFiring;
/**WhattodrawfortheLanderwhentheengineisfiring*/
privateDrawablemFiringImage;
/**Fuelremaining*/
privatedoublemFuel;
/**Allowedangle.*/
privateintmGoalAngle;
/**Allowedspeed.*/
privateintmGoalSpeed;
/**Widthofthelandingpad.*/
privateintmGoalWidth;
/**Xofthelandingpad.*/
privateintmGoalX;
/**MessagehandlerusedbythreadtointeractwithTextView*/
privateHandlermHandler;
/**
*Landerheadingindegrees,with0up,90right.Keptintheran ge
*0..360.
*/
privatedoublemHeading;
/**Pixelheightoflanderimage.*/
privateintmLanderHeight;
/**WhattodrawfortheLanderinitsnormalstate*/
privateDrawablemLanderImage;
/**Pixelwidthoflanderimage.*/
privateintmLanderWidth;
/**Usedtofigureoutelapsedtimebetweenframes*/
privatelongmLastTime;
/**Painttodrawthelinesonscreen.*/
privatePaintmLinePaint;
/**"Bad"speed-too-highvariantofthelinecolor.*/
privatePaintmLinePaintBad;
/**Thestateofthegame.OneofREADY,RUNNING,PAUSE,LOSE,orW IN*/
privateintmMode;
/**Currentlyrotating,-1left,0none,1right.*/
privateintmRotating;
/**Indicatewhetherthesurfacehasbeencreated&isreadytodr aw*/
privatebooleanmRun=false;
/**Scratchrectobject.*/
privateRectFmScratchRect;
/**Handletothesurfacemanagerobjectweinteractwith*/
privateSurfaceHoldermSurfaceHolder;
/**Numberofwinsinarow.*/
privateintmWinsInARow;
/**Xoflandercenter.*/
privatedoublemX;
/**Yoflandercenter.*/
privatedoublemY;
publicLunarThread(SurfaceHoldersurfaceHolder,Contextco ntext,
Handlerhandler){
//gethandlestosomeimportantobjects
mSurfaceHolder=surfaceHolder;
mHandler=handler;
mContext=context;
Resourcesres=context.getResources();
//cachehandlestoourkeysprites&otherdrawables
mLanderImage=context.getResources().getDrawabl e(
nder_plain);
mFiringImage=context.getResources().getDrawabl e(
nder_firing);
mCrashedImage=context.getResources().getDrawab le(
nder_crashed);
//loadbackgroundimageasaBitmapinsteadofaDrawab leb/c
//wedon'tneedtotransformitandit'sfastertodrawt hisway
mBackgroundImage=BitmapFactory.decodeResource( res,
R.drawable.earthrise);
//Usetheregularlanderimageasthemodelsizeforall sprites
mLanderWidth=mLanderImage.getIntrinsicWidth();
mLanderHeight=mLanderImage.getIntrinsicHeight( );
//Initializepaintsforspeedometer
mLinePaint=newPaint();
mLinePaint.setAntiAlias(true);
mLinePaint.setARGB(255,0,255,0);
mLinePaintBad=newPaint();
mLinePaintBad.setAntiAlias(true);
mLinePaintBad.setARGB(255,120,180,0);
mScratchRect=newRectF(0,0,0,0);
mWinsInARow=0;
mDifficulty=DIFFICULTY_MEDIUM;
//initialshow-upoflander(notyetplaying)
mX=mLanderWidth;
mY=mLanderHeight*2;
mFuel=PHYS_FUEL_INIT;
mDX=0;
mDY=0;
mHeading=0;
mEngineFiring=true;
}
/**
*Startsthegame,settingparametersforthecurrentdifficu lty.
*/
publicvoiddoStart(){
synchronized(mSurfaceHolder){
//FirstsetthegameforMediumdifficulty
mFuel=PHYS_FUEL_INIT;
mEngineFiring=false;
mGoalWidth=(int)(mLanderWidth*TARGET_W IDTH);
mGoalSpeed=TARGET_SPEED;
mGoalAngle=TARGET_ANGLE;
intspeedInit=PHYS_SPEED_INIT;
//AdjustdifficultyparamsforEASY/HARD
if(mDifficulty==DIFFICULTY_EASY){
mFuel=mFuel*3/2;
mGoalWidth=mGoalWidth*4/3;
mGoalSpeed=mGoalSpeed*3/2;
mGoalAngle=mGoalAngle*4/3;
speedInit=speedInit*3/4;
}elseif(mDifficulty==DIFFICULTY_HARD){
mFuel=mFuel*7/8;
mGoalWidth=mGoalWidth*3/4;
mGoalSpeed=mGoalSpeed*7/8;
speedInit=speedInit*4/3;
}
//pickaconvenientinitiallocationforthe landersprite
mX=mCanvasWidth/2;
mY=mCanvasHeight-mLanderHeight/2;
//startwithalittlerandommotion
mDY=Math.random()*-speedInit;
mDX=Math.random()*2*speedInit-speedIni t;
mHeading=0;
//Figureinitialspotforlanding,nottoone arcenter
while(true){
mGoalX=(int)(Math.random()*(mC anvasWidth-mGoalWidth));
if(Math.abs(mGoalX-(mX-mLander Width/2))>mCanvasHeight/6)
break;
}
mLastTime=System.currentTimeMillis()+1 00;
setState(STATE_RUNNING);
}
}
/**
*Pausesthephysicsupdate&animation.
*/
publicvoidpause(){
synchronized(mSurfaceHolder){
if(mMode==STATE_RUNNING)setState(STATE _PAUSE);
}
}
/**
*RestoresgamestatefromtheindicatedBundle.Typicallyca lledwhen
*theActivityisbeingrestoredafterhavingbeenpreviously
*destroyed.
*
*@paramsavedStateBundlecontainingthegamestate
*/
publicsynchronizedvoidrestoreState(BundlesavedState){
synchronized(mSurfaceHolder){
setState(STATE_PAUSE);
mRotating=0;
mEngineFiring=false;
mDifficulty=savedState.getInt(KEY_DIFF ICULTY);
mX=savedState.getDouble(KEY_X);
mY=savedState.getDouble(KEY_Y);
mDX=savedState.getDouble(KEY_DX);
mDY=savedState.getDouble(KEY_DY);
mHeading=savedState.getDouble(KEY_HEAD ING);
mLanderWidth=savedState.getInt(KEY_LAN DER_WIDTH);
mLanderHeight=savedState.getInt(KEY_LA NDER_HEIGHT);
mGoalX=savedState.getInt(KEY_GOAL_X);
mGoalSpeed=savedState.getInt(KEY_GOAL_ SPEED);
mGoalAngle=savedState.getInt(KEY_GOAL_ ANGLE);
mGoalWidth=savedState.getInt(KEY_GOAL_ WIDTH);
mWinsInARow=savedState.getInt(KEY_WINS );
mFuel=savedState.getDouble(KEY_FUEL);
}
}
@Override
publicvoidrun(){
while(mRun){
Canvasc=null;
try{
c=mSurfaceHolder.lockCanvas(nu ll);
synchronized(mSurfaceHolder){
if(mMode==STATE_RUNNIN G)updatePhysics();
doDraw(c);
}
}finally{
//dothisinafinallysothatifanex ceptionisthrown
//duringtheabove,wedon'tleavet heSurfaceinan
//inconsistentstate
if(c!=null){
mSurfaceHolder.unlockC anvasAndPost(c);
}
}
}
}
/**
*DumpgamestatetotheprovidedBundle.Typicallycalledwhe nthe
*Activityisbeingsuspended.
*
*@returnBundlewiththisview'sstate
*/
publicBundlesaveState(Bundlemap){
synchronized(mSurfaceHolder){
if(map!=null){
map.putInt(KEY_DIFFICULTY,Inte ger.valueOf(mDifficulty));
map.putDouble(KEY_X,Double.val ueOf(mX));
map.putDouble(KEY_Y,Double.val ueOf(mY));
map.putDouble(KEY_DX,Double.va lueOf(mDX));
map.putDouble(KEY_DY,Double.va lueOf(mDY));
map.putDouble(KEY_HEADING,Doub le.valueOf(mHeading));
map.putInt(KEY_LANDER_WIDTH,In teger.valueOf(mLanderWidth));
map.putInt(KEY_LANDER_HEIGHT,I nteger
.valueOf(mLand erHeight));
map.putInt(KEY_GOAL_X,Integer. valueOf(mGoalX));
map.putInt(KEY_GOAL_SPEED,Inte ger.valueOf(mGoalSpeed));
map.putInt(KEY_GOAL_ANGLE,Inte ger.valueOf(mGoalAngle));
map.putInt(KEY_GOAL_WIDTH,Inte ger.valueOf(mGoalWidth));
map.putInt(KEY_WINS,Integer.va lueOf(mWinsInARow));
map.putDouble(KEY_FUEL,Double. valueOf(mFuel));
}
}
returnmap;
}
/**
*Setsthecurrentdifficulty.
*
*@paramdifficulty
*/
publicvoidsetDifficulty(intdifficulty){
synchronized(mSurfaceHolder){
mDifficulty=difficulty;
}
}
/**
*Setsiftheengineiscurrentlyfiring.
*/
publicvoidsetFiring(booleanfiring){
synchronized(mSurfaceHolder){
mEngineFiring=firing;
}
}
/**
*Usedtosignalthethreadwhetheritshouldberunningornot.
*Passingtrueallowsthethreadtorun;passingfalsewillshu tit
*downifit'salreadyrunning.Callingstart()afterthiswas most
*recentlycalledwithfalsewillresultinanimmediateshutd own.
*
*@parambtruetorun,falsetoshutdown
*/
publicvoidsetRunning(booleanb){
mRun=b;
}
/**
*Setsthegamemode.Thatis,whetherwearerunning,paused,i nthe
*failurestate,inthevictorystate,etc.
*
*@see#setState(int,CharSequence)
*@parammodeoneoftheSTATE_*constants
*/
publicvoidsetState(intmode){
synchronized(mSurfaceHolder){
setState(mode,null);
}
}
/**
*Setsthegamemode.Thatis,whetherwearerunning,paused,i nthe
*failurestate,inthevictorystate,etc.
*
*@parammodeoneoftheSTATE_*constants
*@parammessagestringtoaddtoscreenornull
*/
publicvoidsetState(intmode,CharSequencemessage){
/*
*Thismethodoptionallycancauseatextmessagetob edisplayed
*totheuserwhenthemodechanges.SincetheViewtha tactually
*rendersthattextispartofthemainViewhierarchy andnot
*ownedbythisthread,wecan'ttouchthestateoftha tView.
*InsteadweuseaMessage+Handlertorelaycommands tothemain
*thread,whichupdatestheuser-textView.
*/
synchronized(mSurfaceHolder){
mMode=mode;
if(mMode==STATE_RUNNING){
Messagemsg=mHandler.obtainMess age();
Bundleb=newBundle();
b.putString("text","");
b.putInt("viz",View.INVISIBLE) ;
msg.setData(b);
mHandler.sendMessage(msg);
}else{
mRotating=0;
mEngineFiring=false;
Resourcesres=mContext.getResou rces();
CharSequencestr="";
if(mMode==STATE_READY)。

相关文档
最新文档