android studio飛機大戰遊戲帶註釋源碼教程(多線程)

第一次發博客,學了3天的android studio還有一點以前的java基礎做了個基於多線程的飛機大戰的遊戲

不過,不建議這麼做,遊戲一般一個主線程控制所有對象


遊戲比較簡單大概就這幾個功能

1.會動的背景

2.我的飛機

3.發射子彈

3.敵人飛機


第一步新建一個項目

我用的是Android4.4版本




新建好項目之後 xml文件之類的什麼都不用管

先新建個類


叫做hua


hua.java

package com.dahuijii.liziguo;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.view.MotionEvent;
import android.view.View;
import java.util.Vector;

/**
 * Created by Liziguo on 2018/5/10.
 */
class my{//新建一個類 裏面的東西都是靜態的 當全局變量用
    public static int js=0;//擊殺數
    public static int w,h;//屏幕的寬高
    public static float bili;//比例,用於適應不同屏幕
    public static Vector<hj> list=new Vector<hj>();//所有飛行物的集合,添加進這個集合才能被畫出來
    public static Vector<hj> drlist=new Vector<hj>();//敵人飛機的集合,添加進這個集合才能被子彈打中
    //我集合學的挺爛的哈 爲什麼用Vector呢?因爲他線程是安全的。。。
    public static Bitmap myhj,drhj,bj,myzd;//圖片:我的灰機 敵人灰機 背景 我的子彈
    public static myhj my;//我的灰機
    public static bj b;//背景
}

public class hua extends View{//畫
    private Paint p=new Paint();//畫筆
    private float x,y;//按下屏幕時的座標
    private float myx,myy;//按下屏幕時玩家飛機的座標

