JavaSwing GUI 编写射击小游戏

本文详细介绍了如何使用Java Swing GUI库开发一款简单的游戏,从项目结构搭建、组件设计到事件监听,涵盖游戏帧布局、角色动画、碰撞检测及键盘输入处理等核心内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、初步学习 JavaSwing GUI 编写小游戏

AWT和Swing
Swing是对AWT的提高和扩展。在写GUI程序时,Swing和AWT都要作用。他们共存于 Java基础类(JFC)中。

AWT依赖于主平台绘制用户界面组件;
而Swing有自己的机制,在主平台提供的窗口中绘制和管理界面组件。
Swing和AWT之间的最明显的区别是界面组件的外观,AWT在不同平台上运行相同的程序,界面的外观和风格可能会有些差异。然而一个基于Swing的应用程序可能在任何平台上都会有相同的外观和风格。

Swing中的类是从AWT继承的,有些Swing类直接扩展AWT中对应的类。如JApplet JDialog JFrame JWindow

使用Swing设计图形界面,主要引入两个包
javax.swing包和java.awt.event包
组件和容器
组件是图形界面的基本元素,用户可以直接操作,如按钮。
容器是图形界面的复合元素。容器可以包含组件。

Java语言为每种组件都预定义类,程序通过它们或者它们的子类各种组件对象。如Swing中预定义的按钮类JButton是一种类,程序创建的JButton对象,或JButton子类的对象就是按钮。
Java也为每种容器预定义类,程序通过它们或它们的子类创建各种容器对象。例如,Swing中预定义的窗口类JFrame是一种容器类,程序创建的JFrame或JFrame子类的对象就是窗口。

为了统一管理组件和容器,为所有组件类定义超类,把组件的共有操作都定义在Component类中。同样,为所有容器类定义超类Container类,把容器的共有操作都定义在Container类中。例如,Container类中定义了add()方法,大多数容器都可以用add()方法向容器添加组件。

Component Container和Graphics类是AWT库中的关键类。为能层次地构造复杂的图形界面,容器被当做特殊的组件,可以把容器放入另一个容器中。这种层次地构造界面的方法,都以增量的方式构造复杂的用户界面。

事件驱动程序设计基础
1.事件/监视器/监视器注册
图形界面上的事件是指在某个组件上发生用户操作。例如,用户单机了界面上的某个按钮,就说在这个按钮上发生了事件,这个按钮对象就是事件的击发者。对事件作监视的对象成为监视器,监视器提供响应事件的处理方法。为了让监视器与事件对象关联起来,需要对事件对象作监视器注册,告诉系统事件对象的监视器。

以程序相应按钮事件为例,程序要创建按钮对象,把它添加到界面中,要为按钮监视器注册,程序要有响应按钮事件的方法。当”单机按钮”事件发生时,系统就调用已为这个按钮注册的事件处理方法,完成处理按钮事件的工作。

2.实现事件处理的途径
Java语言编写事件处理程序主要有两种方案,一个是程序重设handleEvent,采用这个方法的程序工作量稍大一些。另一个方法是程序实现一些系统设定的接口。java按事件类型提供多种接口,作为监视器对象的类穾实现相应的接口,即实现响应事件的方法。当事件发生时,系统内设的handleEvent方法就自动调用监视器的类实现的响应事件的方法。

java.awt.event包中用来检测并对事件做出反应的模型包括以下三个组成元素
1、源对象
2、监视器对象
3、事件对象

一个事件驱动程序要做的工作除了创建源对象和监视器对象之外,还必须安排监视器了解源对象,或者向源对象注册监视器。每个源对象有一个已经注册的监视器列表,提供一个方法能向该列表添加监视器对象。只有在源对象注册了监视器之后,系统才会将源对象上发生的时间通知监视器对象。

