Android 飞机大战详解与感悟

13 篇文章 0 订阅
6 篇文章 0 订阅

一.飞机大战的整体思路:

飞机大战的主要使用的方法大纲:

1.概略:

Android的飞机大战用的是SurfaceView()来写,那么首先我们要继承SurfaceView这个类,然后我们还需要用多线程的来运行,那么还要实现Runnable以及SurfaceHolder.CallBack接口。实现多线程就必须要用到run()方法。
由于飞机大战的大部分工作是需要画图片到app里,那么我们又会用到Canvas这个类,这个类是画布类,所有的图片都需要画在Canvas的对象上。
然后,我们需要把图片转换成Bitmap格式。
那么,总结一下:飞机大战使用的最重要的一些类,接口和方法分别是
SurfaceView();Runnable;SurfaceHolder.CallBack;Canvas;Bitmap以及run()方法。

2.规划大概要写的类以及要实现的效果

(1)进入界面的类(Start):

构建一个Start类,这个界面上会有三张图片:背景图,logo图,开始游戏的按钮图。
那么,我们就要定义三张图片的X,Y坐标变量,还有Bitmap变量,这些变量都用private进行修饰,这是用了Java的封装,提高了代码的安全性。
然后,开始游戏按钮需要点击屏幕然后进入游戏界面,这里就用到了onTouchEvent()这个方法,那么我们就需要在SurfaceView的子类里复写这个方法,然后把start类的touchEvent()方法调用过去。

(2)进入游戏界面(需要多个类)

在进入到游戏界面时,我们需要建背景类(Background)我的飞机类(MyPlan)
boss飞机类(BossPlan)子弹类(MyBullet)爆炸类(Boom)
音效类(GameSoundPool) ,一共6个类。
首先,游戏界面的背景需要滚动,那么要在背景类(Background)将图片进行无限的循环
在游戏界面,我们要移动自己的飞机和Boss飞机对战,那么自己飞机就需要可移动,那么同样要在MyPlan类里写一个touchEvent()方法。
boss飞机会进入疯狂模式,那么要在boss飞机类(BossPlan)写一个方法来判断它什么时候进入这个模式以及持续的时间。
子弹发射出去有它的速度和频率,那么在子弹类(MyBullet)里要对这些进行判断。
子弹击中飞机以及飞机撞到飞机会产生爆炸效果,我们需要在MyPlanBossPlan类里分别判断飞机是否被击中或者是否被撞到。
子弹发射会有声音,以及爆炸时的爆炸声,这时我们要在音效类(GameSoundPool)里使用load()方法调用音效,然后再用play()方法播放。在使用音效前要先实例化SoundPool的对象,SoundPool是返回一个int类型的值,那么要先int一个整数来接受它,这些变量的声明都用private修饰

3.分步进行详细操作

(1)如何绘制循环滚动背景图片

当第一张图片的y轴大于屏幕的高度时,说明第一张图片滚动结束了,用第二张图片的y轴减去图片高度,这时第一张图片下一次的y轴就在这。
滚动逻辑代码:

public void logic() {
        y1 += 3;
        y2 += 3;
        if (y1 > MySurfaceView.height) {
            y1 = y2 - bitmap1.getHeight();  //当y轴大于手机屏幕高度,下一张图的y轴减去图片高度,第一张图片下一次的y轴就在这
        }
        if (y2 > MySurfaceView.height) {
            y2 = y1 - bitmap1.getHeight();
        }
    }
(2)如何绘制飞机

绘制飞机的图片用Canvas的对象调用draw()方法就行了,由于飞机需要根据手指移动,那么就要判断当手指的坐标在飞机内移动,飞机才会跟随移动,否则飞机不动。
触摸事件就要用toTouchEveny()方法来写,boss飞机有疯狂模式,要定义一个计数器来判断它进入疯狂模式的时间
触摸事件逻辑代码:

   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;//起始坐标(0,0)在左上角,移动的话会向反方向,把起始坐标调整到它中间
                y = (int) ey - height / 2;
                if (y < 0) {
                    y = 0;
                }
                if (y > MySurfaceView.height - height) {
                    y = MySurfaceView.height - height;
                }
                if (x < 0) {
                    x = 0;
                }
                if (x > MySurfaceView.width - width) {
                    x = MySurfaceView.width - width;
                }
            }
        }
    }

判断boss飞机是否进入疯狂模式代码

private int count;             //疯狂模式计数器
private int crazySpeed = 45;   //疯狂模式速读
private int time = 200;        //疯狂模式时间  
private boolean isCrazy;       //是否进入疯狂模式