    public hua(Context context) {
        super(context);
        //添加事件控制玩家飛機
        setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent e) {
                if(e.getAction()==MotionEvent.ACTION_DOWN){
                    x=e.getX();
                    y=e.getY();
                    myx=my.my.r.left;
                    myy=my.my.r.top;
                }
                float xx=myx+e.getX()-x;
                float yy=myy+e.getY()-y;
                //我的飛機不能飛出屏幕
                xx=xx<my.w-my.my.w/2?xx:my.w-my.my.w/2;
                xx=xx>-my.my.w/2?xx:-my.my.w/2;
                yy=yy<my.h-my.my.h/2?yy:my.h-my.my.h/2;
                yy=yy>-my.my.h/2?yy:-my.my.h/2;
                my.my.setX(xx);
                my.my.setY(yy);
                return true;
            }
        });

        setBackgroundColor(Color.BLACK);//設背景顏色爲黑色

        my.myhj= BitmapFactory.decodeResource(getResources(),R.mipmap.hj);//加載圖片
        my.drhj=BitmapFactory.decodeResource(getResources(),R.mipmap.dr);
        my.myzd=BitmapFactory.decodeResource(getResources(),R.mipmap.zd);
        my.bj=BitmapFactory.decodeResource(getResources(), R.mipmap.bj);

        new Thread(new re()).start();//新建一個線程 讓畫布自動重繪
        new Thread(new loaddr()).start();//新建一個 加載敵人的線程
    }
    @Override
    protected void onDraw(Canvas g) {//這個相當於swing的paint方法吧 用於繪製屏幕上的所有物體
        super.onDraw(g);
        g.drawBitmap(my.b.img,null,my.b.r,p);//畫背景 我沒有把背景添加到list裏

        for(int i=0;i<my.list.size();i++){//我們把所有的飛行物都添加到了my.list這個集合裏
            hj h=my.list.get(i);           //然後在這裏用一個for循環畫出來
            g.drawBitmap(h.img,null,h.r,p);
        }
        g.drawText("擊殺:"+my.js,0,my.h-50,p);

    }
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {//這個方法用來獲取屏幕寬高的
        super.onSizeChanged(w, h, oldw, oldh);
        my.w=w;//獲取寬
        my.h=h;//高

        //獲取手機(應該不是手機的吧 是這控件的吧)分辨率和1920*1080的比例
        //然後飛機的寬高乘上這個分辨率就能在不同大小的屏幕正常顯示了
        //爲什麼用1920*1080呢 因爲我手機就是這個分辨率。。。
        my.bili= (float) (Math.sqrt(my.w * my.h)/ Math.sqrt(1920 * 1080));
        p.setTextSize(50*my.bili);//設置字體大小,“擊殺”的大小
        p.setColor(Color.WHITE);//設爲白色
        //好了 到這裏遊戲開始了
        my.b=new bj();//初始化背景
        my.my=new myhj();//初始化 我的灰機
    }
    private class re implements Runnable {
        @Override
        public void run() {
            //每10ms刷新一次界面
            while(true){
                try { Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
                postInvalidate();//刷新畫布
                //就是這個東西拖了我一天
                //swing是repaint()方法刷新的
                //然後這裏沒有repaint方法
                //然後突然想起C#有一個invalidate()方法是刷新畫布的
                //然後這線程裏用invalidate()會閃退.....
                //煩死了
            }
        }
    }
    private class loaddr implements Runnable{
        @Override
        public void run() {
            while(true){
                //每300ms刷一個敵人
                try {Thread.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}
                try {
                    new drhj();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
class hj{//遊戲內所有物體的父類
    public RectF r=new RectF();//這個是用來確定位置的
    public int hp;//生命
    public float w,h;//寬高
    public Bitmap img;//圖片


    //這裏的畫圖方法和swing的不太一樣
    //設兩個方法來設置x,y的座標
    public void setX(float x){
        r.left=x;
        r.right=x+w;
    }
    public void setY(float y){
        r.top=y;
        r.bottom=y+h;
    }

    public boolean pengzhuang(hj obj,float px) {//判斷碰撞 判斷時忽略px個像素
        px*=my.bili;//凡是涉及到像素的 都乘一下分辨率比例my.bili
        if (r.left+px - obj.r.left <= obj.w && obj.r.left - this.r.left+px <= this.w-px-px)
            if (r.top+px - obj.r.top <= obj.h && obj.r.top - r.top+px <= h-px-px) {
                return true;
            }
        return false;

    }
}
class bj extends hj implements  Runnable{//背景
    public bj(){
        w=my.w;
        h=my.h*2;//背景的高是 屏幕高的兩倍
        img=my.bj;
        setX(0);
        setY(-my.h);
        new Thread(this).start();
    }
    @Override
    public void run() {
        //這裏控制背景一直向下移
        while(true){
            try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
            if(r.top+2<=0){
                setY(r.top+2);
            }else{
                setY(-my.h);
            }
        }
    }
}

class drhj extends hj implements Runnable{//敵人灰機
    private long sd0=(long) (Math.random()*10)+10;//生成一個[10,20)的隨機數 用來控制敵人速度 敵人速度是不一樣的

    public drhj(){
//        w=my.w/5.4f;
//        h=my.h/9.6f;
        w=h=200*my.bili;
        //敵人刷出來的位置
        setX((float)( Math.random()*(my.w-w)));//x是隨機的
        setY(-h);//在屏幕外 剛好看不到的位置
        img=my.drhj;
        hp=12;//生命=12
        my.list.add(this);//添加到集合裏 這樣才能被畫出來
        my.drlist.add(this);//添加到敵人的集合 添加進這個集合子彈纔打得到
        new Thread(this).start();
    }

    @Override
    public void run() {
        while(hp>0){//如果生命>0 沒有死 就繼續向前飛,死了還飛什麼?
            try {Thread.sleep(sd0);} catch (InterruptedException e) {e.printStackTrace();}
            setY(r.top+2*my.bili);
            if(r.top>=my.h)break;//敵人飛出屏幕 跳出循環
        }
        //從集合刪除
        my.list.remove(this);
        my.drlist.remove(this);
    }
}

class myhj extends hj implements Runnable{//我的灰機

    public myhj(){
        w=h=200*my.bili;//凡是涉及到像素的 都乘一下分辨率比例my.bili
        //設置初始位置
        setX(my.w/2-w/2);
        setY(my.h*0.7f-h/2);
        img=my.myhj;//初始化圖片
        my.list.add(this);//添加到集合裏 這樣才能被畫出來
        new Thread(this).start();//發射子彈的線程
    }

    @Override
    public void run() {
        while(true){
            //90毫秒發射一發子彈
            try {Thread.sleep(90);} catch (InterruptedException e) {e.printStackTrace();}
            new myzd(this);
        }
    }
}
class myzd extends hj implements Runnable{//我的子彈
    private int dps;
    private float sd0;

    public myzd(hj hj){
        w=h=90*my.bili;//凡是涉及到像素的 都乘一下分辨率比例my.bili
        img=my.myzd;//圖片
        sd0=6*my.bili;//速度=6
        dps=6;//傷害=6
        //設在玩家中心的偏上一點
        setX(hj.r.left+hj.w/2-w/2);
        setY(hj.r.top-h/2);
        my.list.add(this);//添加到集合裏 這樣才能被畫出來
        new Thread(this).start();//新建一個子彈向上移動的線程
    }

    @Override
    public void run() {
        boolean flag=false;//一個標記 用來跳出嵌套循環
        while(true){
            try {Thread.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}
            setY(r.top-sd0);//向上移sd0個像素,sd0=6

            try {//try一下 怕出錯
                //這裏判斷有沒有和集合裏的敵人發生碰撞
                for(int i=0;i<my.drlist.size();i++){
                    hj h=my.drlist.get(i);
                    if(pengzhuang(h,30)){//判斷碰撞
                        h.hp-=dps;//敵人生命-子彈傷害
                        flag=true;//一個標記 用來跳出嵌套循環
                        my.js++;//擊殺+1
                        break;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                break;
            }
            if(flag || r.top+h<=0)break;//如果子彈擊中過敵人 或者超出屏幕範圍 跳出循環
        }
        my.list.remove(this);//從集合刪除
    }
}

然後回到MainActivity.java

package com.dahuijii.liziguo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
    private long time;//用於檢測按兩次 "再按一次退出遊戲"

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getSupportActionBar().hide();//隱藏標題欄
        setContentView(new hua(this));
        //setContentView()跟swing的add()差不多吧,不過這裏只能添加一個控件,默認鋪滿屏幕
    }


    public boolean onKeyDown(int keyCode,KeyEvent event) { //返回鍵
        if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0){
            long t=System.currentTimeMillis();//獲取系統時間
            if(t-time<=500){
                exit(); //如果500毫秒內按下兩次返回鍵則退出遊戲
            }else{
                time=t;
                Toast.makeText(getApplicationContext(),"再按一次退出遊戲",Toast.LENGTH_SHORT).show();
            }

            return true;
        }
        return false;

    }
    public void exit(){
        MainActivity.this.finish();
        new Thread(new Runnable(){
            @Override
            public void run() {
                try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}
                System.exit(0);
            }
        }).start();
    }
}

把圖片添加到mipmap

bj.png 背景

dr.png 敵人

hj.png 我的灰機

zd.png 子彈


好了!大功告成!快試試吧!


下載地址:https://download.csdn.net/download/u010756046/10406656

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章