3.事件类型和监视器接口
在Java语言中,为了便于系统管理事件,也为了便于程序作监视器注册,系统将时间分类,称为事件类型。系统为每个事件类型提供一个接口。要作为监视器对象的类必须实现相应的接口,提供接口规定的响应时间的方法。

再以程序相应按钮事件为例,JButton类对象button可以是一个事件的击发者。当用户点击界面中与button对应的按钮时,button对象就会产生一个ActionEvent类型的时间。如果监视器对象时obj,对象obj的类是Obj,则类Obj必须实现addActionListener方法注册它的监视器obj。

程序运行时,当用户点击button对象对应的按钮时,系统就讲一个ActionEvent对象从事件传递到监视器。ActionEvent对象包含的信息包括事件发生在哪一个按钮,以及有关该事件的其他信息。

实际事件发生时,通常会产生一系列的时间,例如,用户点击按钮,会产生ChangeEvent事件提示光标到了按钮上,接着又是一个ChangeEvent事件表示鼠标被按下,然后是ActionEvent事件表示鼠标已松开,但光标依旧在按钮上,最后是ChangeEvent事件,表示光标已经离开按钮,但是应用程序通常只处理按下按钮的完整动作的单个ActionEvent事件。

每个事件类型都有一个相应的监视器接口,实现监视器接口的类必须实现所有定义在接口中的方法。

