我是靠谱客的博主 精明秋天,最近开发中收集的这篇文章主要介绍【18.5.31 日常】Android项目——飞机大战详解整体实现思路如何绘制循环滚动的背景图片如何绘制飞机如何绘制子弹如何判断碰撞如何绘制爆炸效果如何添加音效哪些地方用到封装、继承、多态、方法重载、接口等收获及感悟游戏下载地址,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

整体实现思路

通过两张背景图片实现背景滚动,同时做出能根据用户触摸位置不同而改变位置的飞机,能够不停地射出子弹,Boss同理,Boss移动方式随机,同时添加小飞机以增强游戏的表现力,外加例如激光之类的特殊游戏模块来增强游戏的可玩性,最后添加主界面和启动界面来增强游戏的整体性。

如何绘制循环滚动的背景图片

实现思路

通过编辑两张同样照片实现再屏幕上永远显示一张图片,同时当第一张图片完全移除窗口时迅速的移到另一张图片的上方相接来制造出背景无限滚动的效果

注意

图片的选择与使用必须采用整体对称相接的,以免出现接不到一起的尴尬,本次游戏图片默认第一关使用下图
这里写图片描述

代码

`Public class BackGround {
    private static int y1;
    private static int y2;
    private Bitmap bitmap;
    static int height;
    public BackGround(Bitmap bitmap,int height){
        this.bitmap = bitmap;
        this.height = height;
        y1 = 0;
        y2 = y1-MySurfaceView.height;
    }

    public void bGP(Canvas canvas,Paint paint){     //绘制每一帧的背景图片

        bYH();
        paint.setColor(Color.RED);
        canvas.drawBitmap(bitmap,0,y1,paint);
        canvas.drawBitmap(bitmap,0,y2,paint);

    }

 public void bYH(){                                                       //在每一帧中变换两张图片的Y坐标实现图片变化
        y1+=10;
        y2+=10;
        if(y1>=MySurfaceView.height){
            y1 = y2-bitmap.getHeight();
        }
        if(y2>=MySurfaceView.height){
            y2 = y1-bitmap.getHeight();
        }
    }
}`

如何绘制飞机

飞机的整体移动原理

通过创建一个事件监听器,在用户每一次对屏幕进行触摸滑动操作时进行监听,一旦用户的触碰点在飞机的坐标范围围城的矩形范围内时,就进行数据的赋值,将拖动后的坐标赋予该帧的飞机,以此来实现飞机的拖动移动,至于飞机的绘制,直接使用canvas.drawBitmap即可

代码片

触摸事件监听及判断核心代码

 public boolean onTouchEvent(MotionEvent event) {
         plane.touchEvent(event);
        return true;  //该句代码实现了监听器永远监听的功能
    }
    public void touchEvent(MotionEvent event) {//该方法处在飞机类中,用来对坐标进行判断并对飞机坐标进行赋值
        if (event.getY() > 0 && event.getY() < MySurfaceView.height && event.getX() >= x && event.getX() <= bitmap.getWidth() + x && event.getY() >= y && event.getY() <= y + bitmap.getHeight()) {
            x = (int) event.getX() - bitmap.getWidth() / 2;
            y = (int) event.getY() - bitmap.getHeight() / 2;
        }
    }

如何绘制子弹

实现思想

在绘制子弹时,我发现若是使用类似背景图片的切换方式会导致子弹的频率底下,不能全部从飞机的位置进行发射等问题;若使用启用一个新线程的方式创立的话又会白白浪费资源做一件很小的事情,造成了资源的浪费同时也加重了工作量,最后我才用了Vector数组对子弹进行存放

Vector数组

这是一个类似于ArrayList数组的类型,我将数据类型定义为Bullet类型,即子弹类,在MySurfaceView中定义一个int类型的计数器count,在每次循环一帧所有画面时对count进行一次+1的操作,最后进行判断,若count整除一个数,则新建一个Bullet的对象并将其添加至Vector中,在添加之后再通过for循环对每个数组中的数据(即每一颗子弹)进行一次位置坐标的改变

优点

