1.整体思路的实现
飞机大战的类继承了SurfaceView,用了SurfaceHolder.Callback和Runnble接口,Runnable必须要有run方法,SurfaceHolder.Callback中定义了三个接口方法:
1:public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){}//Surface的大小发生改变时调用。
2: public void surfaceCreated(SurfaceHolder holder){}//Surface创建时激发,一般在这里调用画面的线程。
3: public void surfaceDestroyed(SurfaceHolder holder){}//销毁时激发,一般在这里将画面的线程停止、释放。
要想在屏幕上有东西,就必须要有canvas画布和paint画笔,要想能够屏幕上飞机有反应,就必须要有touchEvent方法,并且要return true,永远监听屏幕事件。
在绘制子弹的时候要用Vector数组,他和ArrayList的区别在于Vector是线程安全的。
首先我们要添加连续滚动的背景图片,其次添加我的飞机和敌人的飞机,然后再添加我的飞机的子弹和敌机的子弹,添加子弹音效与飞机爆炸的音效时,我们用了SoundPool。
2.如何绘制循环滚动的图片
第一步要把图片放到mipmap文件夹中,再转换格式。
用drawbitmap绘制两张一样的背景图片,第一张接着第二张滚动,当第二张的图片的y大于屏幕的高度时,第一张图片立马移到第二张图片的后面,循环播放即可。
public class BackGround {
private int y1;
private int y2;
private Bitmap bitmap;
public BackGround (Bitmap bitmap){
this.bitmap = bitmap;
y1 = 0;
y2 = bitmap.getHeight();
}
public void draw(Canvas canvas,Paint paint){
logic();
canvas.drawBitmap(bitmap,0,y1,paint);
canvas.drawBitmap(bitmap,0,y2,paint);
}
private void logic(){
y1+=5;
y2+=5;
if (y1>=MySurfaceView.height){
y1 = y2 - bitmap.getHeight();
}
if(y2>MySurfaceView.height){
y2 = y1-bitmap.getHeight();
}
}
}
4.如何绘制飞机
首先定义自己的飞机类,定义飞机的坐标和宽和高,还有飞机的血量,要想能够让飞机移动,旧的有touchEvent方法,并且不能让他移除屏幕
public void touchEvent(MotionEvent event){
if (event.getAction()==MotionEvent.ACTION_MOVE){
float ex = event.getX();
float ey = event.getY();
if (ex>x && ex<x+width&&ey>y&&ey<y+height){
x = (int) (ex - width/2);
y = (int) (ey - height/2);
if(y<0){
y=0;
}
if (y+height>MySurfaceView.height){
y=MySurfaceView.height-height;
}
}
}
}
飞机也要有无敌闪烁状态和无敌时间 public void draw(Canvas canvas,Paint paint){
if (noCollision){
noCollisionCount++;
if (noCollisionCount%10==0){
canvas.drawBitmap(bitmap,x,y,paint); //飞机闪烁
}
if (noCollisionCount>50){//无敌时间
noCollision = false;
noCollisionCount = 0;
}
}
else{
//非无敌状态
canvas.drawBitmap(bitmap,x,y,paint);
}
for (int i = 0;i<hp;i++){
canvas.drawBitmap(bitmapHp,i*bitmapHp.getWidth(),MySurfaceView.height-bitmapHp.getHeight(),paint);
}
}
public boolean isCollision(MyBullet myBullet){
if (noCollision){
return false;
}else {
if (myBullet.getX()>x&&myBullet.getX()<x+width&&myBullet.getY()>y&&myBullet.getY()<y+height){
noCollision = true;
if (hp > 0){
hp--;
}
return true;
}
}
return false;
}
public boolean isCollision(BossPlane bossPlane){
if (noCollision){
return false;
}else {
if(y > bossPlane.getY()+bossPlane.getFrameH()||y+height < bossPlane.getY()||x >bossPlane.getX()+bossPlane.getFrameW()||x+width < bossPlane.getX()){
}else {
noCollision = true;
if (hp > 0){
hp--;
}
return true;
}
}
bossplane和我的飞机同理,但是bossplane有个疯狂模式,一定事件后会俯冲,这时X不变,之变个Y,俯冲时有个阻尼效果,速度会慢慢下降,让后回到原点。 public void logic() {
count++;
if (isCrazy) {
//疯狂模式
y = y + crazySpeed;
crazySpeed--;
if (y == 0) {
isCrazy = false;
crazySpeed = 50;//阻尼效果
}
if (y > MySurfaceView.height - frameH) {
crazySpeed = -crazySpeed;
}
} else {
if (count % time == 0) {
isCrazy = true;
}
x = x + speed;
if (x > MySurfaceView.width - frameW) {
speed = -speed;
}
if (x < 0) {
speed = -speed;
}
}
}
4.如何绘制子弹
首先创建一个Vector数组,存放对象bullet。用Count来计算存放了多少bullet,当bullet到达屏幕尽头时,就让bullet消失,这样可以不过多占用内存
public void logic(){
switch(type){
case 0:
y-=speed;
if (y<0){
isDead = true;
}
break;
case 1:
y+=bSpeed+2;
if (y>MySurfaceView.height){
isDead = true;
}
break;
default:
break;
}
}
无论是我的飞机还是敌机都要赋予一个子弹的初始速度,并且不能让敌机的自当速度与屏幕滚动的速度一样。
private int speed = 18; private int bSpeed = 14;
子弹的图片也要用到Bitmap。
写敌机的子弹时不需要再创建一个类,可以用switch语句来写。
当写我的子弹时,要绘制子弹,移除子弹,判断子弹是否击中敌机
if (count % 20 == 0) {
MyBullet myBullet = new MyBullet(BitmapFactory.decodeResource(getResources(), R.mipmap.feidan), myPlane.getX(), myPlane.getY(), 0);
MyBullet myBullet2 = new MyBullet(BitmapFactory.decodeResource(getResources(), R.mipmap.feidan), myPlane.getX() + myPlane.getWidth(), myPlane.getY(), 0);
bulletVector.add(myBullet);
bulletVector.add(myBullet2);
gameSoundPool.playSound(1);
}
//移除消失的玩家子弹
for (int i = 0; i < bulletVector.size(); i++) {
if (bulletVector.elementAt(i).isDead()) {
bulletVector.remove(i);
}
}
//绘制玩家子弹
for (int i = 0; i < bulletVector.size(); i++) {
bulletVector.elementAt(i).draw(canvas, paint);
if (bossPlane.isCollision(bulletVector.elementAt(i))) { //判断我的子弹是否击中boss飞机,击中就画一张boom图
Boom boom = new Boom(BitmapFactory.decodeResource(getResources(), R.mipmap.boom), bossPlane.getX(), bossPlane.getY(), 7);
boomVector.add(boom);
gameSoundPool.playSound(2);
}
}
boss的子弹也是同理 if (count % 50 == 0) {
MyBullet myBullet3 = new MyBullet(BitmapFactory.decodeResource(getResources(), R.mipmap.bossbullet), bossPlane.getX(), bossPlane.getY() + bossPlane.getFrameH(), 1);
MyBullet myBullet4 = new MyBullet(BitmapFactory.decodeResource(getResources(), R.mipmap.bossbullet), bossPlane.getX() + bossPlane.getFrameW(), bossPlane.getY() + bossPlane.getFrameH(), 1);
bossBulletVector.add(myBullet3);
bossBulletVector.add(myBullet4);
}
//移除消失的Boss子弹
for (int i = 0; i < bossBulletVector.size(); i++) {
if (bossBulletVector.elementAt(i).isDead()) {
bossBulletVector.remove(i);
}
}
//绘制boss子弹
for (int i = 0; i < bossBulletVector.size(); i++) {
bossBulletVector.elementAt(i).draw(canvas, paint);
myPlane.isCollision(bossBulletVector.elementAt(i)); //判断boss子弹是否击中我的飞机
}
myPlane.isCollision(bossPlane);//判断我的飞机是否和boss飞机碰撞
5如何判断碰撞
还要判断我的飞机是否与敌机碰撞。
if (x+bitmap1.getWidth()<bossPlane.getX()||x>bossPlane.getX()+bossPlane.getFrameW()||
y>bossPlane.getY()+bossPlane.getFrameH()||y+bitmap1.getHeight()<bossPlane.getY()){
return false;
}else {
hp--;
noCollsion = true;
return true;
}
6.如何绘制爆炸效果
爆炸效果也要用Vector数组,爆炸的图片有好几帧,如何让他们衔接起来是难点,首先要将他们进行剪裁,剪成七个,也就是七帧,也要创建一个count对象来计数
public void draw(Canvas canvas, Paint paint){
canvas.save();
canvas.clipRect(x,y,x+frameW,y+frameH);
canvas.drawBitmap(bitmap,x-currentFrame*frameW,y,paint);
canvas.restore();
logic();
}
public void logic(){
if (currentFrame<totalFrame){
currentFrame++;
}else {
isEnd = true;
}
}
7.如何添加音效
添加音效时要先建造一个GameSoundPool类,定义一个SoundPool,定义一个对象,给他初始值,音乐文件,soundpool.play播放
public class GameSoundPool {
private SoundPool soundPool;
private int s1,s2,s3;
public GameSoundPool(Context context){
this.soundPool = new SoundPool(2, AudioManager.STREAM_MUSIC,0);
s1 = soundPool.load(context,R.raw.shoot,1);
s2 = soundPool.load(context,R.raw.explosion2,1);
s3 = soundPool.load(context,R.raw.bgm_zhandou2,1);
}
public void playSound(int s){
switch(s){
case 1:
soundPool.play(s1,1,1,1,0,1.0f);
break;
case 2:
soundPool.play(s2,1,1,1,0,1.0f);
break;
soundPool.play(s3,1,1,1,0,1.0f);
}
}
}
soundpool.play里有左右声道和音量大小。
8.那些地方用到封装,继承,多态,方法重载,接口。
所有类中都用到了封装
public class MySurfaceView extends SurfaceView用到了继承
没有多态
没有方法重载
SurfaceHolder.Callback,与Runnable用到了接口
9.我的心得
这两个礼拜学习了飞机大战,主要目的并不是你要会做,而是要理解前两个礼拜学的java基础知识,这个项目是对java知识的一种汇总,通过这两个礼拜的学习,我java的知识得到了一定的提高。这个飞机项目非常锻炼我的逻辑思维能力,做项目前一定要理清思路,我们的java老师非常的负责人,我们同学有任何问题他都会帮助我们,是一个十分经责和蔼的老师。