算是第一个写的比较满意的游戏吧,肯定还有很多很多不足,希望高手能指点下,呵。现在在看图灵出版的一本android游戏的书籍,有点难,涉及底层有点多,都是JNI、NDK、linux shell脚本等,主要是将怎么将c(++)平台的游戏移植到android平台下。啥时候俺能移植了就牛X了。
游戏总介:
重力感应,480*320分辨率,设计为3关,没用到游戏引擎,含有开始界面(开始、退出、声音、说明),游戏情节设计花费了很长时间,想尽量做到每一关都不同,游戏介绍:
游戏素材:玩家——中国歼十战斗机,敌机——日本二战期间飞机(自己PS了半天);
敌机种类:1.向玩家飞机移动
2.随机个数、随机路径(8种)移动
3.只向Y方向移动,模拟碉堡,散弹,打掉后出现换子弹精灵(2上子弹、3上子弹、5上子弹、螺旋向上子弹);
加血设定:设定加血精灵,每关出现由加血精灵排列而成的C、H、E、n、S、H、I、q、I、A、n、g的字母(ChenShiqiang)即为加血的,每一个加血精灵与玩家碰撞玩家血值加一,满100玩家生命数加一,生命数<=5;
关卡设定:每一关背景图片、背景音乐、敌机出现的频率、敌机生命值、敌机路径、boss生命值、加血字母出现频率、boss出现时间等设置不同,以boss死亡为通关标志(boss出现到死亡期间以vibrator模拟心跳效果),每一关通过都显示得分界面再进入下一关;
为了便于修改,屏幕分辨率、背景滚动的速度、GameView刷新频率、飞行路径等静态常量都放一个工具类里面。
游戏截图:
0、开始界面:
1、第一关
2、第二关
3、第三关
任务成功的图片就不传了,玩一盘游戏太费时了,玩不起啊玩不起。
游戏模块介绍:
重力感应控制:
SensorManager mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
Sensor mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
注册:
mRegistedSensor = mSensorManager.registerListener(MainActivity.this, mSensor, SensorManager.SENSOR_DELAY_GAME);
取消注册:
if(mRegistedSensor)
{
mSensorManager.unregisterListener(this);
mRegistedSensor = false;
}
控制:(倾斜角度为ConstantUtil.accuracy有效)
心跳效果:
Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
long[] pattern = {800, 40,400, 30}; // OFF/ON/OFF/ON...
vibrator.vibrate(pattern, 2);//-1不重复,非-1为从pattern的指定下标开始重复
取消:
if(null!=vibrator)
{
vibrator.cancel();
}
界面切换:在Activity的onCreate方法里定义一个Handler来接受界面变换消息。
游戏声音:
背景音乐用MediaPlayer,
游戏各种音效用SoundPool(小容量(<1M),多个音频同时播放)
背景图片绘制:
int hight1 = 0;
int hight2 = -ConstantUtil.screenheight;
int w = ConstantUtil.screenweith;
int h = ConstantUtil.screenheight;
//背景循环显示,在GameView的Draw()方法里,不断刷新移动
Rect rectsrc = new Rect(0, 0, w, h);
Rect rectdst1 = new Rect(0, hight1, w, h+hight1);
Rect rectdst2 = new Rect(0, hight2, w, h+hight2);
canvas.drawBitmap(background, rectsrc, rectdst1, null);
canvas.drawBitmap(background, rectsrc, rectdst2, null);
hight1+=ConstantUtil.bgspan;
hight2+=ConstantUtil.bgspan;
if(hight1>h)
{
hight1=-h;
}
if(hight2>h)
{
hight2=-h;
}
碰撞检测:
//判断两个矩形是否碰撞,根据矩形重叠来判断
private boolean isContain(int otherX, int otherY, int otherWidth, int otherHeight){//判断两个矩形是否碰撞
int xd = 0;//大的x
int yd = 0;//大大y
int xx = 0;//小的x
int yx = 0;//小的y
int width = 0;
int height = 0;
boolean xFlag = true;//玩家飞机x是否在前
boolean yFlag = true;//玩家飞机y是否在前
if(this.x >= otherX){
xd = this.x;
xx = otherX;
xFlag = false;
}else{
xd = otherX;
xx = this.x;
xFlag = true;
}
if(this.y >= otherY){
yd = this.y;
yx = otherY;
yFlag = false;
}else{
yd = otherY;
yx = this.y;
yFlag = true;
}
if(xFlag == true){
width = this.bitmap.getWidth();
}else {
width = otherWidth;
}
if(yFlag == true){
height = this.bitmap.getHeight();
}else{
height = otherHeight;
}
if(xd>=xx&&xd<=xx+width-1&&
yd>=yx&&yd<=yx+height-1){//首先判断两个矩形有否重叠
double Dwidth=width-xd+xx; //重叠区域宽度
double Dheight=height-yd+yx; //重叠区域高度
if(Dwidth*Dheight/(otherWidth*otherHeight)>=0.20){//重叠面积超20%则判定为碰撞
return true;
}
}
return false;
}
例:判断玩家是否与敌机碰撞
public boolean contain(EnemyPlane ep) //敌机
{
if(isContain(ep.x, ep.y, ep.bitmap.getWidth(), ep.bitmap.getHeight()))
{//检测成功
blood-=20;//自己的生命减20
return true;
}
return false;
}
子弹类:构造函数里包含x位置, y位置, 子弹的类型,图片type, 子弹方向dir,以构造所有子弹
螺旋子弹实现:定义一段路径,轨迹片段通过sin(x)实现。想了半天,似乎没有回旋的函数,只能这样了。
public static double[][] arcpath =
{ {Math.sin(Math.PI/16)*50,Math.sin(Math.PI*2/16)*50,Math.sin(Math.PI*3/16)*50,Math.sin(Math.PI*4/16)*50,Math.sin(Math.PI*5/16)*50,Math.sin(Math.PI*6/16)*50,Math.sin(Math.PI*7/16)*50,Math.sin(Math.PI*8/16)*50,Math.sin(Math.PI*9/16)*50,Math.sin(Math.PI*10/16)*50,Math.sin(Math.PI*11/16)*50,Math.sin(Math.PI*12/16)*50,Math.sin(Math.PI*13/16)*50,Math.sin(Math.PI*14/16)*50,Math.sin(Math.PI*15/16)*50,Math.sin(Math.PI*16/16)*50,Math.sin(Math.PI*18/16)*50,Math.sin(Math.PI*20/16)*50,Math.sin(Math.PI*22/16)*50,Math.sin(Math.PI*24/16)*50,Math.sin(Math.PI*26/16)*50,Math.sin(Math.PI*28/16)*50,Math.sin(Math.PI*30/16)*50,Math.sin(Math.PI*32/16)*50},
{10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,-10,-10,-10,-10,-10,-10,-10,-10},
};
加血模块:思路是定义各个字母数组以存储各个加血精灵的位置,到指定时间生成字母加血精灵组,然后整体向下移动。
敌机随机路径实现:定义数组存储路径,数组中存放路径中每个“站点”的步数,生成第二种敌机时可指定敌机从第几点开始移动。
//飞行路径
public static int[][] pathA = //右左
{
{-120,-80, -40, 0, 320, -30},//路径中每个点的X坐标
{-10, 0 , 30, 30, 200, 350},//路径中每个点的Y坐标
{7 , 7 , 7, 40, 40, 0},//路径中两点间的步数
};
public static int[][] pathB = //左右
{
{420,400, 360, 320, -20, 350},//路径中每个点的X坐标
{-10,0 , 15, 30, 200, 350},//路径中每个点的Y坐标
{7 , 7 , 7, 40, 40, 0},//路径中两点间的步数
};
public static int[][] pathC = //左
{
{420,400, 360, 320, -20},//路径中每个点的X坐标
{-10, 0 , 15, 30, 450},//路径中每个点的Y坐标
{7 , 7 , 7, 80, 0},//路径中两点间的步数
};
public static int[][] pathD = //右
{
{-50 ,-40, -30, -20, 320},//路径中每个点的X坐标
{-10 , 0 , 15, 30, 450},//路径中每个点的Y坐标
{7 , 7 , 7 , 80, 0 },//路径中两点间的步数
};
public static int[][] pathE = //右左右
{
{-100,-80, -40, 0, 320, -30 ,320},//路径中每个点的X坐标
{ -10 ,0 , 30, 30, 245, 245 ,400},//路径中每个点的Y坐标
{ 7 ,7 , 7, 40, 40, 40 , 0 },//路径中两点间的步数
};
public static int[][] pathF = //左右左
{
{400 ,360, 340, 320, -20, 320 ,-20},//路径中每个点的X坐标
{-10 , 0 , 30, 30, 245, 245 ,400},//路径中每个点的Y坐标
{7 , 7 , 7, 40, 40, 40 , 0 },//路径中两点间的步数
};
在敌机EnemyPlane构造函数中有指定第几种敌机、指定路径和路径中起始点的参数。
算了,其余的程序全部贴上吧:

ConstantUtil.java
WelcomeView.java
GameView.java
LevelView.java
FailView.java
MyPlane.java
EnemyPlane.java
ChangeBullet.java
Addblood.java
Boss.java
重力感应控制:
SensorManager mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
Sensor mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
注册:
mRegistedSensor = mSensorManager.registerListener(MainActivity.this, mSensor, SensorManager.SENSOR_DELAY_GAME);
取消注册:
if(mRegistedSensor)
{
mSensorManager.unregisterListener(this);
mRegistedSensor = false;
}
控制:(倾斜角度为ConstantUtil.accuracy有效)
心跳效果:
Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
long[] pattern = {800, 40,400, 30}; // OFF/ON/OFF/ON...
vibrator.vibrate(pattern, 2);//-1不重复,非-1为从pattern的指定下标开始重复
取消:
if(null!=vibrator)
{
vibrator.cancel();
}
界面切换:在Activity的onCreate方法里定义一个Handler来接受界面变换消息。
游戏声音:
背景音乐用MediaPlayer,
游戏各种音效用SoundPool(小容量(<1M),多个音频同时播放)
背景图片绘制:
int hight1 = 0;
int hight2 = -ConstantUtil.screenheight;
int w = ConstantUtil.screenweith;
int h = ConstantUtil.screenheight;
//背景循环显示,在GameView的Draw()方法里,不断刷新移动
Rect rectsrc = new Rect(0, 0, w, h);
Rect rectdst1 = new Rect(0, hight1, w, h+hight1);
Rect rectdst2 = new Rect(0, hight2, w, h+hight2);
canvas.drawBitmap(background, rectsrc, rectdst1, null);
canvas.drawBitmap(background, rectsrc, rectdst2, null);
hight1+=ConstantUtil.bgspan;
hight2+=ConstantUtil.bgspan;
if(hight1>h)
{
hight1=-h;
}
if(hight2>h)
{
hight2=-h;
}
碰撞检测:
//判断两个矩形是否碰撞,根据矩形重叠来判断
private boolean isContain(int otherX, int otherY, int otherWidth, int otherHeight){//判断两个矩形是否碰撞
int xd = 0;//大的x
int yd = 0;//大大y
int xx = 0;//小的x
int yx = 0;//小的y
int width = 0;
int height = 0;
boolean xFlag = true;//玩家飞机x是否在前
boolean yFlag = true;//玩家飞机y是否在前
if(this.x >= otherX){
xd = this.x;
xx = otherX;
xFlag = false;
}else{
xd = otherX;
xx = this.x;
xFlag = true;
}
if(this.y >= otherY){
yd = this.y;
yx = otherY;
yFlag = false;
}else{
yd = otherY;
yx = this.y;
yFlag = true;
}
if(xFlag == true){
width = this.bitmap.getWidth();
}else {
width = otherWidth;
}
if(yFlag == true){
height = this.bitmap.getHeight();
}else{
height = otherHeight;
}
if(xd>=xx&&xd<=xx+width-1&&
yd>=yx&&yd<=yx+height-1){//首先判断两个矩形有否重叠
double Dwidth=width-xd+xx; //重叠区域宽度
double Dheight=height-yd+yx; //重叠区域高度
if(Dwidth*Dheight/(otherWidth*otherHeight)>=0.20){//重叠面积超20%则判定为碰撞
return true;
}
}
return false;
}
例:判断玩家是否与敌机碰撞
public boolean contain(EnemyPlane ep) //敌机
{
if(isContain(ep.x, ep.y, ep.bitmap.getWidth(), ep.bitmap.getHeight()))
{//检测成功
blood-=20;//自己的生命减20
return true;
}
return false;
}
子弹类:构造函数里包含x位置, y位置, 子弹的类型,图片type, 子弹方向dir,以构造所有子弹
螺旋子弹实现:定义一段路径,轨迹片段通过sin(x)实现。想了半天,似乎没有回旋的函数,只能这样了。
public static double[][] arcpath =
{ {Math.sin(Math.PI/16)*50,Math.sin(Math.PI*2/16)*50,Math.sin(Math.PI*3/16)*50,Math.sin(Math.PI*4/16)*50,Math.sin(Math.PI*5/16)*50,Math.sin(Math.PI*6/16)*50,Math.sin(Math.PI*7/16)*50,Math.sin(Math.PI*8/16)*50,Math.sin(Math.PI*9/16)*50,Math.sin(Math.PI*10/16)*50,Math.sin(Math.PI*11/16)*50,Math.sin(Math.PI*12/16)*50,Math.sin(Math.PI*13/16)*50,Math.sin(Math.PI*14/16)*50,Math.sin(Math.PI*15/16)*50,Math.sin(Math.PI*16/16)*50,Math.sin(Math.PI*18/16)*50,Math.sin(Math.PI*20/16)*50,Math.sin(Math.PI*22/16)*50,Math.sin(Math.PI*24/16)*50,Math.sin(Math.PI*26/16)*50,Math.sin(Math.PI*28/16)*50,Math.sin(Math.PI*30/16)*50,Math.sin(Math.PI*32/16)*50},
{10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,-10,-10,-10,-10,-10,-10,-10,-10},
};
加血模块:思路是定义各个字母数组以存储各个加血精灵的位置,到指定时间生成字母加血精灵组,然后整体向下移动。
敌机随机路径实现:定义数组存储路径,数组中存放路径中每个“站点”的步数,生成第二种敌机时可指定敌机从第几点开始移动。
//飞行路径
public static int[][] pathA = //右左
{
{-120,-80, -40, 0, 320, -30},//路径中每个点的X坐标
{-10, 0 , 30, 30, 200, 350},//路径中每个点的Y坐标
{7 , 7 , 7, 40, 40, 0},//路径中两点间的步数
};
public static int[][] pathB = //左右
{
{420,400, 360, 320, -20, 350},//路径中每个点的X坐标
{-10,0 , 15, 30, 200, 350},//路径中每个点的Y坐标
{7 , 7 , 7, 40, 40, 0},//路径中两点间的步数
};
public static int[][] pathC = //左
{
{420,400, 360, 320, -20},//路径中每个点的X坐标
{-10, 0 , 15, 30, 450},//路径中每个点的Y坐标
{7 , 7 , 7, 80, 0},//路径中两点间的步数
};
public static int[][] pathD = //右
{
{-50 ,-40, -30, -20, 320},//路径中每个点的X坐标
{-10 , 0 , 15, 30, 450},//路径中每个点的Y坐标
{7 , 7 , 7 , 80, 0 },//路径中两点间的步数
};
public static int[][] pathE = //右左右
{
{-100,-80, -40, 0, 320, -30 ,320},//路径中每个点的X坐标
{ -10 ,0 , 30, 30, 245, 245 ,400},//路径中每个点的Y坐标
{ 7 ,7 , 7, 40, 40, 40 , 0 },//路径中两点间的步数
};
public static int[][] pathF = //左右左
{
{400 ,360, 340, 320, -20, 320 ,-20},//路径中每个点的X坐标
{-10 , 0 , 30, 30, 245, 245 ,400},//路径中每个点的Y坐标
{7 , 7 , 7, 40, 40, 40 , 0 },//路径中两点间的步数
};
在敌机EnemyPlane构造函数中有指定第几种敌机、指定路径和路径中起始点的参数。
算了,其余的程序全部贴上吧:

ConstantUtil.java
WelcomeView.java
GameView.java
LevelView.java
FailView.java
MyPlane.java
EnemyPlane.java
ChangeBullet.java
Addblood.java
Boss.java
