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