java 雷霆战机游戏 飞机大战 全过程教学+免费素材(附全部源代码)

这个游戏已是我第二次编写了,之前写过一个简易版的飞机大战类似demo。这次在上一次基础上添加了许多元素,增添了可玩性。 游戏效果图如下:

 

游戏是有背景音乐和打击音效的,十分炫酷。那么接下来,我将编写此游戏的全过程做一遍详细介绍,解释部分核心效果实现代码,只要读者此时具备基础的java语法知识,我相信一定可以非常轻松的编写出一款属于自己的雷霆战机游戏。(大神可跳过,直接看源代码)

话不多说,我们直接开始:

一、首先理清编写思路,利用javaOOP面对对象思想分析游戏:

 游戏界面组成部分无非是:玩家飞机、敌方飞机、子弹、背景、爆炸效果等。利用OOP思想创建 Class

【如此图,这是我创建的对象类。】

二、需要将对象一一添加到面板中:

首先将背景图片绘制到面板中:代码如下

package com.my.plane;

import java.awt.Graphics;
import java.awt.image.BufferedImage;

import util.ImageUtil;

public class Background {
	BufferedImage bg=ImageUtil.img.get("bg.jpg");
	BufferedImage bg1=ImageUtil.img.get("bg_meitu_1.jpg");
	int by=0;
	public void drawbg(Graphics g) {
		if(GameJPanel.gameover) {
		g.drawImage(bg1,0,0,null);
		
		}
		else {
		g.drawImage(bg,0,by,null);
		g.drawImage(bg,0,by-ImageUtil.img.get("bg.jpg").getHeight(),null);
	}
	}
	public void move() {	
		this.by+=2;
		if(by>=ImageUtil.img.get("bg.jpg").getHeight())
			by=0;
		
	}
}

【每次重绘将背景图片向下移动2个像素,即可实现飞机移动效果,绘制两张,以免出现面板空白】

重写JPanel  paint()方法将各个元素添加到面板中   核心代码如下:

public void paint(Graphics g) {
		
		bgck.drawbg(g);//绘制背景
		plane.drawplane(g);//绘制玩家飞机
		
		for(int i=0;i<allEnemy.size();i++) {//绘制敌方飞机
		   Enemy demo=allEnemy.get(i);
		   demo.drawEnemy(g);
		 }
		for(int i=0;i<allBoss.size();i++) {
			Boss now=allBoss.get(i);
			now.drawBoss(g);
			}   //绘制boss
		
		for(int i=0;i<allBullet.size();i++) {
			Bullet now=allBullet.get(i);
			now.bullet1=this.bullet1;
			now.drawBullet(g);  //绘制子弹
			}
		for(int i=0;i<allBoom.size();i++) {
				Boom now=allBoom.get(i);
			    now.drawBoom(g);   //绘制爆炸效果
			    
			}
		}

【希望读者编写程序时,也将代码注释标写清楚,否则在程序出错时没有头绪将是很糟糕的事情,本人初期就深受其害】

paint()方法中创建元素对象,并调用对象类中的绘制方法实现 将元素添加到面板中。我以玩家飞机类为例,介绍元素对象类的编写:

我这里玩家飞机类为Plane

定义对象类十分简单且易于理解:  只需要定义 Plane 的 x,y坐标,并通过drawImage将对象元素绘制到面板中即可,代码如下:

package com.my.plane;

import java.awt.Graphics;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.util.LinkedList;

import util.ImageUtil;

public class Plane {
      int px;
      int py;
	  int pw;
	  int ph;
	  int count=0;
	 
	  
	  BufferedImage []pimage= {ImageUtil.img.get("success.png"),ImageUtil.img.get("success1.png")};
	  Plane() {
		
		this.pw=pimage[0].getWidth();
		this.ph=pimage[0].getHeight();	
		this.px=GameJFrame.jx/2-pw/2;//飞机的坐标定义面板的正下方
		this.py=GameJFrame.jy-ph+50;
		
	}
	  public void drawplane(Graphics g) {
		  if(GameJPanel.gameover) { //这里我是用来判断游戏结束时绘制死亡效果的 ,读者可以暂时忽略
				g.drawImage(ImageUtil.img.get("die2.png"),px,py,null);}
			else {
		 
		  g.drawImage(pimage[count], px, py, null);
		  count++;
		  if(count>1) count=0;
	 }
}
}

 

 

【我这里定义了一个ImageUtil类,此类作为API工具类将所有需要使用的图片读取到内存中,方便读取,提高游戏性能,代码如下:】

package util;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.imageio.ImageIO;

public final class ImageUtil {
	