public void logic(){
          count++;
          if(isCrazy){                        //判断boss飞机是否进入疯狂模式
               y = y+crazySpeed;
               crazySpeed--;
               if(y==0){
                   isCrazy = false;
                   crazySpeed = 50;
               }
          }else{
              if(count%time==0){
                  isCrazy = true;
              }

              x = x+speed;
              if(x < 0){
                  speed = -speed;
              }
              if(x > MySurfaceView.width - oneW){
                  speed = -speed;
              }
          }
    }
(3)如何绘制子弹

子弹是连续发射的,那么不光要将它画出来,在MysurfaceView里面还要定义两个Vector数组,分别用来添加我的飞机和Boss飞机射出的子弹,将每一发子弹都add进去,然后将子弹画到界面里,而且要判断子弹是否飞出界面,一旦飞出界面,立马将它删除
定义数组代码:

 private Vector<MyBullet> bulletVector = new Vector<>();             //用Vector数组定义一个子弹类数组
 private Vector<MyBullet> bossBulletVector = new Vector<>();       //用Vector数组定义一个boss子弹类数组

绘制boss子弹代码:

if (count % 50 == 0) {
                            MyBullet bossBullet = new MyBullet(BitmapFactory.decodeResource(getResources(), R.mipmap.bossbullet), bossPlane.getX(), bossPlane.getY() + bossPlane.getOneH(), 1);
                            MyBullet bossBullet1 = new MyBullet(BitmapFactory.decodeResource(getResources(), R.mipmap.bossbullet), bossPlane.getX() + bossPlane.getOneW(), bossPlane.getY() + bossPlane.getOneH(), 1);
                            bossBulletVector.add(bossBullet);           //把boss子弹对象加到boss子弹数组里
                            bossBulletVector.add(bossBullet1);
                        }
 for (int i = 0; i < bossBulletVector.size(); i++) {
      bossBulletVector.elementAt(i).draw(canvas, paint);
      myPlane.isCollision(bossBulletVector.elementAt(i));
}

绘制我的子弹代码:

if (count % 10 == 0) {
                            MyBullet myBullet = new MyBullet(BitmapFactory.decodeResource(getResources(), R.mipmap.mybullet), myPlane.getX(), myPlane.getY(), 0);
                            MyBullet myBullet1 = new MyBullet(BitmapFactory.decodeResource(getResources(), R.mipmap.mybullet), myPlane.getX() + myPlane.getWidth(), myPlane.getY(), 0);
                            bulletVector.add(myBullet);
                            bulletVector.add(myBullet1);
                            gameSoundPool.playSound(1);//添加射击音效
                        }
for (int i = 0; i < bulletVector.size(); i++) {
                            bulletVector.elementAt(i).draw(canvas, paint);
                            if (bossPlane.isCollision(bulletVector.elementAt(i))){      //判断我的子弹是否击中boss飞机,击中就画一张爆炸图
                                Boom boom = new Boom(BitmapFactory.decodeResource(getResources(),R.mipmap.boom),bossPlane.getX(),bossPlane.getY(),7);
                                boomVector.add(boom);
                                gameSoundPool.playSound(2); //在爆炸时添加爆炸音效
                            }
                        }                        

移除子弹代码

//移除消失的boss子弹
                        for (int i = 0; i < bossBulletVector.size(); i++) {
                            if (bossBulletVector.elementAt(i).isDead()) {
                                bossBulletVector.remove(i);
                            }
                        }
//移除消失我的的子弹
                        for (int i = 0; i < bulletVector.size(); i++) {
                            if (bulletVector.elementAt(i).isDead()) {
                                bulletVector.remove(i);
                            }
                        }

判断子弹是否超出边界代码

public void logic(){
        switch (temp){
            case 0:
                y -= speed + 3;               //我的子弹的坐标
                if(,y < 0){                //对我的子弹是否超过屏幕边界判断
                    isDead = true;
                }
                break;
            case 1:
                y += Bspeed + 3;                       //boss子弹的坐标
                if(y > MySurfaceView.height){         //对boss子弹是否超过屏幕边界判断
                    isDead = true;
                }
                break;
        }
    }
(4)如何判断碰撞(子弹与飞机碰撞,飞机与飞机碰撞)

我的飞机如果受到了碰撞,那么会进入一段时间的无敌状态,要先定义一个boolean值,然后进行判断,当碰撞到时,让飞机闪烁,不然就不闪烁,这里要定义一个计数器
判断飞机是否碰撞也是先定义一个Boolean值,然后对坐标是否重合进行判断,
判断我的飞机是否是无敌时间代码:

private boolean noCollision;    //判断是否进入无敌时间
private int noCollisionCount;  //无敌时间计数

    public void draw(Canvas canvas, Paint paint) {
        if (hp <= 0){
            MySurfaceView.GameStart = 3;
        }
        if (noCollision) {
            noCollisionCount++;
            if (noCollisionCount % 10 == 0) {
                canvas.drawBitmap(bitmap, x, y, paint);    //飞机闪烁
            }
            if (noCollisionCount > 100) {         //无敌时间
                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);
        }
    }

判断我的飞机是否被碰撞代码

/**
     * 判断子弹是否击中飞机
     *
     * @param
     * @return
     */
    public boolean isCollision(MyBullet bullet) {
        if (noCollision) {
            return false;
        } else {
            if (bullet.getX() > x && bullet.getX() < x + width && bullet.getY() > y && bullet.getY() < y + height) {
                noCollision = true;
                if (hp > 0) {
                    hp--;
                }
                return true;
            }
        }
        return false;
    }

/**
     * 判断我的飞机是否撞上boss飞机
     */
    public boolean isCollision(BossPlane bossPlane) {
        if (noCollision){
            return false;
        }else {
            if(y > bossPlane.getY()+bossPlane.getOneH()||y+height  < bossPlane.getY()||x+width < bossPlane.getX()||x >bossPlane.getX()+bossPlane.getOneW()){

            }else {
                noCollision = true;
                if (hp>0){
                    hp--;
                }
                return true;
            }
        }
        return false;
    }

判断boss飞机是否被我的子弹击中代码

public boolean isCollision(MyBullet bullet){
        if (bullet.getX() > x&&bullet.getX()+bullet.getBitmap().getWidth() < x+oneW&&bullet.getY() > y&&bullet.getY() < y+oneH){
            bossHp--;
            bullet.setDead(true);
            if (bossHp < 0){
                MySurfaceView.GameStart = 2;
            }
            return true;
        }
       return false;
    }
(5)如何绘制爆炸效果

首先,爆炸的图片不止一帧,要一帧一帧依次播放,先要定义两个整数 帧数和播放第几帧,
然后再用Vector数组实例化一个爆炸数组,把一帧一帧的图片add进去,然后在绘制我的子弹时,进行判断,如果我的子弹击中boss飞机,那么就将爆炸数组里的图片画一次,然后将结束爆炸的图删去。
显示爆炸图片的逻辑代码

public void logic() {
        if (currentFrame < totalFrame) {
            currentFrame++;
        } else {
            isEnd = true;
        }
    }

定义爆炸数组

private Vector<Boom>boomVector = new Vector<>();                //爆炸图的数组,显示图片的每一帧画面

判断我的子弹是否击中boss飞机

if (bossPlane.isCollision(bulletVector.elementAt(i))){      //判断我的子弹是否击中boss飞机,击中就画一张爆炸图
                                Boom boom = new Boom(BitmapFactory.decodeResource(getResources(),R.mipmap.boom),bossPlane.getX(),bossPlane.getY(),7);
                                boomVector.add(boom);
                                                            }
(6)如何添加音效

这里音效有两种,一种是游戏内的子弹声和爆炸声,还有一种是背景音乐,游戏内声音用的是
SoundPool来写,背景音乐用MediaPlayer写。游戏内的音乐建一个类来写,
先用load方法添加,再用play方法播放,最后用这个类的对象来调用play方法,再将它插入需要播放的地方,
背景音乐直接在 主类的run方法里写。
游戏内音效代码

import android.content.Context;
import android.media.AudioManager;
import android.media.SoundPool;

public class GameSoundPool {
    private SoundPool soundPool;
    private int s1;
    private int s2;
    private int 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.explosion3,1);
        s3 = soundPool.load(context,R.raw.bgm_zhuxuanlv,1);
    }

    public void playSound(int s){
        switch (s){
            case 1:
                /**
                 * 第一个参数是 音效对象,第二和第三个参数是左,右声道音量,第四个是优先级,第五个是是否循环,第六个是播放倍速
                 */
                soundPool.play(s1,1,1,2,0,2f);//
                break;
            case 2:
                soundPool.play(s2,1,1,1,0,1f);
                break;
            case 3:
                soundPool.play(s3,1,1,3,0,2f);
        }
    }

}

将游戏内音效添加到需要的地方
射击声:在绘制子弹里添加

if (count % 10 == 0) {
                            MyBullet myBullet = new MyBullet(BitmapFactory.decodeResource(getResources(), R.mipmap.mybullet), myPlane.getX(), myPlane.getY(), 0);
                            MyBullet myBullet1 = new MyBullet(BitmapFactory.decodeResource(getResources(), R.mipmap.mybullet), myPlane.getX() + myPlane.getWidth(), myPlane.getY(), 0);
                            bulletVector.add(myBullet);
                            bulletVector.add(myBullet1);
                            gameSoundPool.playSound(1);//添加射击音效
                        }