减少了代码的复杂程度,同时应注意在子弹结束运行后,比如击中了敌人,飞出地图之后进行remove操作从而保证不会因为内存占有量过大而造成的内存溢出错误

代码片

最重要的核心代码

      if (count % 8 == 0) {
                        Bullet bullet = new Bullet(BitmapFactory.decodeResource(getResources(), R.mipmap.mybullet), Plane.x, Plane.y);
                        bulletVector.add(bullet);
                        gameSoundPool.playSound(2);
                    }
                    for (int i = 0; i < bulletVector.size(); i++) {
                        if (bulletVector.elementAt(i).isHit()) {
                            score += 200;
                            gameSoundPool.playSound(3);
                            if (Boss.isLife) {
                                boss.beFight(canvas, paint);
                            }
                            bulletVector.remove(i);
                        } else if (bulletVector.elementAt(i).isDead()) {
                            bulletVector.remove(i);
                        }
                    }

如何判断碰撞

原理

其实子弹与飞机碰撞与飞机与飞机碰撞的原理时相同的,不过在考虑前者是我们一般只需要考虑其xy坐标与以飞机xy坐标为基准的矩形范围的位置判定即可,但是如果是后者的话,由于他们的自身面积都比较大,因此不能仅仅使用xy坐标来进行判定,而应该使用多种方法判断多种情况下的击中事件

代码片

下面为子弹与Boss飞机击中判定的代码片

    private void sg() {
        count++;
        y -= 40;
        if (y<=0) {
            isDead = true;
        }
        if(x >= Boss.x-70&& x <= Boss.x + Boss.bitmap.getWidth() / 10-70 && y <= Boss.y + Boss.bitmap.getHeight()){
            isHit = true;
            MySurfaceView.gameSoundPool.playSound(4);
        }

    }

如何绘制爆炸效果

原理及思想

在绘制爆炸效果之前其实绘制Boss飞机时就用到了该方法,即canvas中的裁剪方法clipRect,只需要裁剪出一个显示空间,然后将图片依次贴近该空间内即可实现一个简短的爆炸效果

注意事项

使用canvas中的clipRect方法时一定要注意使用sava方法保存当前画布的状态,以及restore方法恢复之前保存的画布状态

代码片

  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;
        }
    }

如何添加音效

方法的使用及思想

SoundPool方法

使用思路

该方法需要定义一个新的类,通过添加相应的音频文件后就可以在主程序中进行调用,但是需要注意的时,调用该类中的对象时一定要设置一个参数方便判断具体播放哪一个音乐

代码片

 public GameSoundPool(Context context) {
        this.soundPool = new SoundPool(6, AudioManager.STREAM_MUSIC,0);
        s1 = soundPool.load(context,R.raw.bgm_zhuxuanlv,2);  //背景音乐
        s2 = soundPool.load(context,R.raw.shoot,2);  //射击声
        s3 = soundPool.load(context,R.raw.explosion,3); //小兵被击中
        s4 = soundPool.load(context,R.raw.explosion2,4); //boss被击中
        s5 = soundPool.load(context,R.raw.button,1); //轰炸预警
        s6 = soundPool.load(context,R.raw.explosion3,2); //轰炸
        s7 = soundPool.load(context,R.raw.eryingzhang,2); //二营长
        s8 = soundPool.load(context,R.raw.kaipao,2);//开炮
        s9 = soundPool.load(context,R.raw.tiancai,2);//天才
    }

    public void playSound(int i){
        switch (i){
            case 8:
                soundPool.play(s1,8,8,1,1,1);
                break;
            case 2:
                soundPool.play(s2,0.2f,0.2f,1,2,1);
                break;
            case 3:
                soundPool.play(s3,0.2f,0.2f,1,2,1);
                break;
            case 4:
                soundPool.play(s4,0.2f,0.2f,1,2,1);
                break;
            case 5:
                soundPool.play(s5,1.0f,1.0f,1,1,1);
                break;
            case 6:
                soundPool.play(s6,0.2f,0.2f,1,1,1);
                break;
            case 9:
                soundPool.play(s7,0.2f,0.2f,1,1,1);
                break;
            case 10:
                soundPool.play(s8,0.2f,0.2f,1,1,1);
                break;
            case 0:
                soundPool.play(s9,0.2f,0.2f,1,1,1);
                break;
        }
    }

