飞机大战小游戏
程序员文章站
2022-06-29 11:27:46
...
实现思路
首先创建我们要创建我们需要的类,比如我方战机,敌方战机等等,然后再主程序里面创建一个surfaceview,用来实现飞机背景图等的绘制,在主程序里面把当前的类中的方法被实现或者是把父类中的方法所重写,在主程序中还用到了线程,在启动线程的时候一定要用new Thread().start来调用,在飞机子弹那些子类中把需要贴的图全调用出来,最后只要在主程序里面调用一下,如果在调用时出先不能调用的情况,可能是封装引起的,这时候需要把你要调用的东西get或set一下,把基本的操作做完之后就可以添加音效,还有游戏开始结束的一些图片。
如何实现背景图的循环
先创建背景图的类,然后用canvas.drawBitmap方法把图片绘制出来,然后把你图片的初始位置定义一下,要想使图片动起来,肯定要改变它的Y轴坐标,这时候只需要让它的y轴一直减减就好了,这时候你就会发现两张图动完之后就会白屏,所以我们最后只要用个if语句判断一下就好了。然后再主程序中把你所需要用到的背景图调用一下。
MySufaceView主程序
package com.example.a11359.android1;
import android.content.Context;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import java.util.Vector;
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
private SurfaceHolder surfaceHolder;
private Canvas canvas;//绘制图形
private Boolean a = true;//标志位
public static int height;
public static int width;
private MyPlan plan;
private Vector<Bullet> bulletVector = new Vector<>();//玩家子弹数组
private Vector<Bullet> boosBulletVector = new Vector<>();
private Vector<Boom> boomVector = new Vector<>();
GameSoundPool gameSoundPool;
public static int GAME_STATE=4;
// private boolean start=false;
private int count;
public MySurfaceView(Context context) {
super(context);
init();
}
/**
* 初始化操作
*/
private void init() {
surfaceHolder = getHolder();
surfaceHolder.addCallback(this);//添加回调时间监听
setFocusable(true);//设置可聚焦
setKeepScreenOn(true);//设置屏幕常亮
setFocusableInTouchMode(true);//设置触摸模式
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
new Thread(this).start();//启动子线程
height = getHeight();
width = getWidth();
gameSoundPool = new GameSoundPool(getContext());
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
a = false;
}
@Override
public void run() {
//
// MySurfaceView.GAME_STATE = 4;
Paint paint = new Paint();
BackGround backGround = new BackGround(BitmapFactory.decodeResource(getResources(), R.mipmap.bk));//调用背景图片
plan = new MyPlan(BitmapFactory.decodeResource(getResources(), R.mipmap.myplan1), BitmapFactory.decodeResource(getResources(), R.mipmap.hp));//调用飞机图片
BossPlan bossPlan = new BossPlan(BitmapFactory.decodeResource(getResources(), R.mipmap.bossplane));//调用boos图片
gameSoundPool.playSound(3);
while (a) {
count++;
try {
canvas = surfaceHolder.lockCanvas();//锁定画布
canvas.drawColor(Color.WHITE);
switch (GAME_STATE){
case 0:
backGround.draw(canvas, paint);//实现背景图的绘制
plan.draw(canvas, paint);//实现飞机图的绘制
bossPlan.draw(canvas, paint);//实现boos图的绘制
paint.setColor(Color.BLUE);
paint.setTextSize(80);
canvas.drawText("boos血量"+bossPlan.getBoosHp(),0,60,paint);
canvas.drawText("飞行"+count+"米",0,MySurfaceView.height-plan.getBitmapHp().getWidth(),paint);
//打印我方子弹
if (count % 1 == 0) {
gameSoundPool.playSound(1);
Bullet bullet = new Bullet(BitmapFactory.decodeResource(getResources(), R.mipmap.zidan), plan.getX(), plan.getY(), 0);
Bullet bullet1 = new Bullet(BitmapFactory.decodeResource(getResources(), R.mipmap.zidan), plan.getX() + plan.getWidth(), plan.getY(), 0);
Bullet bullet2 = new Bullet(BitmapFactory.decodeResource(getResources(), R.mipmap.mybullet), plan.getX() + plan.getWidth() / 2, plan.getY(), 0);
// Bullet bullet3 = new Bullet(BitmapFactory.decodeResource(getResources(), R.mipmap.mybullet), plan.getX() -plan.getWidth()/2, plan.getY(), 0);
// Bullet bullet4 = new Bullet(BitmapFactory.decodeResource(getResources(), R.mipmap.mybullet), plan.getX() +plan.getWidth()+plan.getWidth(), plan.getY(), 0);
bulletVector.add(bullet);
bulletVector.add(bullet1);
bulletVector.add(bullet2);
// bulletVector.add(bullet3);
// bulletVector.add(bullet4);
}
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 (bossPlan.isCollision(bulletVector.elementAt(i))) {
gameSoundPool.playSound(2);
Boom boom = new Boom(BitmapFactory.decodeResource(getResources(), R.mipmap.boom), bossPlan.getX(), bossPlan.getY(), 7);
boomVector.add(boom);
}
}
for (int i = 0; i < boomVector.size(); i++) {
if (boomVector.elementAt(i).isEnd()) {
boomVector.remove(i);
} else {
boomVector.elementAt(i).draw(canvas, paint);
}
}
//打印boos子弹
if (count % 50 == 0) {
Bullet bullet = new Bullet(BitmapFactory.decodeResource(getResources(), R.mipmap.bossbullet), bossPlan.getX(), bossPlan.getY() + bossPlan.getFrameH(), 1);
Bullet bullet1 = new Bullet(BitmapFactory.decodeResource(getResources(), R.mipmap.bossbullet), bossPlan.getX() + bossPlan.getFrameH(), bossPlan.getY() + bossPlan.getFrameH(), 1);
boosBulletVector.add(bullet);
boosBulletVector.add(bullet1);
}
for (int i = 0; i < boosBulletVector.size(); i++) {
if (boosBulletVector.elementAt(i).isDead()) {
boosBulletVector.remove(i);//控制子弹数量
}
}
for (int i = 0; i < boosBulletVector.size(); i++) {
boosBulletVector.elementAt(i).draw(canvas, paint);//添加子弹
plan.isCollision(boosBulletVector.elementAt(i));
}
plan.isCollision(bossPlan);
break;
case 1:
Rect rect = new Rect(0,0,getWidth(),getHeight());
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(),R.mipmap.gamewin),null,rect,paint);
break;
case 2:
Rect rect1 = new Rect(0,0,getWidth(),getHeight());
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(),R.mipmap.gamelost),null,rect1,paint);
break;
case 3:
break;
case 4:
Rect rect2 = new Rect(0,0,getWidth(),getHeight());
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(),R.mipmap.mainmenu),null,rect2,paint);
Rect rect3 = new Rect(0,getHeight()*2/5,getWidth(),getHeight()*3/5);
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(),R.mipmap.logo),null,rect3,paint);
if (count>=25){
MySurfaceView.GAME_STATE=0;
break;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);//解锁画布
}
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
plan.touchEvent(event);
return true;//永远监听屏幕触摸事件
}
}
BackGround//背景图片类
package com.example.a11359.android1;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
public class BackGround {
private int y1;
private int y2;
private Bitmap bitmap;
//绘制图片
public BackGround(Bitmap bitmap) {
this.bitmap = bitmap;
y1 = 0;
y2 = y1 - bitmap.getHeight();
}
/
public void draw(Canvas canvas, Paint paint) {
logic();
canvas.drawBitmap(bitmap, 0, y1, paint);
canvas.drawBitmap(bitmap, 0, y2, paint);
}
//实现背景图的循环
public void logic() {
y1 += 4;
y2 += 4;
if (y1 >= MySurfaceView.height) {
y1 = y2 - bitmap.getHeight();
}
if (y2 >= MySurfaceView.height) {
y2 = y1 - bitmap.getHeight();
}
}
}
如何绘制飞机
先创建一个飞机类,然后开始绘制图片,在主程序里调用所需要的图片,最够实现图片的绘制;在绘制我方战机的时候需要实现飞机的拖动,这时候需要在主程序里设置触摸,可聚焦,还有屏幕的常亮,屏幕常亮的实现需要添加时间监听。在实现飞机拖动时,需要在我方飞机中添加一个触摸的类,只要让实时屏幕监控你触摸的,xy交点,但是这时候会有一个BUG就是你点哪地方飞机就会从另一个地方闪到另一个地方,所以要用到if判断语句判断只要飞机的位置可以拖动。
MyPlan我的飞机类
package com.example.a11359.android1;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.MotionEvent;
public class MyPlan {
private Bitmap bitmap;
private Bitmap bitmapHp;
private int x,y;
private int width,height;
private boolean noCollision;
private int noCollisionCount;
private int hp = 3 ;
public MyPlan(Bitmap bitmap,Bitmap bitmapHp ) {
this.bitmapHp= bitmapHp;
this.bitmap = bitmap;
x = MySurfaceView.width/2-bitmap.getWidth()/2;
y = MySurfaceView.height-bitmap.getHeight();
width = bitmap.getWidth();
height = bitmap.getHeight();
}
public void draw(Canvas canvas, Paint paint) {
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);
}
}
//实现飞机的拖动
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 boolean isCollision(Bullet bullet){
if (hp<=0){
MySurfaceView.GAME_STATE = 2;
}
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;
}
//实现飞机与飞机的碰撞
public boolean isCollision(BossPlan bossPlan){
if (noCollision){
return false;
}else{
if (bossPlan.getY()+bossPlan.getFrameH()>y&&bossPlan.getY()+bossPlan.getFrameH()<y+height){
if (x<bossPlan.getX()&&x+width>bossPlan.getX()){
noCollision = true;
if(hp>0){
hp--;
}
return true;
}
if (x>bossPlan.getX()&&x+width<bossPlan.getX()+bossPlan.getFrameW()){
noCollision = true;
if(hp>0){
hp--;
}
return true;
}
if (x<bossPlan.getX()&&x+width>bossPlan.getX()+bossPlan.getFrameW()){
noCollision = true;
if(hp>0){
hp--;
}
return true;
}
}
}
return false;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public Bitmap getBitmapHp() {
return bitmapHp;
}
}
如何绘制子弹
先创建一个子弹类,然后绘制飞机一样把子弹先贴上去,然后要实现子弹的发射这时候用y-,要实现子弹的循环我们要不停的往里面添加子弹;这个要在主程序里写,但是子弹发射出去会继续往前走不会消失,占用大量内存,所以我们要删除已经出界的子弹,绘制boos子弹和这和一样,只不过boos子弹是y+.
Bullet子弹类
package com.example.a11359.android1;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
public class Bullet {
private int speed=40;
private int x;
private int y;
private Bitmap bitmap;
private int type;
private boolean isDead;
public Bullet(Bitmap bitmap,int x,int y,int type){
this.bitmap = bitmap;
this.x = x;
this.y = y;
this.type = type;
}
public void draw(Canvas canvas, Paint paint){
canvas.drawBitmap(bitmap,x,y,paint);
logic();
}
public void logic() {
switch (type){
//玩家子弹
case 0:
y -= speed;
if (y < 0) {
isDead = true;
}
break;
//boos子弹
case 1:
y += speed;
if (y < 0) {
isDead = true;
}
break;
}
}
public boolean isDead() {
return isDead;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public Bitmap getBitmap() {
return bitmap;
}
public void setDead(boolean dead) {
isDead = dead;
}
}
判断碰撞
子弹与飞机的碰撞
实现子弹与飞机碰撞很简单,只需要在飞机类里面创建一个判断子弹和飞机之间的距离的方法就可以了;最后在主程序添加子弹里面调用子弹碰撞飞机的方法,
实现飞机与飞机的碰撞
这个和子弹与飞机性质一样,不多说直接代码
public boolean isCollision(BossPlan bossPlan){
if (noCollision){
return false;
}else{
if (bossPlan.getY()+bossPlan.getFrameH()>y&&bossPlan.getY()+bossPlan.getFrameH()<y+height){
if (x<bossPlan.getX()&&x+width>bossPlan.getX()){
noCollision = true;
if(hp>0){
hp--;
}
return true;
}
if (x>bossPlan.getX()&&x+width<bossPlan.getX()+bossPlan.getFrameW()){
noCollision = true;
if(hp>0){
hp--;
}
return true;
}
if (x<bossPlan.getX()&&x+width>bossPlan.getX()+bossPlan.getFrameW()){
noCollision = true;
if(hp>0){
hp--;
}
return true;
}
}
}
return false;
}
for (int i = 0; i < boosBulletVector.size(); i++) {
boosBulletVector.elementAt(i).draw(canvas, paint);//添加子弹
plan.isCollision(boosBulletVector.elementAt(i));//子弹与飞机相撞
}
plan.isCollision(bossPlan);//飞机与飞机相撞
爆炸效果
创建爆炸类,爆炸这个图片是由好几张图构成的,所以要实现爆炸效果,让每一幅图依此显示一遍,
Boom爆炸类
package com.example.a11359.android1;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
public class Boom {
private int frameW,frameH;
private Bitmap bitmap;
private int x, y;
private int totalFrame;
private int currentFrame;//显示当前第几幅画
private boolean isEnd;
public Boom(Bitmap bitmap, int x, int y, int totalFrame) {
this.bitmap = bitmap;
this.x = x;
this.y = y;
this.totalFrame = totalFrame;
frameW = bitmap.getWidth()/totalFrame;
frameH = bitmap.getHeight();
}
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;
}
}
public boolean isEnd() {
return isEnd;
}
}
//爆炸效果
for (int i = 0; i < boomVector.size(); i++) {
if (boomVector.elementAt(i).isEnd()) {
boomVector.remove(i);
} else {
boomVector.elementAt(i).draw(canvas, paint);
}
}
添加音效
创建一个音效类,把你需要用到的音效调用出来,用switch语句分开,然后再主程序调用需要的音效。
GameSoundPool音效类
package com.example.a11359.android1;
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.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;
case 3:
soundPool.play(s3, 1, 1, 1, 0, 1.0f);
break;
}
}
}
- 在每个类中定义时我们基本上都用到封装,这样会使代码更安全。
- 继承的话基本上就用到了一两次,在MainActivity中和主程序中用到了。
- 多态在主程序中绘制图片的时候用到了。
- 线程在主程序中也用到了。
- 方法的重载在子弹类,飞机等类中都有用到。
收获与感悟
我的收获是让我进一步对Java有了深一步的了解,加强了我对Java的理解,知道什么时候该用什么方法,写代码时候要有一个逻辑,不能盲目去写,通过这个小游戏让我知道Java的强的,还有很多的知识等着我去学习,加油吧少年!