一、代码实现

创建窗口

首先创建一个游戏窗体类gameframe,继承至jframe,用来显示在屏幕上(window的对象),每个游戏都有一个窗口,设置好窗口标题、尺寸、布局等就可以。

/*
 * 游戏窗体类
 */
public class gameframe extends jframe {
	
	public gameframe() {
		settitle("飞机大战");//设置标题
		setsize(526, 685);//设定尺寸
		setlayout(new borderlayout());
		setdefaultcloseoperation(jframe.exit_on_close);//点击关闭按钮是关闭程序
        setlocationrelativeto(null);   //设置居中
    	setresizable(false); //不允许修改界面大小
	}
}

创建面板容器gamepanel继承至jpanel

package main;

import java.awt.graphics;
import javax.swing.jframe;
import javax.swing.jpanel;
/*
 * 画布类
 */
public class gamepanel extends jpanel{
	gamepanel gamepanel=this;
	private jframe mainframe=null;
	//构造里面初始化相关参数
	public gamepanel(jframe frame){
		this.setlayout(null);
		mainframe = frame;
		
		mainframe.setvisible(true);
	}
	
	@override
	public void paint(graphics g) {
		
	}
}

再创建一个main类,来启动这个窗口,用来启动。

package main;
public class main {
	//主类
	public static void main(string[] args) {
		gameframe frame = new gameframe();
		gamepanel panel = new gamepanel(frame);
		frame.add(panel);
		frame.setvisible(true);//设定显示
	}
}

右键执行这个main类,窗口建出来了

​​

二、创建菜单及菜单选项

创建菜单

private void  initmenu(){
	// 创建菜单及菜单选项
	jmb = new jmenubar();
	jmenu jm1 = new jmenu("游戏");
	jm1.setfont(new font("微软雅黑", font.bold, 15));// 设置菜单显示的字体
	jmenu jm2 = new jmenu("帮助");
	jm2.setfont(new font("微软雅黑", font.bold, 15));// 设置菜单显示的字体
	
	jmenuitem jmi1 = new jmenuitem("开始新游戏");
	jmenuitem jmi2 = new jmenuitem("退出");
	jmi1.setfont(new font("微软雅黑", font.bold, 15));
	jmi2.setfont(new font("微软雅黑", font.bold, 15));
	
	jmenuitem jmi3 = new jmenuitem("操作说明");
	jmi3.setfont(new font("微软雅黑", font.bold, 15));
	jmenuitem jmi4 = new jmenuitem("胜利条件");
	jmi4.setfont(new font("微软雅黑", font.bold, 15));
	
	jm1.add(jmi1);
	jm1.add(jmi2);
	
	jm2.add(jmi3);
	jm2.add(jmi4);
	
	jmb.add(jm1);
	jmb.add(jm2);
	mainframe.setjmenubar(jmb);// 菜单bar放到jframe上
	jmi1.addactionlistener(this);
	jmi1.setactioncommand("restart");
	jmi2.addactionlistener(this);
	jmi2.setactioncommand("exit");
	
	jmi3.addactionlistener(this);
	jmi3.setactioncommand("help");
	jmi4.addactionlistener(this);
	jmi4.setactioncommand("win");
}

实现actionlistener并重写方法actionperformed

actionperformed方法的实现

@override
public void actionperformed(actionevent e) {

	string command = e.getactioncommand();
	uimanager.put("optionpane.buttonfont", new fontuiresource(new font("宋体", font.italic, 18)));
	uimanager.put("optionpane.messagefont", new fontuiresource(new font("宋体", font.italic, 18)));
	if ("exit".equals(command)) {
		object[] options = { "确定", "取消" };
		int response = joptionpane.showoptiondialog(this, "您确认要退出吗", "",
				joptionpane.yes_option, joptionpane.question_message, null,
				options, options[0]);
		if (response == 0) {
			system.exit(0);
		} 
	}else if("restart".equals(command)){
		if(startflag){
			object[] options = { "确定", "取消" };
			int response = joptionpane.showoptiondialog(this, "游戏中,您确认要重新开始吗", "",
					joptionpane.yes_option, joptionpane.question_message, null,
					options, options[0]);
			if (response == 0) {
				//需要先结束游戏
				realgameend(1);
				restart();
			} 
		}else{
			restart();
		}
	}else if("help".equals(command)){
		joptionpane.showmessagedialog(null, "游戏开始后,要先动鼠标到飞机处,触发移动效果,然后飞机就会跟随鼠标移动!",
				"提示!", joptionpane.information_message);
	}else if("win".equals(command)){
		joptionpane.showmessagedialog(null, "得分1000,获得胜利!",
				"提示!", joptionpane.information_message);
	}
}

三、创建背景

在gamepanel类中重写paint方法,绘制背景图即可

//绘图方法
@override
public void paint(graphics g) {
	gameheight = this.getheight();
	gamewidth = this.getwidth();
	//绘制背景
	g.drawimage((bufferedimage)imagemap.get("bg"), 0, -150, null);
}

四、开启主线程

主线程,用来重绘页面,重绘全部交给主线程,主线程调用 repaint方法就行,要产生动画就要靠这个repaint。