使用中碰到的问题

在实际使用该类时,我发现背景音乐无法进行完美的调用,并且由于只定义了一个SoundPool对象,造成了同一个时间段内无法播放过多的音频,同时还要不断地调用,传参造成了一定程度上的工作量加大,并且定义一个新的音频文件时还要编写很多无用的参数,同时该类仅仅可以播放音乐,功能在MediaPlayer前暂时稍显单一

MediaPlayer方法

思路

当我在SoundPool中遇到了无数的问题,尤其是背景音乐无法播放的问题后我就想到了用一种新的方法来辅助SoundPool类进行编程,最后发现了这个功能强大使用方便的类

代码片

 MediaPlayer mediaPlayer = null;
            mediaPlayer = MediaPlayer.create(getContext(),R.raw.bgm_zhuxuanlv);
            mediaPlayer.setVolume(1.0f,1.0f);
            mediaPlayer.setLooping(thgg);//此处传入的thgg是一个标志位,为true
            mediaPlayer.start();

使用心得

该方法大大的增强了游戏的整体效果,并且还可以播放视频,更加让我对他爱不释手

哪些地方用到封装、继承、多态、方法重载、接口等

封装

概念

封装的概念是使用private修饰的类中的成员变量,每个成员的该变量均不相同,并且提升了安全性

实际案例

我在每个类中对于成员变量总是采用封装的思想,并且设置get方法来使得其他类可以在安全的状态下访问该变量而不会造成修改,例如飞机类中的x、y变量

继承

概念

从已有的类中派生出新类,新的类能继承原有类的属性和方法并能派生出新的属性和方法

实际案例

在飞机大战游戏中,最最典型的继承莫过于MySurfaceView继承SurfaceView类了

多态

概念

两个或两个以上的类中的不同对象对于同一个方法所产生的不同的结果

实际案例

在生成子弹时,根据不同的子弹而定义出相应的构造方法

Bullet bullet1 = new PlaneBullet();
Bullet bullet2 = new BossBullet();

方法重载

概念

同一个类中两个方法的方法名相同,参数列表不同,即构成方法的重载

实际案例

在飞机大战的游戏中,构成方法重载的地方主要在子弹的创建中,根据传入的参数不同进行不同的操作

接口

概念

使用interface修饰的特殊抽象类

实际案例

同理,在该游戏中,我唯一运用到接口的地方即为定义MySurfaceView时调用了 Runnable, SurfaceHolder.Callback两个接口

收获及感悟

通过老师的讲解,加上平时自己的钻研,在短短的几个星期内快速的上手了java,从无到有,现在能够通过百度、贴吧等等教程对自己的知识进行不断地填充,同时编写飞机大战使得我对之前学过的基础知识有了更深的感悟,并且加强了我对基础理论知识在实战中如何变成实实在在的工具的理解,对于今后的编程道路有着无法比拟的作用,但是这次的代码还是不够完善,今后我一定会对它进行优化的

游戏下载地址

链接:https://share.weiyun.com/5SIDKAj 密码:0gg0xk

最后

以上就是精明秋天为你收集整理的【18.5.31 日常】Android项目——飞机大战详解整体实现思路如何绘制循环滚动的背景图片如何绘制飞机如何绘制子弹如何判断碰撞如何绘制爆炸效果如何添加音效哪些地方用到封装、继承、多态、方法重载、接口等收获及感悟游戏下载地址的全部内容,希望文章能够帮你解决【18.5.31 日常】Android项目——飞机大战详解整体实现思路如何绘制循环滚动的背景图片如何绘制飞机如何绘制子弹如何判断碰撞如何绘制爆炸效果如何添加音效哪些地方用到封装、继承、多态、方法重载、接口等收获及感悟游戏下载地址所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(60)

评论列表共有 0 条评论

立即
投稿
返回
顶部