一、项目分析
1、设计步骤
在一个Java Swing的小游戏中,首先需要考虑的是组件的绘制,其次是如何让需要运动的组件运动起来,最后是如何检测物体的碰撞等。
(1)、组件绘制:这一步需要将游戏界面中的组件全部绘制出来,包括背景、飞机、子弹、文字等。
(2)、组件运动:游戏开始,首先需要背景滚动起来,其次是飞机、子弹等物体需要运动,这里需要使用多线程控制不同组件的运动状态。
(3)、组件碰撞:这一步需要对物体进行碰撞检测,以便在后续结算过程能得到数据,以及判断飞机是否被击落等情况。
2、知识点
(1)、面向对象的开发思想和过程,其中包括封装、接口实现和继承等。
(2)、Java Swing使用,其中包括窗口创建、图形绘制等。
(3)、ArrayList使用,使用ArrayList存储并读取对象等。
(4)、多线程技术,通过多线程控制不同的组件完成不同的效果
二、代码实现
1、主程序入口
public class GameMain extends JFrame implements config{
static int gameStatus = 0; //游戏状态码,0表示未开始
BufferedImage BFI; //图像双缓冲
GameListener gl; //当前界面的监听器
//窗口设置
public void setWindow(int width , int height){
//窗口标题
this.setTitle("飞机大战");
//窗口大小设置
Dimension d = this.getToolkit().getScreenSize();
Rectangle r = new Rectangle((d.width - width) / 2 , (d.height - height) / 2 , width , height);
this.setBounds(r);
//设置窗口可见
this.setVisible(true);
//无法调节窗口大小
this.setResizable(false);
//窗口关闭状态
this.setDefaultCloseOperation(3);
//窗口图像缓冲,在重绘组件时防止闪烁
BFI = new BufferedImage(windowsWidth , windowsHeight , 2);
}
//添加监听器
public void addListener(){
//获取界面画笔
Graphics g = this.getGraphics();
//将画笔传给监听器,在监听到鼠标时绘制相应组件
gl = new GameListener(g);
//在窗口添加监听器
this.addMouseListener(gl);//鼠标点击事件
this.addMouseMotionListener(gl);//鼠标滑动事件
this.addKeyListener(gl);//键盘输入事件
}
//设置窗口重绘功能,保证在最小化后图像依然存在
@Override
public void paint(Graphics g) {
//游戏未开始时绘制开始界面
if(gameStatus == 0){
g.drawImage(backGround , 0 , 0 , null);
g.drawImage(plane0 , 80 , 40 , null);
g.setColor(Color.ORANGE);
g.setFont(new Font("Strasua" , Font.BOLD , 35));
g.drawString("CLICK TO START" , 75 , 500);
}
}
//初始化函数
public void init(){
setWindow(windowsWidth , windowsHeight);
addListener();
}
public static void main(String[] args) {
GameMain gm = new GameMain();
gm.init();
}
}
2、监听器
public class GameListener extends JPanel implements KeyListener, MouseListener , MouseMotionListener , config{
//设置监听器画笔
Graphics drawObj;
//设置游戏状态
static int gameStatus;
//键盘按键代码
static int keyCode;
public GameListener(Graphics g){
this.drawObj = g;
}
public void keyTyped(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
//设置游戏暂停功能(按下空格)
keyCode = e.getKeyCode();
if(e.getKeyCode() == 32){
switch (gameStatus){
case 1:
gameStatus = 0;
break;
case 0:
gameStatus = 1;
break;
}
}
}
public void keyReleased(KeyEvent e) {
}
public void mouseClicked(MouseEvent e) {
//当状态码为1时启动绘制组件的线程
if(e.getButton() == 1 && gameStatus == 0 && keyCode != 32){
gameStatus = 1;
objMotion();
}
if(keyCode == 32){
System.out.println("暂停");
}
}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mouseDragged(MouseEvent e){}
public void mouseMoved(MouseEvent e){
//捕捉鼠标的坐标
if(gameStatus == 1){
objectMotion.setX(e.getX());
objectMotion.setY(e.getY());
}
}
}
3、游戏对象类
public class GameObject extends JPanel implements config{
Image img;
int x , y , width , height;
double speedX , speedY;
//一下均为对象类的构造器,每个构造器所含参数不同,根据每个对象的需求使用。
public GameObject(){}
public GameObject(Image img , int x , int y , int width , int height , double speedY){
this.img = img;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.speedY = speedY;
}
public GameObject(Image img , int x , int y){
this.img = img;
this.x = x;
this.y = y;
}
public GameObject(Image img, int x, int y, int width, int height, double speedX, double speedY) {
this.img = img;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.speedX = speedX;
this.speedY = speedY;
}
public GameObject(Image img, int x, int y, double speedX, double speedY) {
this.img = img;
this.x = x;
this.y = y;
this.speedX = speedX;
this.speedY = speedY;
}
public GameObject(Image img, int x, int y, double speedY) {
this.img = img;
this.x = x;
this.y = y;
this.speedY = speedY;
}
//图片
public Image getImg() {
return img;
}
public void setImg(Image img) {
this.img = img;
}
//物体坐标
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
//物体大小
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
//物体速度
public double getSpeedX() {
return speedX;
}
public void setSpeedX(double speedX) {
this.speedX = speedX;
}
public double getSpeedY() {
return speedY;
}
public void setSpeedY(double speedY) {
this.speedY = speedY;
}
//对象的绘制方法
public void paintSelf(Graphics g){
g.drawImage(img , x , y , null);
}
//获取对象的矩形,后续进行碰撞检测
public Rectangle getRec(){
return new Rectangle(this.x , this.y , this.width , this.height);
}
}
4、我方战机类
public class PlayerPlane extends GameObject implements config{
BufferedImage BFI3;
static Rectangle enePlaneBullet;
//设置玩家机生命
static int life = 100;
//玩家机构造器
public PlayerPlane(Image img, int x, int y, int width, int height, double speedX, double speedY) {
super(img, x, y, width, height, speedX, speedY);
}
public PlayerPlane(Image img , int x , int y , int width , int height){
this.img = img;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public PlayerPlane(Image img, int x, int y) {
super(img, x, y);
}
public PlayerPlane(){}
public PlayerPlane(Image img, int x, int y, double speedX, double speedY) {
super(img, x, y, speedX, speedY);
}
public void setLife(int life){
this.life = life;
}
public int getLife(){
return life;
}
@Override
public int getX() {
return super.getX() - 38;
}
@Override
public void setX(int x) {
super.setX(x);
}
@Override
public int getY() {
return super.getY() - 38;
}
@Override
public void setY(int y) {
super.setY(y);
}
@Override
public void paintSelf(Graphics g) {
g.drawImage(img , getX() , getY() , null);
}
@Override
public Rectangle getRec() {
return super.getRec();
}
}
5、我方战机子弹
public class Bullet extends GameObject implements config{
public Bullet(){}
public Bullet(Image img, int x, int y, int width, int height, double speedX, double speedY) {
super(img, x, y, width, height, speedX, speedY);
}
public Bullet(Image img, int x, int y, double speedX, double speedY) {
super(img, x, y, speedX, speedY);
}
@Override
public int getX() {
return super.getX();
}
@Override
public void setX(int x) {
super.setX(x);
}
@Override
public int getY() {
return super.getY();
}
@Override
public void setY(int y) {
super.setY(y);
}
@Override
public void paintSelf(Graphics g) {
y += speedY; //子弹速度设置
g.drawImage(img , x , y , null);
}
@Override
public Rectangle getRec() {
return super.getRec();
}
}
6、敌方战机
public class enePlaneObj extends GameObject implements config{
static Rectangle playerPlane;
static Bullet playerPlaneBullet;
PlayerPlane playerPlane01 = new PlayerPlane();
Score score = new Score();
public enePlaneObj (){}
public enePlaneObj(Image img, int x, int y, int width, int height, double speedX, double speedY) {
super(img, x, y, width, height, speedX, speedY);
}
//敌机矩形
public void setPlayerPlane(Rectangle playerPlane) {
this.playerPlane = playerPlane;
}
public void setPlayerPlaneBullet(Bullet playerPlaneBullet){
this.playerPlaneBullet = playerPlaneBullet;
}
@Override
public void setSpeedX(double speedX) {
super.setSpeedX(speedX);
}
@Override
public void setSpeedY(double speedY) {
super.setSpeedY(speedY);
}
@Override
public void paintSelf(Graphics g) {
x += speedX;
y += speedY;
g.drawImage(img , x , y , null);
//敌机与玩家机碰撞检测
//新建碰撞检测对象
rectCross rectCross = new rectCross(this.getX() , this.getY() , this.getWidth() , this.getHeight() , (int)(playerPlane.getX()) - 38 , (int)(playerPlane.getY()) - 38, (int)(playerPlane.getWidth()) - 19 , (int)(playerPlane.getHeight()) - 19);
//发生碰撞后,敌机消失,分数增加
if( rectCross.cross() == true){
g.drawImage(explode , this.getX() - 50 , this .getY() - 50 , null);
playerPlane01.setLife(playerPlane01.getLife() - 4);
score.setScore(score.getScore() + 1);
this.setX(-100);
this.setY(100);
}
//敌机与玩家子弹碰撞检测
for(Bullet bullet : bulletList){
rectCross rectCross1 = new rectCross(this.getX() , this.getY() , this.getWidth() , this.getHeight() , bullet.getX() , bullet.getY() , bullet.getWidth() , bullet.getHeight());
if(rectCross1.cross() == true){
g.drawImage(explode , this.getX() - 50 , this .getY() - 50 , null);
score.setScore(score.getScore() + 1);
bullet.setX(-100);
bullet.setY(100);
this.setX(-200);
this.setY(200);
}
}
}
@Override
public Rectangle getRec() {
return super.getRec();
}
}
7、敌方子弹
public class eneBulletObject extends GameObject{
static Rectangle playerPlane;
PlayerPlane playerPlane02 = new PlayerPlane();
public eneBulletObject(Image img, int x, int y, int width, int height, double speedX, double speedY) {
super(img, x, y, width, height, speedX, speedY);
}
public eneBulletObject (){}
public void setPlayerPlane(Rectangle playerPlane){
this.playerPlane = playerPlane;
}
@Override
public void paintSelf(Graphics g) {
y += speedY;
g.drawImage(img , x , y , null);
//敌机子弹与玩家机碰撞
rectCross rectCross = new rectCross(this.getX() , this.getY() , this.getWidth() , this.getHeight() , (int)(playerPlane.getX()) - 38 , (int)(playerPlane.getY()) - 38, (int)(playerPlane.getWidth()) - 19 , (int)(playerPlane.getHeight()) - 19);
if(rectCross.cross() == true){
playerPlane02.setLife(playerPlane02.getLife() - 1);
this.setX(-100);
this.setY(100);
}
}
@Override
public Rectangle getRec() {
return super.getRec();
}
}
8、启动线程
public class objectMotion extends JPanel implements config , Runnable {
Graphics objMove; //设置画笔
BufferedImage BFI2;//图片双缓冲
static int count = 0;//对象创建计数
int x , eneBulletX , bossPlaneX;
int y , eneBulletY , bossPlaneY;
static int bossBulletGo;
Random r = new Random();
backGroundObj backGroundObj = new backGroundObj(backGround ,0 , -1200 , 5);
enePlaneObj enePlaneObj = new enePlaneObj();
eneBulletObject eneBulletObject = new eneBulletObject();
PlayerPlane playerPlane01 = new PlayerPlane();
Score newSc = new Score();
bossPlane bossPlane = new bossPlane();
BossBullet boss_Bullet = new BossBullet();
public objectMotion(){}
public objectMotion(Graphics g){
this.objMove = g;
}
@Override
public int getX() {
return x;
}
//我方战机坐标
public void setX(int x) {
this.x = x;
}
@Override
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
@Override
public void run() {
rolling();
}
public void rolling(){
while (true){
FlyObjectCreate();
PlayerPlane playerPlane = new PlayerPlane(plane1 , getX() , getY() , 76 , 76);
PlayerPlane playerPlane2 = new PlayerPlane(plane1 , playerPlane.getX() + 38 , playerPlane.getY() + 38, 76 , 76);
playerPlaneList.add(playerPlane2);
BFI2 = new BufferedImage(windowsWidth , windowsHeight , 2);
backGroundObj.paintSelf(objMove);
playerPlane.paintSelf(objMove);
//玩家机列表
for (int i = 0; i < playerPlaneList.size(); i++) {
enePlaneObj.setPlayerPlane(playerPlaneList.get(i).getRec());
eneBulletObject.setPlayerPlane(playerPlaneList.get(i).getRec());
bossPlane.setPlayerplane(playerPlaneList.get(i).getRec());
boss_Bullet.setPlayerPlane(playerPlaneList.get(i).getRec());
}
//玩家机子弹
for (int i = 0; i < bulletList.size(); i++) {
bulletList.get(i).paintSelf(objMove);
}
//敌机列表
for (int i = 0; i < enePlaneList.size(); i++) {
enePlaneList.get(i).paintSelf(objMove);
eneBulletX = enePlaneList.get(i).getX();
eneBulletY = enePlaneList.get(i).getY();
}
//敌机子弹列表
for (int i = 0; i < eneBulletList.size(); i++) {
eneBulletList.get(i).paintSelf(objMove);
}
//boss机列表
for (int i = 1; i < bossPlaneList.size(); i++) {
bossPlaneList.get(i).paintSelf(objMove);
bossPlaneX = bossPlaneList.get(i).getX();
bossPlaneY = bossPlaneList.get(i).getY();
bossBulletGo = 1;
}
//boss机子弹集合
for (int i = 0; i < bossBulletList.size(); i++) {
bossBulletList.get(i).paintSelf(objMove);
}
repaint();
count ++;
try {
scorePanel();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Thread.sleep(35);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void FlyObjectCreate(){
int rEne = r.nextInt(400);
//敌机列表
if (count % 15 == 0){
enePlaneList.add(new enePlaneObj(enemyPlane1 , rEne , 0 , 64 , 64 , 0 , 3));
}
//我方战机子弹列表
if (count % 9 == 0){
bulletList.add(new Bullet(bullet001, getX() - 10 , getY() - 38, 20 , 30 , 0 , -5));
}
//敌机子弹列表
if (count % 15 == 0){
eneBulletList.add(new eneBulletObject(eneBullet , eneBulletX + 28, eneBulletY + 32 , 9 , 21, 0 , 5));
}
//boss机
if (count % 901 == 0){
bossPlaneList.add(new bossPlane(plane0 , 80 , -50 , 259 , 196 , 0 , 1));
}
if(count % 65 == 0){
if(bossBulletGo == 1){
bossBulletList.add(new BossBullet(bossBullet , bossPlaneX + 16, bossPlaneY + 95, 20 , 24 , 0 , 5));
bossBulletList.add(new BossBullet(bossBullet , bossPlaneX + 83, bossPlaneY + 125, 20 , 24 , 0 , 5));
bossBulletList.add(new BossBullet(bossBullet , bossPlaneX + 150, bossPlaneY + 95, 20 , 24 , 0 , 5));
}
}
}
public void scorePanel() throws InterruptedException {
objMove.setColor(Color.RED);
objMove.setFont(new Font("Strasua" , Font.BOLD , 18));
objMove.drawString("SCORE" , 12 , 52);
objMove.drawString(String.valueOf(newSc.score) , 80 , 52);
objMove.setColor(Color.RED);
objMove.setFont(new Font("Strasua" , Font.BOLD , 18));
objMove.drawString("LIFE" , 12 , 76);
objMove.drawString(String.valueOf(playerPlane01.life) , 76 , 76);
if(playerPlane01.life <= 0){
objMove.drawImage(explode , playerPlane01.getX() , playerPlane01.getY() , null);
playerPlane01.setX(-100);
playerPlane01.setY(100);
objMove.drawString("GAME OVER" , 180 , 500);
try {
Thread.sleep(900000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
三、运行结果
启动界面
游戏运行界面