//刷新线程,用来重新绘制页面
private class refreshthread implements runnable {
	@override
	public void run() {
		while (startflag) {
			repaint();
			try {
				thread.sleep(50);
			} catch (interruptedexception e) {
				e.printstacktrace();
			}
		}
	}
}

在gamepanel的构造里面启动这个主线程

有了这个主线程刷新,待会我们更新飞机的位置,飞机就会移动,不需要另外的代码去调用repaint方法了(这是我的做法,仅供参考)。

五、创建我方飞机

创建myplane类,属性有坐标x、y,宽高、图片、是否存活、是否可以移动等;方法主要有绘制、移动、射击等。

public class myplane {

	private int x = 0;
	private int y = 0;
	private int width = 0;
	private int height = 0;
	private bufferedimage image = null;
	private gamepanel panel=null;
	private hashmap imagemap=null;
	private boolean alive=true;
	private boolean canmove=false;
	private int key=1;
	private hashmap boomimagemap=null;
	private boolean hitflag=false;//正在碰撞
	
	public myplane(int x,int y,int width,int height,gamepanel panel) {
		this.x=x;
		this.y=y;
		this.width=width;
		this.height=height;
		this.panel=panel;
		this.imagemap=panel.imagemap;
		this.image=(bufferedimage)imagemap.get("myplane1");
		this.boomimagemap=panel.mypalneboomimagemap;
		
	}
	//绘制
	public void draw(graphics g) {
		g.drawimage(image, x, y, width,height, null);
	}
}

创建(这里只是创建好了飞机对象,需要绘制)

//创建自己飞机
private void initmyplane() {
	myplane = new myplane(200, 530, 132, 86, this);
}

在paint方法中绘制

//绘图方法
@override
public void paint(graphics g) {
	gameheight = this.getheight();
	gamewidth = this.getwidth();
	//绘制背景
	g.drawimage((bufferedimage)imagemap.get("bg"), 0, -150, null);
	
	//绘制飞机
	if(myplane!=null){
		myplane.draw(g);
	}
}

六、鼠标事件监听

加入监听是为了让飞机跟随鼠标移动,我这里定的规则是第一次鼠标必须移动到飞机上,然后飞机才会跟随。

代码里面用一个属性canmove来控制,默认是false,只有鼠标第一次移入到飞机上时,这个属性设置为true,然后就可以跟随鼠标移动了。

//鼠标事件的创建
private void createmouselistener() {
	mouseadapter mouseadapter = new mouseadapter() {
		@override
		public void mousemoved(mouseevent e) {
			int x = e.getx();
			int y = e.gety();
			if(myplane==null) return ;
			//飞机第一次是不允许移动的,只有飞机的canmove为true才去跟随
			if(myplane.iscanmove()){
				myplane.move(x,y);
				return;
			}
			//判断鼠标的移入,如果移动到飞机上则canmove设置为true
			if(myplane.ispoint(x,y)){
				myplane.setcanmove(true);
			}
		}
	};
	addmousemotionlistener(mouseadapter);
	addmouselistener(mouseadapter);
}

来实现一下myplane的move方法,这里处理了边界,保证飞机不出界,同时保证鼠标在飞机的中间位置

//飞机跟随鼠标移动
public void move(int x,int y) {
	//判断范围,当横向移动在窗口范围内
	if(x-width/2>=0 && x<=panel.getwidth()-width/2){
		this.x=x-width/2;
	}
	//判断范围,当纵向移动在窗口范围内
	if(y-height/2>=0 && y<=panel.getheight()-height/2){
		this.y=y-height/2;
	}
}

七、创建子弹类

属性也就是坐标、宽高这些,给子弹加入移动方法

//移动
void move(){
	new thread(new runnable() {
		@override
		public void run() {
			while(alive){
				y-=speed;
				if(y<=0){
					clear();
				}
				
				try {
					thread.sleep(50);
				} catch (interruptedexception e) {
					e.printstacktrace();
				}
			}
		}
	}).start();
}

飞机类加入射击方法,200毫秒创建一发子弹

//射击
void shoot() {
	new thread(new runnable() {
		@override
		public void run() {
			while(alive){
				//创建子弹
				createbullet();
				try {
					thread.sleep(200);
				} catch (interruptedexception e) {
					e.printstacktrace();
				}
			}
		}

		private void createbullet() {
			bullet bullet = new bullet(x+width/2-10, y, 20, 30, panel);
			panel.bulletlist.add(bullet);
			new musicplayer("/music/shoot.wav").play();
		}
	}).start();
}

别忘记在paint方法里面绘制子弹出来

//绘制子弹
bullet bullet=null;
for (int i = 0; i < bulletlist.size(); i++) {
	bullet = (bullet)bulletlist.get(i);
	bullet.draw(g);
}

八、创建敌机

创建抽象类plane

package main;

import java.awt.graphics;

public abstract class plane {
	public abstract void move();
	public abstract void draw(graphics g);
	public abstract void boom();
	public abstract void clear();
	
	abstract int getx();
	abstract int gety();
	abstract int getwidth();
	abstract int getheight();
}

创建敌机子类

