一天时间用Java写了个飞机大战游戏,朋友直呼高手

网友投稿 292 2022-10-22


一天时间用Java写了个飞机大战游戏,朋友直呼高手

一、代码实现

创建窗口

首先创建一个游戏窗体类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);

http://}

}

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

//创建自己飞机

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

return true;

}

return false;

}

//用我机的坐标来判断

private boolean comparePoint(int x,int y){

//大于左上角,小于右下角的坐标则肯定在范围内

if(x>this.x && y >this.y

&& x

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

return true;

}

return false;

}

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

return true;

}

return false;

}

//用我机的坐标来判断

private boolean comparePoint(int x,int y){

//大于左上角,小于右下角的坐标则肯定在范围内

if(x>this.x && y >this.y

&& x

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

return true;

}

return false;

}

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

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

return true;

}

return false;

}

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

return true;

}

return false;

}

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


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:新版计算机专业课-四大金刚 33G计算机网络+操作系统+组成原理+数据结构课程 必备基础
下一篇:行业 | 王思聪的熊猫直播再融10亿,直播平台未来发展只剩两条路
相关文章

 发表评论

暂时没有评论,来抢沙发吧~