笔者第一次写,不太会写请见谅。接下来就是游戏的编写了。
1、项目工程,由于用SpringBoot整合了一些技术,又不想单独分开所有就写在了一起。主要文件在games目录下
在这里插入图片描述
2、编写GameFrame程序布局和启动都写在里面具体代码如下

    package com.java.games.role.game;
    
    import java.awt.Color;
    import java.awt.Font;
    import java.awt.Graphics;
    import java.awt.Image;
    import java.util.ArrayList;
    import javax.swing.JFrame;
    
    import com.java.games.role.BaseRole;
    import com.java.games.role.Boss;
    import com.java.games.role.Bullet;
    import com.java.games.role.Girl;
    import com.java.games.role.util.CreatEnemy;
    
    
    public class GameFrame extends JFrame implements Runnable {
    	//@Fields serialVersionUID :  
    	private static final long serialVersionUID = 1L;
    	// 程序运行的标志
    	private static boolean isRun = true;
    	private Image memoryimage;
    	private Graphics memorygraphics;
    	Boss boss=new Boss();
    	public static Girl girl=new Girl();
    	Bullet bullet=new Bullet(girl.getX(),girl.getY());
    	
    	/**角色集合*/
    	public static ArrayList<BaseRole> rolelist=new ArrayList<BaseRole>();
    	/**玩家的得分*/
    	public static int point;
    	public GameFrame() {
    		this.setSize(1000, 700);
    		this.setVisible(true);
    		this.setDefaultCloseOperation(3);
    		this.setLocationRelativeTo(null);
    		// 创建大小为1000*700的内存画布
    		memoryimage = this.createImage(1000, 700);
    		// 从内存画布中得到内存画笔,内存画笔绘制的内容就在内存画布上
    		this.memorygraphics = this.memoryimage.getGraphics();
    	    new InitImage(this);
            rolelist.add(girl);
            
    		Thread th = new Thread(this);
    		th.start();
    		this.addKeyListener(new PressKey());
    	}
    
    	public static void main(String[] args) {
    		new GameFrame();
    
    	}
    
    	@Override
    	public void run() {
    		while (isRun) {
    			this.drawFrame(this.getGraphics());
    			
    			try {
    				Thread.sleep(20);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    
    	}
    
    	// 绘制图片
    	public void drawFrame(Graphics g) {
    		GameMap.drawMyself(memorygraphics);
    		CreatEnemy.creatEnemy();
    	    for(int i=0;i<rolelist.size();i++){
                rolelist.get(i).drawMyself(memorygraphics);
    		}
    	    this.drawInfo();
    		g.drawImage(memoryimage, 0, 0, this);
    	}
    	/**绘制游戏得分等信息*/
    	public void drawInfo(){
    		memorygraphics.setColor(Color.BLUE);
    		memorygraphics.setFont(new Font("宋体",Font.BOLD,30));
    		memorygraphics.drawString("得分:"+point, 50, 80);
    	}
    }
    

2、编写工具处理类主要处理图片切图等信息

package com.java.games.role.util;

import java.awt.Image;

public class ChangeImg {
	private int speed;
	private int index;
	private int count;
	private Image[] img;
 public ChangeImg(int speed){
	this.speed=speed;
}
public Image change(Image[] img){
	count++;
	if(count>speed){
		count=0;
		index++;
		if(index>img.length-1){
			index=0;
		}
	}
	return img[index];
}
}

package com.java.games.role.util;

import com.java.games.role.LightEnemy;
import com.java.games.role.RedEnemy;
import com.java.games.role.game.GameFrame;

public class CreatEnemy {
	/** 产生红怪的计数器 */
	private static int redcount;
	/** 产生灯怪的计数器 */
	private static int lightcount;
    /** 产生红怪的方法 */
	public static void creatEnemy(){
		CreatRedEnemy();
		CreatLightEnemy();
	}
	public static void CreatRedEnemy() {
		redcount++;
		if (redcount > 80) {
			redcount=0;
			GameFrame.rolelist.add(new RedEnemy());
			
		}
	}
	
    /** 产生灯怪的方法 */
	public static void CreatLightEnemy() {
		lightcount++;
		if (lightcount > 80) {
			lightcount=0;
			GameFrame.rolelist.add(new LightEnemy());
			
		}
	}
}
package com.java.games.role.util;

import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.image.CropImageFilter;
import java.awt.image.FilteredImageSource;
import java.awt.image.ImageProducer;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
/**
 * 图像切割类
 * @Title: CutImage.java 
 * @Package com.java.games.role.util 
 * @Description: TODO(用一句话描述该文件做什么) 
 * @author lys
 * @date 2019年6月13日 下午5:08:10 
 * Copyright (c) ©1994-2019 Scjydz.com All Rights Reserved.
 */
public class CutImage {

	/**
	 *  切割多行图片
	 * 
	 * @param filePath            原始图片路径
	 * @param row	               图片切割行数
	 * @param col                 图片切割列数
	 * @param track               媒体追踪器
	 * @param groupId 			   分组ID
	 * @return Image[][]          切割完成后的图像数组
	 */
	public static  Image[][] cutManyImage(String filePath, int row, int col,MediaTracker track,int groupId) {
		
		Image[][] img = new Image[row][col];

		ImageIcon imIcon = new ImageIcon(filePath);// 创建图像数组对象

		Image imgTemp = imIcon.getImage();// 创建源图像
		
		int imageWidth = imgTemp.getWidth(null)/col;
		int imageHight = imgTemp.getHeight(null)/row;
		
		// 为源 图象获取ImageProducer源
		ImageProducer sourceProducer = imgTemp.getSource();

		for (int i = 0; i < row; i++) {
			for (int j = 0; j < col; j++) {
				// 创建图片分割图像对象,第一、二个参数为分割图像起始坐标。后两个参数为图像大小
				CropImageFilter cropImg = new CropImageFilter(j * imageWidth, i * imageHight,imageWidth, imageHight);

				/* 获得图像生成器 */
				ImageProducer imgProducer = new FilteredImageSource(sourceProducer, cropImg);

				/* 获图像对象 */
				img[i][j] = new JFrame().createImage(imgProducer);
				track.addImage(img[i][j], groupId);
			}
		}

		return img;
	}
	
	/**
	 * 切割单行图片
	 * @param filePath 文件路径
	 * @param col 列数
	 * @param track 媒体追踪器
	 * @param groupId 分组ID
	 * @return 切割完成后的图像数组
	 */
	public static  Image[] cutOneImage(String filePath,int col,MediaTracker track,int groupId) {
		
		Image[] img = new Image[col];

		ImageIcon imIcon = new ImageIcon(filePath);// 创建图像数组对象

		Image imgTemp = imIcon.getImage();// 创建源图像
		int imageWidth = imgTemp.getWidth(null)/col;
		int imageHight = imgTemp.getHeight(null);
		// 为源 图象获取ImageProducer源
		ImageProducer sourceProducer = imgTemp.getSource();


			for (int j = 0; j < col; j++) {
				// 创建图片分割图像对象,第一、二个参数为分割图像起始坐标。后两个参数为图像大小
				CropImageFilter cropImg = new CropImageFilter(j * imageWidth, 0,imageWidth, imageHight);

				/* 获得图像生成器 */
				ImageProducer imgProducer = new FilteredImageSource(sourceProducer, cropImg);

				/* 获图像对象 */
				img[j] = new JFrame().createImage(imgProducer);
				track.addImage(img[j], groupId);
			}


		return img;
	}
	/**
	 * 得到单个图像
	 * @param filePath 文件路径
	 * @param track 媒体追踪器
	 * @param groupId 分组ID
	 * @return 图像对象
	 */
	public static  Image getSingleImage(String filePath,MediaTracker track,int groupId){
		Image img = new ImageIcon(filePath).getImage();
		track.addImage(img, groupId);
		
		return img;
	}
}

3、编写BaseRole写成抽象类,到时候人物和Boos等都可以继承此类

package com.java.games.role;

import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;

import com.java.games.role.game.GameFrame;
/**
 * 
 * @Title: BaseRole.java 
 * @Package com.java.games.role 
 * @Description: TODO(角色抽象类) 
 * @author lys
 * @date 2019年6月13日 下午5:02:41 
 * Copyright (c) ©1994-2019 Scjydz.com All Rights Reserved.
 */
public abstract class BaseRole {
	/** 角色的x坐标 */
	protected int x;
	/** 角色的y坐标 */
	protected int y;
	/** 角色的宽度 */
	protected int width;
	/** 角色的高度 */
	protected int height;
	/** 角色需要绘制的图片 */
	protected Image img;
	/** 角色的碰撞区域 */
	protected Rectangle rect;

	public int getX() {
		return x;
	}

	public int getY() {
		return y;
	}

	public BaseRole(int x, int y, int width, int height) {
		this.x = x;
		this.y = y;
		this.width = width;
		this.height = height;
		this.rect = new Rectangle(x,y,width,height);
	}

	/** 将碰撞区域和角色重合 */
	public void setRect() {
		rect.x = this.x;
		rect.y = this.y;
		rect.width = this.width;
		rect.height = this.height;
	}

	/** 角色的绘制方法 */
	public void drawMyself(Graphics memorygraphics) {
		memorygraphics.drawImage(img, x, y, width, height, null);
		this.move();
		this.setRect();
		this.removeMyself();
	}

	/** 角色移除方法 角色到达屏幕以外 自动移除 */
	public void removeMyself() {
		if (this.x < -200 || this.x > 1200 || this.y < -200 || this.y > 900) {
			GameFrame.rolelist.remove(this);
		}
	}

	/** 角色的移动方法 */
	protected abstract void move();
}

4、图片改变

package com.java.games.role;

import java.awt.Image;

import com.java.games.role.game.GameFrame;
import com.java.games.role.util.ChangeImg;


public class Boom extends BaseRole{
public static Image[] boomimg;
private int count;
private ChangeImg a=new ChangeImg(3);

	public Boom(int x,int y) {
		super(x, y,100, 100);
		
	}

	@Override
	protected void move() {
		this.img=a.change(boomimg);
		count++;
		if(count>30){
			GameFrame.rolelist.remove(this);
		}
	}

}

5、设置Boos

package com.java.games.role;

import java.awt.Image;

import com.java.games.role.util.ChangeImg;



public class Boss extends BaseRole{
	/** 获取boss图片组*/
		public static  Image[] bossimg;
		/**图片切换对象*/
		private ChangeImg a=new ChangeImg(3);
	public Boss(){
		super(300,300,150,150);
	}
		@Override
		protected void move() {
			this.img=a.change(bossimg);
		
		}
}

6、编写子弹类,具体子弹图片大家按照喜好自己选择子弹图片

package com.java.games.role;

import java.awt.Image;

import com.java.games.role.game.GameFrame;



public class Bullet extends BaseRole{
	public static Image bulletimg;
public Bullet(int x,int y){
	super(x,y,50,30);
   this.img=bulletimg;
}
@Override
protected void move() {
	this.x+=15;
	for(int i=0;i<GameFrame.rolelist.size();i++){
		BaseRole b=GameFrame.rolelist.get(i);
		if(this.rect.intersects(b.rect)&&b instanceof EnemyRole){
			EnemyRole a=(EnemyRole) b;
			a.Hit(100);
			GameFrame.rolelist.remove(this);

		}
		
	}
	
}
}

7、编写敌人类,把它设置成抽象类所有敌人都可以继承

package com.java.games.role;

import com.java.games.role.game.GameFrame;

public abstract class EnemyRole extends BaseRole{
private int exp;
private int hp;
	public EnemyRole(int x, int y, int width, int height,int exp,int hp) {
		super(x, y, width, height);
		this.exp=exp;
		this.hp=hp;
	}
public void Hit(int a){
	this.hp-=a;
	if(this.hp<0){
		GameFrame.point+=this.exp;
		GameFrame.rolelist.remove(this);
		GameFrame.rolelist.add(new Boom(this.x,this.y));
	}
}
}

8、编写英雄类,就是角色吧

package com.java.games.role;

import java.awt.Image;

import com.java.games.role.game.PressKey;
import com.java.games.role.util.ChangeImg;

public class Girl extends BaseRole {
	public static Image[] girlimage;
	/** 图片切换对象 */
	private ChangeImg a = new ChangeImg(3);

	public Girl() {
		super(100, 100, 70, 70);

	}

	@Override
	protected void move() {
		if (PressKey.isUp == true) {
			y -= 20;
		}
		if (PressKey.isDown == true) {
			y += 20;
		}
		if (PressKey.isLeft == true) {
			x -= 20;
		}
		if (PressKey.isRight == true) {
			x += 20;
		}
		this.img = a.change(girlimage);

	}

}

9、编写灯怪敌人

package com.java.games.role;

import java.awt.Image;

import com.java.games.role.util.ChangeImg;

public class LightEnemy extends EnemyRole{
private ChangeImg a=new ChangeImg(3);
 public static Image[] lightenemy;
public LightEnemy() {
		super((int)(Math.random()*1001),700, 100, 100,100,200);
		
	}
  @Override
	protected void move() {
		this.img=a.change(lightenemy);
		this.y-=5;
		
	}

}

10、编写红怪敌人

package com.java.games.role;

import java.awt.Image;

import com.java.games.role.util.ChangeImg;

public class RedEnemy extends EnemyRole{
public static Image[] redenemy;
private ChangeImg a=new ChangeImg(3);

	public RedEnemy() 
	{
   super((int)(Math.random()*200+1000),(int)(Math.random()*400+100), 100,100,100,200);
		
	}
  public void move(){
	  this.img=a.change(redenemy);
	  this.x-=5;
  }
}

11、设置背景图片

package com.java.games.role.game;

import java.awt.Graphics;
import java.awt.Image;

import javax.swing.ImageIcon;

public class GameMap {
	// 获取背景图片
	public static Image mapimage = new ImageIcon("image/map.jpg").getImage();
	// 背景图片x值
	public static int mapX;

	public static void drawMyself(Graphics memorygraphics) {
		if (mapX <= -17000) {
			mapX = -17000;
		}
		mapX -= 5;
		memorygraphics.drawImage(mapimage, mapX, 0, null);
	}

}

12、图片初始化类,所有的图标、怪物、子弹、角色、都可以进行按照大家的图片修改。个人喜欢吧

package com.java.games.role.game;

import java.awt.MediaTracker;
import javax.swing.JFrame;

import com.java.games.role.Boom;
import com.java.games.role.Boss;
import com.java.games.role.Bullet;
import com.java.games.role.Girl;
import com.java.games.role.LightEnemy;
import com.java.games.role.RedEnemy;
import com.java.games.role.util.CutImage;


public class InitImage {
	public InitImage(JFrame jf) {
		MediaTracker tracker = new MediaTracker(jf);
		tracker.addImage(GameMap.mapimage, 1);
		// 切割背景图片
		Boss.bossimg = CutImage.cutOneImage("image/boss/playSpear.png", 18, tracker, 1);
		// 切割女孩主角图片
		Girl.girlimage = CutImage.cutOneImage("image/girl/forward.png", 4, tracker, 1);
		//子弹图片初始化
		Bullet.bulletimg=CutImage.getSingleImage("image/jbullet/dart.png",  tracker, 1);
		//红怪图片初始化
		RedEnemy.redenemy=CutImage.cutOneImage("image/enemy/redEnemy.png", 3, tracker,1);
		//灯怪图片初始化
		LightEnemy.lightenemy=CutImage.cutOneImage("image/enemy/lightEnemy.png", 2, tracker, 1);
		//爆炸图片初始化
		Boom.boomimg=CutImage.cutOneImage("image/bomb/detonate.png", 5, tracker, 1);
		try {
			tracker.waitForID(1);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

13、最后最主要的一步,我们给自己角色编写键盘事件,键盘按键可以自己修改。这边就用ASDW了吧!

package com.java.games.role.game;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import com.java.games.role.Bullet;

public class PressKey implements KeyListener {
	/** 玩家是否按下了上键 */
	public static boolean isUp;
	/** 玩家是否按下了下键 */
	public static boolean isDown;
	/** 玩家是否按下了左键 */
	public static boolean isLeft;
	/** 玩家是否按下了右键 */
	public static boolean isRight;

	// 键盘按下事件
	@Override
	public void keyPressed(KeyEvent arg0) {
		int code = arg0.getKeyCode();
		if (code == KeyEvent.VK_W) {
			isUp = true;
		}
		if (code == KeyEvent.VK_S) {
			isDown = true;
		}
		if (code == KeyEvent.VK_A) {
			isLeft = true;
		}
		if (code == KeyEvent.VK_D) {
			isRight = true;
		}

	}

	// 键盘释放事件
	@Override
	public void keyReleased(KeyEvent arg0) {
		int code = arg0.getKeyCode();
		if (code == KeyEvent.VK_W) {
			isUp = false;
		}
		if (code == KeyEvent.VK_S) {
			isDown = false;
		}
		if (code == KeyEvent.VK_A) {
			isLeft = false;
		}
		if (code == KeyEvent.VK_D) {
			isRight = false;
		}
		if (code == KeyEvent.VK_J) {
			GameFrame.rolelist.add(new Bullet(GameFrame.girl.getX(), GameFrame.girl.getY()));
		}

	}

	@Override
	public void keyTyped(KeyEvent arg0) {
		// TODO Auto-generated method stub

	}

}

这里是我自己的Image图片包:https://pan.baidu.com/s/1Nkx_oIhOC-XL9D4fgqDiFg
提取码:ycml
到这里就差不写完了
点击运行看看效果吧
找到我们写的GameFrame,运行main方法启动 如下:

在这里插入图片描述
在这里插入图片描述
项目地址:https://download.youkuaiyun.com/download/weixin_44598993/11239968

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值