有个特殊一点的地方: 因为有4种敌机,这里随机1、2、3、4这4个数字作为属性index,然后根据这个index来展现不同的飞机图片,当然也可以通过这个index来设置敌机不同的移动速度,但是我为了偷懒,就全部一样的移动速度,嘿嘿。
移动就是开启线程让y坐标增加,没什么好讲的,这里加一个飞机碰撞,就是当敌机跟我方飞机如何判断碰撞的问题。

撞机分析(敌机与我机的撞机)

从上面几个图可看出什么?因为图片是方形的,他们的4个顶点一定至少有一个在对方的范围内。再看一下从左边撞击的图:

从上图看到也是这样,其他两个方向的也是一样的道理,为了稳点我还加了一种情况:

1.判断敌机的4个点是否在飞机范围内,如果有则表示碰撞了。
2.如果1不成立,则反过来,判断我机的4个点是否在敌机的范围内,如果是表示碰撞了。

//判断飞机与子弹是否碰撞
private boolean ispoint(myplane plane) {
	/*
	 * 
	 * 两种情况
	 * 1.需要判断敌机的4个点是否在飞机范围内,如果有则表示碰撞了
	 * 2.如果步骤1不成立,则反过来,判断我机的4个点是否在敌机的范围内,如果是标志碰撞了
	*/
	
	//方式1
	
	//左上角
	int x1 = x;
	int y1 = y;
	//右上角
	int x2 = x+width;
	int y2 = y;
	//右下角
	int x3 = x+width;
	int y3 = y+height;
	//左下角
	int x4 = x;
	int y4 = y+height;
	//只要有一个点在范围内,则判断为碰撞
	if(comparepointmyplane(x1,y1,plane)|| comparepointmyplane(x2,y2,plane)||comparepointmyplane(x3,y3,plane)||comparepointmyplane(x4,y4,plane) ){
		return true;
	}
	
	//方式1没成立则用方式2判断
	
	//方式2
	x1 = plane.getx();
	y1 = plane.gety();
	//右上角
	x2 = plane.getx()+plane.getwidth();
	y2 = plane.gety();
	//右下角
	x3 = plane.getx()+plane.getwidth();
	y3 =plane.gety()+plane.getheight();
	//左下角
	x4 = plane.getx();
	y4 = plane.gety()+plane.getheight();
	if(comparepoint(x1,y1)|| comparepoint(x2,y2)||comparepoint(x3,y3)||comparepoint(x4,y4) ){
		return true;
	}
	return false;
}
//用敌机的坐标来判断
private boolean comparepointmyplane(int x,int y,myplane plane){
	//大于左上角,小于右下角的坐标则肯定在范围内
	if(x>plane.getx() && y >plane.gety()
		&& x<plane.getx()+plane.getwidth() && y <plane.gety()+plane.getheight()	){
		return  true;
	}
	return false;
}
//用我机的坐标来判断
private boolean comparepoint(int x,int y){
	//大于左上角,小于右下角的坐标则肯定在范围内
	if(x>this.x && y >this.y
		&& x<this.x+this.width && y <this.y+this.height){
		return  true;
	}
	return false;
}

测试一下效果

忘记说击中敌机的了(原理跟刚才差不多,代码直接放了)

//判断击中敌机
protected void hitenemy() {
	enemyplane enemyplane=null;
	list enemys = panel.enemylist;
	for (int i = 0; i < enemys.size(); i++) {
		try {
			enemyplane = (enemyplane)enemys.get(i);
		} catch (exception e) {
		}
		if(enemyplane==null) continue;
		if(this.ispoint(enemyplane)){
			
			panel.curcount+=enemyplane.getcount();
			//删除当前子弹
			clear();
			
			//飞机爆炸
			enemyplane.boom();
			
			if(panel.curcount>=panel.totalcount){
				panel.myplane.setcanmove(false);
				panel.gamewin();
			}
		}
	}
}

//判断飞机与子弹是否碰撞
private boolean ispoint(enemyplane plane) {
	//因为子弹比飞机小,所以只需要判断子弹的4个点是否在飞机范围内,如果有则表示碰撞了
	//左上角
	int x1 = x;
	int y1 = y;
	//右上角
	int x2 = x+width;
	int y2 = y;
	//右下角
	int x3 = x+width;
	int y3 = y+height;
	//左下角
	int x4 = x;
	int y4 = y+height;
	//只要有一个点在范围内,则判断为碰撞
	if(comparepoint(x1,y1,plane)|| comparepoint(x2,y2,plane)||comparepoint(x3,y3,plane)||comparepoint(x4,y4,plane) ){
		return true;
	}
	return false;
}

private boolean comparepoint(int x,int y,enemyplane plane){
	//大于左上角,小于右下角的坐标则肯定在范围内
	if(x>plane.getx() && y >plane.gety()
		&& x<plane.getx()+plane.getwidth() && y <plane.gety()+plane.getheight()	){
		return  true;
	}
	return false;
}

最后加上计分的、胜利、失败等提示就完成了!

到此这篇关于一天时间用java写了个飞机大战,朋友直呼高手的文章就介绍到这了,更多相关java飞机大战内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!