爆炸声:在画出爆炸图时

for (int i = 0; i < bulletVector.size(); i++) {
                            bulletVector.elementAt(i).draw(canvas, paint);
                            if (bossPlane.isCollision(bulletVector.elementAt(i))){      //判断我的子弹是否击中boss飞机,击中就画一张爆炸图
                                Boom boom = new Boom(BitmapFactory.decodeResource(getResources(),R.mipmap.boom),bossPlane.getX(),bossPlane.getY(),7);
                                boomVector.add(boom);
                                gameSoundPool.playSound(2); //在爆炸时添加爆炸音效
                            }
                        }

背景音乐代码

/**
         * 添加背景音乐
         */
        mediaPlayer = MediaPlayer.create(getContext(),R.raw.balu);
        mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(MediaPlayer mp) {
                mediaPlayer.start();
                mediaPlayer.setVolume(1,1);
            }
        });
(7)有哪些地方使用了封装,继承,多态,方法重载等

方法重载:这里判断我的飞机是被子弹击中,还是被boss飞机撞击用了方法重载
代码

/**
     * 判断子弹是否击中飞机
     *
     * @param
     * @return
     */
    public boolean isCollision(MyBullet bullet) {
        if (noCollision) {
            return false;
        } else {
            if (bullet.getX() > x && bullet.getX() < x + width && bullet.getY() > y && bullet.getY() < y + height) {
                noCollision = true;
                if (hp > 0) {
                    hp--;
                }
                return true;
            }
        }
        return false;
    }

    /**
     * 判断我的飞机是否撞上boss飞机
     */
    public boolean isCollision(BossPlane bossPlane) {
        if (noCollision){
            return false;
        }else {
            if(y > bossPlane.getY()+bossPlane.getOneH()||y+height  < bossPlane.getY()||x+width < bossPlane.getX()||x >bossPlane.getX()+bossPlane.getOneW()){

            }else {
                noCollision = true;
                if (hp>0){
                    hp--;
                }
                return true;
            }
        }
        return false;
    }

继承和接口:主类继承了SurfaceView,实现了Runnable和SurfaceHolder.Callback接口。

封装:所有的类里定义的变量都是用private修饰,都用到了封装,体现了Java的安全性
比如:我的飞机类里定义的变量都是private型。
代码:

    private int x;
    private int y;
    private Bitmap bitmap;
    private Bitmap bitmapHp;
    private int height, width;
    private boolean noCollision;    //判断是否进入无敌时间
    private int noCollisionCount;  //无敌时间计数
    private int hp = 3;            //血量初始值为三

如果别的类想要调用封装的变量,可以用set,get’方法,比如:
get,set方法代码

 public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public int getWidth() {
        return width;
    }
}

二.对于飞机大战的收获与感悟

经过一个月的学习,对以前学习过的和没有学习过的Java知识进行了全面的复习和总结,在前几周,没有写飞机大战这个项目之前,我以为自己的基础学习的还可以,对于基本的概念以经可以流畅的应用了,但是我也知道我自己的缺点在于不能把这些知识点串联起来使用,我只时在每一小节的运用上比较熟练,但是当项目复杂以及涉及的内容和知识点过多时,就会产生一种力不从心的感觉,特别是让我自己去写一个项目的时候,常常是没有任何的头绪,不知道该从哪写起,程序报错了,也不会学着去看错误提示,不会自己试着改正。
通过这个项目的编写,我学会了自我学习的习惯,知道要自己先思考问题,自己先解决错误,到最后在和别人讨论,这样才能收获到更多的东西,才能吸取到精华,如果只是单单把别人的东西拿过来抄一遍,效果是没什么用。一定是自己思考过后。再讨论才有更多自己的想法,而且有时候我觉得我不行,我写不了,但是多想,多思考,然后再到网上学 ,这个问题说不定就能被我自己给解决了。这时,一种满足感油然而生,这种满足感会促使着我不断的去学习,然后不断地进步。
这一次写飞机大战这个项目,我有学会了使用AndroidStudio这个编译工具,说起AndroidStudio的安装,我还真费了好大的劲,最后重置了系统才把它搞定,但是有好多的报错都是我自己一个一个去网上百度,自己解决的,这也加深了我对这些错误的理解,以后遇到了可以更好的去解决它们。
这一次的学习中,不光学到了技术,最重要的是还学到了一些态度,对待学习的态度,一定要多学,多思考,多问。不光是学习,很多方面都是如此。
最后,希望自己在接下来的时间里,能把这个飞机大战的项目多写几遍,把各种知识的串联使用的熟练一点,然后再写一些小项目来提升自己的实力。

  • 5
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 20
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值