美文网首页Android开发资料收集区Android知识Android开发
Android Canvas打飞机之别人家的战斗机

Android Canvas打飞机之别人家的战斗机

作者: Galaxy北爱 | 来源:发表于2016-12-27 12:54 被阅读525次

    前面讲解了<a href="http://www.jianshu.com/p/8b65e7e73f70">主角</a>的诞生以及<a href="http://www.jianshu.com/p/966a3e9d8bdf">武器配置</a>,只有主角在主场岂不是太寂寞?所以今天这里主要来实现敌方战斗机的诞生。

    1.敌机类型主要提供的样式不多,主要实现一下不同敌机出场的飞行轨迹。创建一个DrawEnemy的敌机类,并且定义敌机类型
    public class DrawEnemy extends DrawGame {
        /**
         * 敌机类型
         */
        public final static int TYPE_A=1;
        public final static int TYPE_B=2;
        public final static int TYPE_C=3;
        public final static int TYPE_D=4;
        public final static int TYPE_E=5;
        public final static int TYPE_F=6;
        public final static int TYPE_G=7;
        public final static int TYPE_H=8;
        public final static int TYPE_I=9;
        public final static int TYPE_J=10;
        public final static int TYPE_K=11;
        public final static int TYPE_L=12;
        public final static int TYPE_M=13;
        public final static int TYPE_N=14;
        public final static int TYPE_O=15;
        public final static int TYPE_P=16;
        public final static int TYPE_Q=17;
        public final static int TYPE_R=18;
        public final static int TYPE_S=19;
        /**
         * 高级敌机
         */
        public final static int TYPE_T=20;
        /**
         * 高级敌机
         */
        public final static int TYPE_U=21;
        /**
         * 敌机自动跟踪主角
         */
        public final static int TYPE_V=22;
        /**
         * 敌机自动跟踪主角
         */
        public final static int TYPE_W=23;
        public final static int TYPE_X=24;
        public final static int TYPE_Y=25;
        public final static int TYPE_Z=26;
    
    2.敌机主要特性有生命值,飞行轨迹,飞行速度,旋转运动,冲击模式,是否死亡等自定义的一些属性
    /**
         * 敌机的生命值
         */
        private int mEnemyLife;
        /**
         * 当前敌机飞行模式
         */
        private int mEnemyType;
        /**
         * 敌机飞行速度
         */
        private float mEnemySpeed;
        private float mEnemySpeedX;
        private float mEnemySpeedY;
        /**
         * 敌机初始化位置距离左边上边的距离
         */
        private float mMarginLeft;
        private float mMarginTop;
        /**
         * 战机是否死亡
         */
        private boolean isDead;
        private Random mRandom;
        private float mAngle;
        /**
         * 敌机在某一位置停留
         * 位置从屏幕左上角计算
         */
        private boolean mEnemyStopLeft, mEnemyStopTop;
        /**
         * 让敌机在某一个位置停留一段时常
         */
        public int stopToTime;
        private float mTempEnemyY = 1f;
    
    3.初始化敌机样式,类型包括追踪主角类型以及普通类型,追踪主角类型需要接收到主角在屏幕的位置,然后计算出敌机的追踪速度。
    @Override
        void initialize(Object... objects) {
            super.initialize(objects);
            mRandom = new Random();
            this.mEnemy = (Bitmap) objects[0];
            this.mEnemyX = (float) objects[1];
            this.mEnemyY = (float) objects[2];
            this.mEnemyLife = (int) objects[3];
            this.mEnemyType = (int) objects[4];
            mMatrix = new Matrix();
            if(objects.length==6){//敌机去追杀主角的战机类型
                mEnemySpeedY = ScreenUtils.getScreenHeight(getContext())/100;
                mEnemySpeedX = ((float)objects[5]-this.mEnemyY)/100;
            }else{//普通类型,不可追踪主角
                onSetSpeed();
            }
        }
    
    4.为了体验出游戏中的真实感,敌机的头部朝向需要对着主角的位置,这样才有一种灵活对战的感觉,实时实现角度根据主角变化
    /**
         * 敌机相对于主角方向旋转
         * 保证敌机头部朝向跟随主机位置移动
         */
        private Bitmap getMatrixBitmap(){
            if(mAngle==0){
                return mEnemy;
            }
            mMatrix.reset();
            //旋转角度
            mMatrix.setRotate(mAngle);
            return Bitmap.createBitmap(mEnemy,0, 0, mEnemy.getWidth(), mEnemy.getHeight(), mMatrix, true);
        }
    
    5.前面提到了两种模式,其中追踪主角模式就是敌机直接向主角方向移动
    /**
         * 面向主角方向 重新计算移动坐标
         * isTrack 是否需要去追杀主角
         */
        public void getAngleRotate(float playerX,float playerY,boolean isTrack){
            float cx = playerX-mEnemyX;
            float cy = playerY-mEnemyY;
            float k = Math.abs(cy/cx);//计算偏移量 斜率
            mAngle = (float) (90-Math.toDegrees(Math.atan(k)));//把弧度转换成角度
            if(cx>0){
                mAngle = -mAngle;
            }
            if(isTrack){//重新计算坐标 追杀主角
                mEnemySpeedX = cx/50;
                mEnemySpeedY = cy/50;
                if(cy<0){
                    mEnemySpeedY=-mEnemySpeedY;
                }
                if((cx<0&&mEnemySpeedX>0)||(cx>0&&mEnemySpeedX<0)){
                    mEnemySpeedX=-mEnemySpeedX;
                }
            }
        }
    
    6.根据不同敌机的类型计算出不同的战机运行速度。
    /**
         * 计算敌机飞行速度
         */
        private void onSetSpeed(){
            float screenH = ScreenUtils.getScreenHeight(getContext());
            float screenW = ScreenUtils.getScreenWidth(getContext());
            switch (mEnemyType){
                case TYPE_E:
                    float min = screenH/200;
                    mEnemySpeed = new Random().nextInt(3)+min;
                    break;
                case TYPE_R:
                    mEnemySpeed  = screenH/160;
                    mEnemySpeedX = 1;
                    mEnemySpeedY = mEnemySpeed;
                    break;
                case TYPE_S:
                    mEnemySpeed = screenH/160;
                    mEnemySpeedX = 1;
                    mEnemySpeedY = mEnemySpeed;
                    break;
                case TYPE_T:
                    mEnemySpeed = 6;
                    mEnemySpeedX = 2;
                    mMarginLeft = mEnemy.getWidth()*3;
                    mMarginTop = mEnemy.getHeight()*3;
                    break;
                case TYPE_U:
                    mEnemySpeed = 6;
                    mEnemySpeedX = 2;
                    mMarginLeft = screenW - mEnemy.getWidth()*4;
                    mMarginTop = mEnemy.getHeight()*3;
                    break;
                case TYPE_X:
                    mEnemySpeed = screenH/80;
                    mEnemySpeedX = mRandom.nextInt(10);
                    if(mRandom.nextBoolean()){
                        mEnemySpeedX = -mEnemySpeedX;
                    }
                    mEnemySpeedY = screenH/160;
                    break;
                case TYPE_Y:
                    mEnemySpeed = screenH/160;
                    mEnemySpeedX =screenH/160;
                    mEnemySpeedY =screenH/160;
                    break;
                case TYPE_Z:
                    mEnemySpeed = screenH/220;
                    break;
                default:
                    mEnemySpeed = screenH/160f;
                    break;
            }
        }```
    ######7.通过updateGame的方法来进行战机移动,updateGame中实现了26种运行的轨迹算法,其实就是通过改变敌机XY坐标进行对应的移动动画。
    

    /**
    * 更新敌机运行轨迹
    * 并且判断敌机是否离开屏幕
    /
    @Override
    void updateGame(){
    float screenH = ScreenUtils.getScreenHeight(getContext());
    float screenW = ScreenUtils.getScreenWidth(getContext());
    switch (mEnemyType){
    case TYPE_A://左边垂直下降
    if(!isDead){
    if(mEnemySpeed<=3){
    mEnemySpeed+=1;
    }
    mEnemyY+=mEnemySpeed;
    if(mEnemyY>screenH){
    isDead=true;
    }
    }
    break;
    case TYPE_B:
    if(!isDead){//向右倾斜下降
    mEnemyX+=mEnemySpeed/2;
    mEnemyY+=mEnemySpeed;
    }
    break;
    case TYPE_C:
    if(!isDead){//向左倾斜下降
    mEnemyX-=mEnemySpeed/2;
    mEnemyY+=mEnemySpeed;
    }
    break;
    case TYPE_D:
    if(!isDead){//右边垂直倾斜下降
    if(mEnemySpeed<=3){
    mEnemySpeed+=1;
    }
    mEnemyY+=mEnemySpeed;
    }
    break;
    case TYPE_E:
    if(!isDead){//普通模式 子弹自动追踪主角
    if(mEnemyY>screenH){
    isDead = true;
    break;
    }
    mEnemyY+=mEnemySpeed;
    }
    break;
    case TYPE_F:
    if(!isDead){//向右横向移动循环碰撞
    if(mAngle>=360){
    mAngle=0;
    }
    mAngle+=1;
    if(mEnemyX>=screenW-mEnemy.getWidth()){
    mEnemyStopLeft = true;
    }else if(mEnemyX<=0){
    mEnemyStopLeft = false;
    }
    if(mEnemyStopLeft){
    mEnemyX-=mEnemySpeed;
    }else{
    mEnemyX+=mEnemySpeed;
    }
    mEnemyY+=mEnemySpeed/2;
    }
    break;
    case TYPE_G:
    if(!isDead){//向左横向移动循环碰撞
    if(mAngle<=-360){
    mAngle=0;
    }
    mAngle-=1;
    if(mEnemyX<=0){
    mEnemyStopLeft = true;
    }else if(mEnemyX>screenW-mEnemy.getWidth()){
    mEnemyStopLeft = false;
    }
    if(mEnemyStopLeft){
    mEnemyX+=mEnemySpeed;
    }else{
    mEnemyX-=mEnemySpeed;
    }
    mEnemyY+=mEnemySpeed/2;
    }
    break;
    case TYPE_H:
    if(!isDead){//想右切面运行
    if(mEnemyY>=screenH/2||mEnemyX>screenW-mEnemy.getWidth()){
    mEnemyStopLeft = true;
    }
    if(mEnemyStopLeft){
    if(mEnemyStopTop){
    mEnemyX +=0;
    mEnemyY +=mEnemySpeed;
    }else{
    mEnemyX-=mEnemySpeed/2;
    mEnemyY-=mEnemySpeed/2;
    }
    if(mEnemyY<=0){
    mEnemyStopTop = true;
    }
    }else{
    mEnemyX+=mEnemySpeed/2;
    mEnemyY+=mEnemySpeed/2;
    }
    }
    break;
    case TYPE_I:
    if(!isDead){//想左切面运行
    if(mEnemyY>=screenH/2||mEnemyX-mEnemy.getWidth()<=0){
    mEnemyStopLeft = true;
    }
    if(mEnemyStopLeft){
    if(mEnemyStopTop){
    mEnemyX+=0;
    mEnemyY+=mEnemySpeed;
    }else{
    mEnemyX+=mEnemySpeed/2;
    mEnemyY-=mEnemySpeed/2;
    }
    if(mEnemyY<=0){
    mEnemyStopTop = true;
    }
    }else{
    mEnemyX-=mEnemySpeed/2;
    mEnemyY+=mEnemySpeed/2;
    }
    }
    break;
    case TYPE_J:
    if(!isDead){//右边V形运动
    if(mEnemyY>=screenH/2){
    mEnemyStopLeft = true;
    }
    if(mEnemyStopLeft){
    mEnemyX+=mEnemySpeedX;
    mEnemyY+=mEnemySpeedY;
    }else{
    mAngle=0;
    mEnemyY+=mEnemySpeed;
    mEnemyX+=mEnemySpeed/2;
    }
    }
    break;
    case TYPE_K:
    if(!isDead){//左边V形运动
    if(mEnemyY>=screenH/2){
    mEnemyStopLeft = true;
    }
    if(mEnemyStopLeft){
    mEnemyX+=mEnemySpeedX;
    mEnemyY+=mEnemySpeedY;
    }else{
    mAngle=0;
    mEnemyY+=mEnemySpeed;
    mEnemyX-=mEnemySpeed/2;
    }
    }
    break;
    case TYPE_L:
    if(!isDead){//向上冲锋战斗机
    if(mEnemyStopLeft){
    mEnemyY-=mEnemySpeed/2;
    stopToTime++;
    if(mEnemyY<0||mEnemyY>screenH){
    isDead = true;
    break;
    }
    if(mEnemyY<=screenH/7){
    if(stopToTime >=500){
    mEnemySpeed=20;
    }else{
    mEnemyStopLeft =false;
    }
    }
    }else{
    mEnemyY+=mEnemySpeed/2;
    if(mEnemyY>=screenH/3){
    mEnemyStopLeft = true;
    }
    }
    }
    break;
    case TYPE_M:
    if(!isDead){//向下冲锋战斗机
    if(mEnemyStopLeft){
    stopToTime++;
    mEnemyY-=mEnemySpeed/2;
    if(mEnemyY<=screenH/7){
    mEnemyStopLeft =false;
    }
    }else{
    if(stopToTime >=500){
    mEnemySpeed=20;
    }
    mEnemyY+=mEnemySpeed/2;
    if(mEnemyY>=screenH/4){
    if(stopToTime <300){
    mEnemyStopLeft = true;
    }else if(mEnemyY<0||mEnemyY>screenH){
    isDead = true;
    break;
    }
    }
    }
    }
    break;
    case TYPE_N://左边由上至上到中间
    if(mEnemyStopLeft){
    mEnemyY-=mEnemySpeed;
    mEnemyX+=mEnemySpeed/2;
    if(mEnemyY<=mEnemy.getHeight()&&mEnemyX>=screenW/2-mEnemy.getWidth()){
    mEnemyStopTop = true;
    mEnemyStopLeft = false;
    }
    if(mEnemyY<mEnemy.getHeight()){
    mEnemyY = mEnemy.getHeight();
    }
    if(mEnemyX>screenW/2-mEnemy.getWidth()){
    mEnemyX = screenW/2-mEnemy.getWidth();
    }
    }else{
    if(mEnemyStopTop){
    mEnemyY+=mEnemySpeed;
    if(mEnemyY>screenH){
    isDead = true;
    break;
    }
    }else{
    mEnemyY+=mEnemySpeed
    3;
    if(mEnemyY>=(screenH/2)){
    mEnemyStopLeft = true;
    }
    }
    }
    break;
    case TYPE_O://右边由上至上到中间
    if(mEnemyStopLeft){
    mEnemyY-=mEnemySpeed;
    mEnemyX-=mEnemySpeed/2;
    if(mEnemyY<=mEnemy.getHeight()&&mEnemyY<=screenW/2+mEnemy.getWidth()){
    mEnemyStopTop = true;
    mEnemyStopLeft = false;
    }
    if(mEnemyY<mEnemy.getHeight()){
    mEnemyY = mEnemy.getHeight();
    }
    if(mEnemyX<screenW/2+mEnemy.getWidth()){
    mEnemyX = screenW/2+mEnemy.getWidth();
    }
    }else{
    if(mEnemyStopTop){
    mEnemyY+=mEnemySpeed;
    }else{
    mEnemyY+=mEnemySpeed*3;
    if(mEnemyY>=screenH/2){
    mEnemyStopLeft = true;
    }
    }
    }
    break;
    case TYPE_P://左侧Z字形运动
    if(mEnemyX>=screenW-mEnemy.getWidth()){
    mEnemyStopLeft = true;
    }else if(mEnemyX<=0){
    mEnemyStopLeft = false;
    }
    if(mEnemyY<=0){
    mTempEnemyY = -mTempEnemyY;
    }
    if(mEnemyStopLeft){
    mEnemyY-= mTempEnemyY *2;
    mEnemyX-=mEnemySpeed;
    }else{
    mEnemyY-= mTempEnemyY *2;
    mEnemyX+=mEnemySpeed;
    }
    break;
    case TYPE_Q://右侧z字形运动
    if(mEnemyX<=0){
    mEnemyStopLeft = true;
    }else if(mEnemyX>=screenW-mEnemy.getWidth()){
    mEnemyStopLeft = false;
    }
    if(mEnemyY<=0){
    mTempEnemyY = -mTempEnemyY;
    }
    if(mEnemyStopLeft){
    mEnemyY-= mTempEnemyY 2;
    mEnemyX+=mEnemySpeed;
    }else{
    mEnemyY-= mTempEnemyY 2;
    mEnemyX-=mEnemySpeed;
    }
    break;
    case TYPE_R://左边中间螺旋出场
    if(mAngle>=360){
    mAngle = 0;
    }
    mAngle+=20;
    if(mEnemyY>screenH/2+100){
    mEnemySpeedY = - (mRandom.nextInt(3)+2);
    }else if(mEnemyY<screenH/2){
    mEnemySpeedY =mRandom.nextInt(3)+5;
    }
    mEnemyX+=mEnemySpeedX;
    mEnemyY+=mEnemySpeedY;
    if(mEnemyX>screenW){
    isDead=true;
    }
    break;
    case TYPE_S://右边中间螺旋出场
    if(mAngle<=-360){
    mAngle = 0;
    }
    mAngle-=20;
    if(mEnemyY>screenH/2+100){
    mEnemySpeedY = - (new Random().nextInt(3)+2);
    }else if(mEnemyY<screenH/2){
    mEnemySpeedY = (new Random().nextInt(3)+5);
    }
    mEnemyX-=mEnemySpeedX;
    mEnemyY+=mEnemySpeedY;
    if(mEnemySpeedX<0){
    isDead=true;
    }
    break;
    case TYPE_T://左边高级将领
    if(mEnemyStopLeft){
    if(mEnemyX>=mMarginLeft||mEnemyX<=0){
    mEnemySpeedX= -mEnemySpeedX;
    }
    mEnemyY+=mEnemySpeedX
    2;
    if(mEnemyY>=mMarginTop){
    mEnemyY = mMarginTop;
    }
    if(mEnemyY<=mEnemy.getHeight()){
    mEnemyY=mEnemy.getHeight();
    }
    }else{
    if(mEnemyX>=mMarginLeft){
    mEnemySpeedX= -mEnemySpeedX;
    mEnemyStopLeft =true;
    }
    }
    mEnemyX+=mEnemySpeedX;
    break;
    case TYPE_U://右边高级将领
    if(mEnemyStopLeft){
    if(mEnemyX<=mMarginLeft||mEnemyX>screenW-mEnemy.getWidth()){
    mEnemySpeedX= -mEnemySpeedX;
    }
    mEnemyX+=mEnemySpeedX
    2;
    if(mEnemyY>=mMarginTop){
    mEnemyY = mMarginTop;
    }
    if(mEnemyY<=mEnemy.getHeight()){
    mEnemyY=mEnemy.getHeight();
    }
    }else{
    if(mEnemyX<=mMarginLeft){
    mEnemySpeedX= -mEnemySpeedX;
    mEnemyStopLeft =true;
    }
    }
    mEnemyX-=mEnemySpeedX;
    break;
    case TYPE_V://左边自动追踪
    stopToTime++;
    mEnemyY+=mEnemySpeedY;
    mEnemyX+=mEnemySpeedX;
    if(!mEnemyStopLeft){
    if(mEnemyY>=screenH/4){
    mEnemyStopLeft = true;
    }
    if(stopToTime %25==0){
    mEnemyX = -mEnemySpeedX;
    }
    }else if(mEnemyY<0||mEnemyY>screenH||mEnemyX>screenW||mEnemyX<0){
    isDead=true;
    }
    break;
    case TYPE_W://右边自动追踪
    stopToTime++;
    mEnemyY+=mEnemySpeedY;
    mEnemyX+=mEnemySpeedX;
    if(!mEnemyStopLeft){
    if(mEnemyY>=screenH/4){
    mEnemyStopLeft = true;
    }
    if(stopToTime %25==0){
    mEnemySpeedX = -mEnemySpeedX;
    }
    }else if(mEnemyY<0||mEnemyY>screenH||mEnemyX>screenW||mEnemyX<0){
    isDead=true;
    }
    break;
    case TYPE_X://中空旋转混战模式
    if(mAngle>=360){
    mAngle = 0;
    }
    mAngle++;
    mEnemyX+=mEnemySpeedX;
    mEnemyY+=mEnemySpeedY;
    if((mEnemyY>=screenH/2&&mEnemySpeedY>=0)||(mEnemyY<=0&&mEnemySpeedY<=0)){
    mEnemySpeedY=-mEnemySpeedY;
    if(mEnemySpeedY>=0){
    mEnemySpeedY =mRandom.nextInt((int) mEnemySpeed);
    }
    }
    if((mEnemyX>=screenW-mEnemy.getWidth()&&mEnemySpeedX>=0)||(mEnemyX<=0&&mEnemySpeedX<=0)){
    mEnemySpeedX = -mEnemySpeedX;
    if(mEnemySpeedX>=0){
    mEnemySpeedX = mRandom.nextInt((int) mEnemySpeed);
    }
    }
    break;
    case TYPE_Y://高级将领
    if(mAngle<=-360){
    mAngle=0;
    }
    mAngle-=2;
    mEnemyX+=mEnemySpeedX;
    mEnemyY+=mEnemySpeedY;
    if((mEnemyY>=screenH-mEnemy.getHeight()&&mEnemySpeedY>=0)||(mEnemyY<=0&&mEnemySpeedY<=0)){
    mEnemySpeedY=-mEnemySpeedY;
    }
    if((mEnemyX>=screenW-mEnemy.getWidth()&&mEnemySpeedX>=0)||(mEnemyX<=0&&mEnemySpeedX<=0)){
    mEnemySpeedX = -mEnemySpeedX;
    }
    break;
    case TYPE_Z:
    mEnemyY+=mEnemySpeed;
    break;
    }
    //消失在屏幕以后敌机失效死亡
    if(mEnemyX>screenW||mEnemyY>screenH||mEnemyX<-1){
    isDead=true;
    }
    }

    敌机和前面讲过的子弹逻辑一样,一旦移动出屏幕或者不可见了,通过判断变量isDead移除画布。
    
    为了保证游戏后面的逻辑性以及学习思路的正常进行,并且保证打飞机的效果,最后实现出来感觉就跟一个正常游戏基本一样,所以这里写算法消耗的时间有点多,但是通过敌机类可以更好的学习一些移动,旋转等动画,能够以后自定义实现高效率动画控件的时候提供非常大的帮助,敌机类暂时还没有在GameView中使用,考虑到后面学习过程的顺畅性以及不饶弯路,那么在下一节我会模拟实现一个游戏关卡,通过关卡来模拟敌机出场的顺序和游戏对战,包括后面的碰撞效果。
    
    敌机类源码已经通过Git更新。
    
    <a href="https://github.com/tangyxgit/GameCanvas">我是持续性更新的源码</a>
    
    <a href="http://www.jianshu.com/p/966a3e9d8bdf">上一篇</a>      <a href="http://www.jianshu.com/p/d1768755f1bc">下一篇</a>

    相关文章

      网友评论

      本文标题:Android Canvas打飞机之别人家的战斗机

      本文链接:https://www.haomeiwen.com/subject/jwosvttx.html