	public static Map<String,BufferedImage>img=new HashMap<String,BufferedImage>();//BufferedImage:内存中的图片对象
	
	static{
		File file=new File("img");
		File [] fs=file.listFiles();
		for(File f:fs) {
			//通过当前的文件名作为HashMap的key
			String s=f.getName();
			//将读取到的文件对象 作为BufferedImage存储
			try {
				BufferedImage i=ImageIO.read(f);
				img.put(s, i);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	

 

 你可能注意到,Plane 类中元素的图片有两张,这两张图片在线程中反复重绘,可使飞机更加立体生动。若你有更好的方法实现这个效果,可以忽略。或可忽略立体效果,即只认定飞机元素只有一张图片,将图片绘制于面板中即可。

 

至于 Enemy(敌方飞机) Boom  Bullet 等 类实现方法大同小异,基本一致,代码如下:

Enemy类

package com.my.plane;

import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.util.Random;

import util.ImageUtil;

public class Enemy {
	int []ex=new int[4];
	int []ey=new int[4];
	int []ew=new int[4];
	int []eh=new int[4];
	
	int count=0;
	BufferedImage []enemy=   {ImageUtil.img.get("s2_1.png"),
			                 ImageUtil.img.get("s2_2.png"),
			                 ImageUtil.img.get("s2_2.png"),ImageUtil.img.get("airplane.png")};
	Enemy(int x,int x1,int x2,int x3,int y,int y1,int y2,int y3){
		this.ex[0]=x;
		this.ex[1]=x1;
		this.ex[2]=x2;
		this.ex[3]=x3;
		this.ey[0]=y;	
		this.ey[1]=y1;
		this.ey[2]=y2;
		this.ey[3]=y2;
		this.ew[0]=ImageUtil.img.get("s2_1.png").getWidth();
		this.ew[1]=ImageUtil.img.get("s2_2.png").getWidth();
		this.ew[2]=ImageUtil.img.get("s2_2.png").getWidth();
		this.ew[3]=ImageUtil.img.get("airplane.png").getWidth();
		this.eh[0]=ImageUtil.img.get("s2_1.png").getHeight();
		this.eh[1]=ImageUtil.img.get("s2_2.png").getHeight();
		this.eh[2]=ImageUtil.img.get("s2_1.png").getHeight();
		this.eh[3]=ImageUtil.img.get("airplane.png").getHeight();
		
		
								
			
	}

	public void drawEnemy(Graphics g) {
		if(GameJPanel.gameover) {for(int i=0;i<4;i++) {
			g.drawImage(ImageUtil.img.get("07.png"),ex[i],ey[i],null);}}
		else {
	for(int i=0;i<4;i++)
		g.drawImage(enemy[i],ex[i],ey[i],null);
		
	}
		}
	public void move(int y,int y1,int y2,int y3) {
		
		this.ey[0]=y;
		this.ey[1]=y1;
		this.ey[2]=y2;
		this.ey[3]=y3;
	}
}

Bullet类

package com.my.plane;

import java.awt.Graphics;
import java.awt.image.BufferedImage;

import util.ImageUtil;

public class Bullet {
	int []bx=new int[2];
	int []by=new int[2];
	int []bw=new int[2];
	int []bh=new int[2];
    boolean bullet1=false; //这里我设置一个bullet1绑定到键盘事件中,当按下“J”键可切换子弹
    boolean bullet2=false;
	BufferedImage []bullet={ImageUtil.img.get("myb_3.png"),
            ImageUtil.img.get("feidan.png")};
	Bullet(int x,int x1,int y,int y1){
		this.bx[0]=x;
		this.bx[1]=x1;
		this.by[0]=y;
		this.by[1]=y1;
		
	}
	public void drawBullet(Graphics g) {
		 if(bullet1) g.drawImage(bullet[1],bx[0],by[0],null);
		 else  g.drawImage(bullet[0],bx[1],by[1],null);
	}
	public void  move() {
		by[0]-=5;
		by[1]-=5;
	}
}

Boom 类

package com.my.plane;

import java.awt.Graphics;
import java.awt.image.BufferedImage;

import util.ImageUtil;

public class Boom {
	int ox;
	int oy;
	int count=0;
	
	BufferedImage [] boom= {ImageUtil.img.get("01.png"),ImageUtil.img.get("02.png"),ImageUtil.img.get("03.png"),
			ImageUtil.img.get("04.png"),ImageUtil.img.get("05.png"),
			ImageUtil.img.get("06.png"),ImageUtil.img.get("07.png")};
	
	
	Boom(int x,int y){
		this.ox=x;
		this.oy=y;
	}
	public void drawBoom(Graphics g) {
	
		
		g.drawImage(boom[count], ox, oy, null);
		count++;
		if(count>7) count=0;
	
		
	}
	

}

【boom 类定义了七张图片,这七张图片反复重绘即可实现动态的爆炸视觉效果】

Music类  代码如下

package com.my.plane;

import java.applet.Applet;
import java.applet.AudioClip;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;

public class Music {
	File file1=new File("music\\game_music.wav");
	File file2=new File("music\\bullet.wav");
	File file3=new File("music\\enemy1_down.wav");
	AudioClip bgmusic=null;
	AudioClip bulletmusic=null;
	AudioClip boommusic=null;
	Music(){
		
		
		try {
			bgmusic=Applet.newAudioClip(file1.toURI().toURL());
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		try {
			bulletmusic=Applet.newAudioClip(file2.toURI().toURL());
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		try {
			boommusic=Applet.newAudioClip(file3.toURI().toURL());
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

这里我设置了三种音乐,分别为背景音乐,子弹发射音效,爆炸音效。

3、设置调用线程,完成所有对象类元素的重绘,实现动态效果画面

我这里直接用面板实现Runnable接口, 在面板中添加线程。

面板类的全部代码如下: 

package com.my.plane;

import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.util.LinkedList;
import java.util.Random;

import javax.swing.JPanel;


import util.ImageUtil;

public class GameJPanel extends JPanel implements Runnable,KeyListener{
	Plane plane=new Plane();//这是玩家飞机
	Background bgck=new Background();
	Music music=new Music();
	
	LinkedList<Enemy> allEnemy=new LinkedList<Enemy>();//这是敌方飞机
	LinkedList<Boss> allBoss=new LinkedList<Boss>();
	LinkedList<Bullet> allBullet=new LinkedList<Bullet>();
	LinkedList<Boom> allBoom=new LinkedList<Boom>();
	//用这个四个bool类型来判断 plane(我方飞机)是否移动
	boolean left=false;
	boolean right=false;
	boolean up=false;
	boolean down=false;
	boolean bullet1=false;//用来选择子弹
	public static boolean gameover=false;
	
	
	//*******************
	
	int count=0; //计数器
	int count1=0;
	int count2=0;
	int over=0;
	
	Random rand=new Random();//用来产生随机数
	GameJPanel(){
		this.setFocusable(true);
		this.addKeyListener(this);
		music.bgmusic.loop();
		
		
	}
	public void paint(Graphics g) {
		
		bgck.drawbg(g);//绘制背景
		plane.drawplane(g);//绘制玩家飞机
		
		for(int i=0;i<allEnemy.size();i++) {//绘制敌方飞机
		   Enemy demo=allEnemy.get(i);
		   demo.drawEnemy(g);
		 }
		for(int i=0;i<allBoss.size();i++) {
			Boss now=allBoss.get(i);
			now.drawBoss(g);
			}   //绘制boss
		
		for(int i=0;i<allBullet.size();i++) {
			Bullet now=allBullet.get(i);
			now.bullet1=this.bullet1;
			now.drawBullet(g);  //绘制子弹
			}
		for(int i=0;i<allBoom.size();i++) {
				Boom now=allBoom.get(i);
			    now.drawBoom(g);   //绘制爆炸效果
			    
			}
		

		
		
	}

	
		public void run(){
		
		a:while(true) {
			
			count++;//设置计数器的变化 每循环一次 +1;
			count1++;
			count2++;
			
			this.repaint();
	//****************下方用来设置背景图片的移动*********
			bgck.move();
	//**********************************************
			
	//*********************下方是设置线程休息的时间*************
		try {
			Thread.sleep(20);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	//*****************************************
		
		
	//*************下方是来控制我方飞机的移动**********
		if(left) plane.px-=10;
		if(right) plane.px+=10;
		if(up)plane.py-=10;		
		if(down) plane.py+=10;	
	//************************************
	//*************下方是设置玩家飞机的移动,保证飞机坐标始终在窗口内*************8
	    if(plane.px>= GameJFrame.jx)  plane.px=20;
		if(plane.px<=0) plane.px=GameJFrame.jx;
		if(plane.py>=GameJFrame.jy-55) plane.py=GameJFrame.jy-55;
		if(plane.py<=0) plane.py=0;
	//***************玩家飞机设置到此结束***************************
		
    //***************************下面是增加敌方的飞机**********
		if(count>=20) {
			allEnemy.add(new Enemy(rand.nextInt(GameJFrame.jx-20),
					rand.nextInt(GameJFrame.jx-20),
					rand.nextInt(GameJFrame.jx-20),rand.nextInt(GameJFrame.jx-20),
					-40,-10,-15,-400));
			
			count=0;
		}
	//********************************************

	//*******************设置敌方飞机的移动**********
		for(int i=0;i<allEnemy.size();i++) {
			Enemy now=allEnemy.get(i);
		
			now.move(now.ey[0]+5,now.ey[1]+3,now.ey[2]+10,now.ey[3]+25);
			if(now.eh[0]>GameJFrame.jy&&now.eh[1]>GameJFrame.jy&&now.eh[2]>GameJFrame.jy&&
					now.eh[3]>GameJFrame.jy) {
				allEnemy.remove(i);
			}
				
		}
				
  //********************敌方飞机设置到此结束*************
		
//**********************下方是添加boss**********
		if(count1>=400) {
			allBoss.add(new Boss(rand.nextInt(GameJFrame.jx-500),-20));
			count1=0;}
	//********************************************
			for(int i=0;i<allBoss.size();i++) {
				Boss now=allBoss.get(i);
				now.move(now.by+2);
				if(now.by>GameJFrame.jy) {
					allBoss.remove(i);
	//*****************************BOSS类设置到此结束************		
				}
			}
			//*****************************BOSS类设置到此结束************	
		
		//*********下面是子弹类设置*****	
			int v=90;
		   if(bullet1) {
			   v=20;
		   
		   }
			if(count2>=v) {
				allBullet.add(new Bullet(plane.px+plane.pimage[0].getWidth()/2-10,
						                 plane.px+plane.pimage[0].getWidth()/2-45,
						                 plane.py, plane.py  ));
				music.bulletmusic.play();
				count2=0;
			}
			for(int i=0;i<allBullet.size();i++) {
				Bullet now=allBullet.get(i);
				now.move();
				if(now.by[0]<=0&&now.by[1]<=0) {
					allBullet.remove(i);
		}	
		}
//子弹类设置的到此结束***********************************************
			
			
			
			for(int i=0;i<allEnemy.size();i++) {
				Enemy now1=allEnemy.get(i);
				for(int j=0;j<allBullet.size();j++) {
					Bullet now=allBullet.get(j);
					
						if(checkHit(now1.ex[0],now1.ey[0],now1.ew[0],now1.eh[0],
									now.bx[0],now.by[0],now.bw[0],now.bh[0])) {
							
							allEnemy.remove(i);
							allBullet.remove(j);
							allBoom.add(new Boom(now.bx[0],now.by[0]));
							music.boommusic.play();
							}
						else	if(	checkHit(now1.ex[1],now1.ey[1],now1.ew[1],now1.eh[1],
											now.bx[0],now.by[0],now.bw[0],now.bh[0])) {

									allEnemy.remove(i);
									allBullet.remove(j);
									allBoom.add(new Boom(now.bx[0],now.by[0]));
									music.boommusic.play();
									}
								
						else	if(checkHit(now1.ex[2],now1.ey[2],now1.ew[2],now1.eh[2],
													now.bx[0],now.by[0],now.bw[0],now.bh[0]) ){
								
								allEnemy.remove(i);
								allBullet.remove(j);
								allBoom.add(new Boom(now.bx[0],now.by[0]));
								music.boommusic.play();
										}
						else if(		checkHit(now1.ex[3],now1.ey[3],now1.ew[3],now1.eh[3],
															now.bx[0],now.by[0],now.bw[0],now.bh[0])) {

								allEnemy.remove(i);
								allBullet.remove(j);
								allBoom.add(new Boom(now.bx[0],now.by[0]));
								music.boommusic.play();
								}
						else	if(	checkHit(now1.ex[0],now1.ey[0],now1.ew[0],now1.eh[0],
																	now.bx[1],now.by[1],now.bw[1],now.bh[1])) {
									allEnemy.remove(i);
									allBullet.remove(j);
									allBoom.add(new Boom(now.bx[1],now.by[1]));
									music.boommusic.play();
								}
						else if(	checkHit(now1.ex[1],now1.ey[1],now1.ew[1],now1.eh[1],
											now.bx[1],now.by[1],now.bw[1],now.bh[1])) {
									allEnemy.remove(i);
									allBullet.remove(j);
									allBoom.add(new Boom(now.bx[1],now.by[1]));
									music.boommusic.play();
								}
						else	if(	checkHit(now1.ex[2],now1.ey[2],now1.ew[2],now1.eh[2],
											now.bx[1],now.by[1],now.bw[1],now.bh[1])) {
									allEnemy.remove(i);
									allBullet.remove(j);
									allBoom.add(new Boom(now.bx[1],now.by[1]));
									music.boommusic.play();
								}
						else if (checkHit(now1.ex[3],now1.ey[3],now1.ew[3],now1.eh[3],
											now.bx[1],now.by[1],now.bw[1],now.bh[1])){
								 allEnemy.remove(i);
									allBullet.remove(j);
									allBoom.add(new Boom(now.bx[1],now.by[1]));
									music.boommusic.play();
							 }
						
						   }
						}
			for(int i=0;i<allBoom.size();i++) {
				Boom now=allBoom.get(i);
			if(now.count>=7) {
				allBoom.remove(i);
				
			}
			}		
			over++;
			//这里当敌方飞机与玩家飞机相撞则宣布 gameover
		if(over>500) {	for(int i=0;i<allEnemy.size();i++) {
				Enemy now1=allEnemy.get(i);
				if(checkHit(now1.ex[0],now1.ey[0],now1.ew[0],now1.eh[0],
						plane.px,plane.py,plane.pw,plane.ph)||checkHit(now1.ex[1],now1.ey[1],now1.ew[1],now1.eh[1],
						plane.px,plane.py,plane.pw,plane.ph)||checkHit(now1.ex[2],now1.ey[2],now1.ew[2],now1.eh[2],
						plane.px,plane.py,plane.pw,plane.ph)) {
					allBoom.addFirst(new Boom(plane.px,plane.py));
					gameover=true;
					repaint();
					music.bgmusic.stop();
					break a;
					
				}

					}
			
			
		}
		}
						
					}
				
		
		
		public boolean checkHit(int r1x,int r1y,int r1w,int r1h,
				int r2x,int r2y,int r2w,int r2h) {
			if(r1x>r2x+r2w||r1x+r1w<r2x||r1y>r2y+r2h||r1y+r1h<r2y)
				return false;
				else return true;
		}
	

	



	@Override
	public void keyPressed(KeyEvent e) {
		
		switch(e.getKeyChar()) {
		case 'a':this.left=true;
			break;
		case 'd':this.right=true;
			break;
		case 'w':this.up=true;
			break;
		case 's':this.down=true;
			break;
		case 'j':this.bullet1=true;
		
	    break;
	  }		
		
	}


	@Override
	public void keyReleased(KeyEvent e) {
		
		switch(e.getKeyChar()) {
	case	'a':this.left=false;
	break;
    case 'd':this.right=false;
	break;
    case 'w':this.up=false;
	break;
    case 's':this.down=false;
	break;
    case 'j':this.bullet1=false;
    break;
   
  
 
	}
}		

	@Override
	public void keyTyped(KeyEvent e) {
	
	}
	}

【面板中无非实现两个效果一:使用线程改变各类对象元素的坐标,调用repaint实现元素坐标重绘,二:将飞机类 X Y 坐标的移动绑定到键盘事件上,实现键盘 wasd 控制上下左右。 代码虽长,但很容易理解。】

此时只需要创建一个窗口类将面板添加进去即可,窗口代码如下

package com.my.plane;

import java.applet.Applet;
import java.applet.AudioClip;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;

import javax.swing.JFrame;

import util.ImageUtil;

public class GameJFrame extends JFrame{
		GameJPanel gamePanelnew =new GameJPanel();
		Thread move=new Thread(gamePanelnew);
		
		
		public static int jx=ImageUtil.img.get("bg.jpg").getWidth();
		public static int jy=ImageUtil.img.get("bg.jpg").getHeight();
	@SuppressWarnings("deprecation")
	GameJFrame(){
		this.setTitle("--雷霆战机--(自写原创源码,作者See:QQ:3492625357)");
		this.setSize(jx, jy+90);
		this.setLocationRelativeTo(null);	
		this.setVisible(true);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	  this.add(gamePanelnew);		
	  
	}

	}

  这时候随便创建一个游戏入口类即主类,创建这个窗口即可

package com.my.plane;

import javax.swing.JFrame;

import util.ImageUtil;

public class Game_Main {
	public static void main(String[] args) {
		JFrame game=new GameJFrame();
				
	}

}

 到这里,所有的项目就大功告成了!!这时候运行你的主类,就可以玩属于你自己雷霆战机啦!是不是有 原来如此的感受呢。

看似华丽的背后,仅仅只是一行行简单的代码实现的。加油!!!

最后我已经将有所有源代码和游戏所需要的素材全部打包了,这些素材是我在网络上找了很多的素材的合集,内容非常丰富,如果有需要的,可以添加我的  QQ3492625357 

原创不易,你阅读我文章亦如你与我交谈一般,交谈即为交友,朋友,离别之际请给予你的朋友一个赞,好吗! 后会有期!

 

 

 

 

 

 

 

 

 

©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值