作者简介
作者名:编程界明世隐 简介:CSDN博客专家,从事软件开发多年,精通Java、JavaScript,博主也是从零开始一步步把学习成长、深知学习和积累的重要性,喜欢跟广大ADC一起打野升级,欢迎您关注,期待与您一起学习、成长、起飞!
系列目录
1. Java俄罗斯方块 2. Java五子棋小游戏 3. 老Java程序员花一天时间写了个飞机大战 4. Java植物大战僵尸 5. 老Java程序员花2天写了个连连看 6. Java消消乐(天天爱消除) 7. Java贪吃蛇小游戏 8. Java扫雷小游戏 9. Java坦克大战 10. Java迷宫小游戏
引言:
前几天群里的小伙伴问我Java小游戏啥时候做完,怎么还没更新,说能不能提个建议,然后就说明哥能不能做个拼图游戏。 哈哈,像我这么帅气的博主向来都不太会拒绝人的,于是,它来了它来了。
效果图
实现思路
- 绘制窗口。
- 加载好所有的图片。
- 绘制按钮、边框等。
- 创建右边预览小图。
- 将大图切片,并展示。
- 点击开始按钮,打乱图片位置。
- 加入鼠标移入和点击事件,处理移入和点击效果。
- 点击交换后判断顺序,如果顺序正确则提示成功。
代码实现
创建窗口
首先创建一个游戏窗体类GameFrame,继承至JFrame,用来显示在屏幕上(window的对象),每个游戏都有一个窗口,设置好窗口标题、尺寸、布局等就可以。
package main;
import java.awt.Color;
import javax.swing.JFrame;
public class GameFrame extends JFrame {
public GameFrame(){
setTitle("拼图游戏");
setSize(874, 412);
getContentPane().setBackground(new Color(0,0,0));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setResizable(false);
}
}
创建面板容器GamePanel继承至JPanel
package main;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
public class GamePanel extends JPanel {
private JMenuBar jmb = null;
private GameFrame mainFrame = null;
private GamePanel panel = null;
public GamePanel(GameFrame mainFrame){
this.setLayout(null);
this.setOpaque(false);
this.mainFrame=mainFrame;
this.panel =this;
}
}
再创建一个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类,窗口建出来了(加的黑色背景不好看)
加载好所有的图片
package common;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.Map;
import javax.imageio.ImageIO;
public class ImageValue {
private static String path = "/images/";
public static Map<Integer,BufferedImage> images = new HashMap<Integer,BufferedImage>();
public static void init() {
try {
for (int i = 1; i <= 16; i++) {
images.put(i,ImageIO.read(ImageValue.class.getResource(path+i+".jpeg")));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
在构造函数中调用,待会就可以直接使用了。
创建边框
private void drawBorder(Graphics g) {
BasicStroke bs_2=new BasicStroke(12L,BasicStroke.CAP_ROUND,BasicStroke.JOIN_MITER);
Graphics2D g_2d=(Graphics2D)g;
g_2d.setColor(new Color(128,128,128));
g_2d.setStroke(bs_2);
g_2d.drawRect(6, 6, 652, 372);
}
private void drawViewBorder(Graphics g) {
BasicStroke bs_2=new BasicStroke(12L,BasicStroke.CAP_ROUND,BasicStroke.JOIN_MITER);
Graphics2D g_2d=(Graphics2D)g;
g_2d.setColor(new Color(128,128,128));
g_2d.setStroke(bs_2);
g_2d.drawRect(658, 6, 204, 372);
}
在GamePanel重写 paint 方法,并调用这两个方法,如下:
@Override
public void paint(Graphics g) {
super.paint(g);
drawBorder(g);
drawViewBorder(g);
}
运行效果如下:
难度系数、预览图等文字
难度系数默认是3 创建难度系数、预览图等
private void drawViewText(Graphics g) {
Color oColor = g.getColor();
oColor = g.getColor();
g.setColor(Color.white);
g.setFont(new Font("思源宋体", Font.BOLD, 18));
g.drawString("预览图",668, 40);
g.setColor(oColor);
}
private void drawGradeText(Graphics g) {
Color oColor = g.getColor();
oColor = g.getColor();
g.setColor(Color.white);
g.setFont(new Font("思源宋体", Font.BOLD, 18));
g.drawString("难度系数:"+grade+"",668, 210);
g.setColor(oColor);
}
在paint方法中调用:
@Override
public void paint(Graphics g) {
super.paint(g);
drawBorder(g);
drawViewBorder(g);
drawViewText(g);
drawGradeText(g);
}
创建按钮
创建4个按钮的代码如下:
private void init() {
JButton btnStart = new JButton();
btnStart.setFont(new Font("思源宋体", Font.BOLD, 18));
btnStart.setFocusPainted(false);
btnStart.setText("开始");
btnStart.setBounds(668, 300, 80, 33);
btnStart.setBorder(BorderFactory.createRaisedBevelBorder());
this.add(btnStart);
btnStart.addActionListener(this);
btnStart.setActionCommand("start");
JButton nextStart = new JButton();
nextStart.setFont(new Font("思源宋体", Font.BOLD, 18));
nextStart.setFocusPainted(false);
nextStart.setText("换换");
nextStart.setBounds(768, 300, 80, 33);
nextStart.setBorder(BorderFactory.createRaisedBevelBorder());
this.add(nextStart);
nextStart.addActionListener(this);
nextStart.setActionCommand("next");
JButton gradeDecrease = new JButton();
gradeDecrease.setFont(new Font("思源宋体", Font.BOLD, 18));
gradeDecrease.setFocusPainted(false);
gradeDecrease.setText("难度减");
gradeDecrease.setBounds(668, 250, 80, 33);
gradeDecrease.setBorder(BorderFactory.createRaisedBevelBorder());
this.add(gradeDecrease);
gradeDecrease.addActionListener(this);
gradeDecrease.setActionCommand("gradeDecrease");
JButton gradeIncrease = new JButton();
gradeIncrease.setFont(new Font("思源宋体", Font.BOLD, 18));
gradeIncrease.setFocusPainted(false);
gradeIncrease.setText("难度加");
gradeIncrease.setBounds(768, 250, 80, 33);
gradeIncrease.setBorder(BorderFactory.createRaisedBevelBorder());
this.add(gradeIncrease);
gradeIncrease.addActionListener(this);
gradeIncrease.setActionCommand("gradeIncrease");
}
此时可以看到开发工具中,报红,因为没有实现ActionListener 实现 ActionListener 并重新方法 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("start".equals(command)){
}else if("next".equals(command)){
}else if("gradeDecrease".equals(command)){
}else if("gradeIncrease".equals(command)){
}
}
在构造函数中调用 init方法,运行效果如下:
实现右侧预览小图
创建 ViewImage 类,这个类很简单就是通过调用draw方法在指定的位置、指定的宽高、绘制指定的图片。
package main;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
public class ViewImage {
private int x = 0;
private int y = 0;
private int w = 0;
private int h = 0;
private BufferedImage img = null;
public ViewImage(BufferedImage img,int x,int y,int w,int h){
this.img=img;
this.x=x;
this.y=y;
this.w=w;
this.h=h;
}
public void draw(Graphics g) {
g.drawImage(img, x, y, w, h, null);
}
}
创建预览图实例
private void createViewImage() {
BufferedImage img = ImageValue.images.get(imageKey);
int x=664;
int y=62;
int w=192;
int h=108;
viewImage = new ViewImage(img, x, y, w, h);
}
注意需加入成员变量
private ViewImage viewImage = null;
private int imageKey = 1;
在GamePanel的构造方法中调用
public GamePanel(GameFrame mainFrame){
this.setLayout(null);
this.setOpaque(false);
this.mainFrame=mainFrame;
this.panel =this;
ImageValue.init();
init();
createViewImage();
}
绘制预览图的方法
private void drawViewImage(Graphics g) {
if(viewImage!=null){
viewImage.draw(g);
}
}
在paint方法中调用
@Override
public void paint(Graphics g) {
super.paint(g);
drawBorder(g);
drawViewBorder(g);
drawViewText(g);
drawGradeText(g);
drawViewImage(g);
}
运行效果如下:
将大图切片
图片绘制方法介绍: g.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null);
参数说明: img - 绘制指定图像。如果 img 为 null,不执行任何操作。 dx1 - 目标矩形第一个角的 x 坐标。 dy1 - 目标矩形第一个角的 y 坐标。 dx2 - 目标矩形第二个角的 x 坐标。 dy2 - 目标矩形第二个角的 y 坐标。 sx1 - 源矩形第一个角的 x 坐标。 sy1 - 源矩形第一个角的 y 坐标。 sx2 - 源矩形第二个角的 x 坐标。 sy2 - 源矩形第二个角的 y 坐标。
创建 SmallImage 类
属性名 | 说明 |
---|
dx1 | 目标显示左上角x坐标 | dy1 | 目标显示左上角y坐标 | dWidth | 目标宽度,与dx1相加计算第2个点dx2 | dHeight | 目标高度,与dy1相加计算第2个点dy2 | sx1 | 源图左上角x坐标 | sy1 | 源图左上角y坐标 | sWidth | 源宽度,与sx1相加计算第2个点sx2 | sHeight | 源高度,与sy1相加计算第2个点sy2 | i | 下标i(切片交换时候用的) | j | 下标j(切片交换时候用的) | oi | 原始下标i(用来比较顺序) | oj | 原始下标j(用来比较顺序) | img | 图片对象 | selected | 选择状态,根据选择的状态来显示小图片的边框 | disX | x方向长 | disY | y方向高 |
注意点:
- 根据的 oi,oj 来计算源的两个点坐标,因为一旦实例话oi,oj 就是固定的,也就是来源变。
- 根据 i,j 来计算目标的两个点坐标,当我们执行拼图交换位置的时候,就是交换 i,j ,所以达到交换的目的。
public void init() {
int start = 12;
int x = start+i*disX;
int y = start+j*disY;
this.dx1 = x;
this.dy1 = y;
this.dWidth = disX;
this.dHeight = disY;
int sx = start+oi*disX;
int sy = start+oj*disY;
this.sx1 = sx;
this.sy1 = sy;
this.sWidth = disX;
this.sHeight = disY;
}
- 绘制完图片后要根据图显示类型来显示小边框。
public void draw(Graphics g) {
init();
g.drawImage(img, dx1, dy1, dx1+dWidth, dy1+dHeight, sx1, sy1, sx1+sWidth, sy1+sHeight,
null);
if(selected==0){
g.setColor(new Color(0,0,0));
g.drawRect(dx1, dy1, dWidth-1, dHeight-1);
}else if(selected==1){
g.setColor(new Color(255,255,255));
g.drawRect(dx1, dy1, dWidth-1, dHeight-1);
}else if(selected==2){
g.setColor(new Color(209,146,17));
g.drawRect(dx1, dy1, dWidth-1, dHeight-1);
}
}
- 加入判断鼠标是否在小图片范围内的方法
public boolean isPoint(int x,int y){
int x1 = this.dx1;
int y1 = this.dy1;
int x2 = this.dx1 + dWidth;
int y2 = this.dy1 + dHeight;
return x>x1&&y>y1&&x<x2&&y<y2;
}
SmallImage类完整代码
package main;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
public class SmallImage {
private int dx1 = 0;
private int dy1 = 0;
private int dWidth = 0;
private int dHeight = 0;
private int sx1 = 0;
private int sy1 = 0;
private int sWidth = 0;
private int sHeight = 0;
private int i = 0;
private int j = 0;
private int oi = 0;
private int oj = 0;
private BufferedImage img = null;
private int selected = 0;
private int disX;
private int disY;
public SmallImage(BufferedImage img, int i, int j, int disX, int disY) {
this.img = img;
this.i = this.oi = i;
this.j = this.oj = j;
this.disX=disX;
this.disY=disY;
}
public void init() {
int start = 12;
int x = start+i*disX;
int y = start+j*disY;
this.dx1 = x;
this.dy1 = y;
this.dWidth = disX;
this.dHeight = disY;
int sx = start+oi*disX;
int sy = start+oj*disY;
this.sx1 = sx;
this.sy1 = sy;
this.sWidth = disX;
this.sHeight = disY;
}
public void draw(Graphics g) {
init();
g.drawImage(img, dx1, dy1, dx1+dWidth, dy1+dHeight, sx1, sy1, sx1+sWidth, sy1+sHeight,
null);
if(selected==0){
g.setColor(new Color(0,0,0));
g.drawRect(dx1, dy1, dWidth-1, dHeight-1);
}else if(selected==1){
g.setColor(new Color(255,255,255));
g.drawRect(dx1, dy1, dWidth-1, dHeight-1);
}else if(selected==2){
g.setColor(new Color(209,146,17));
g.drawRect(dx1, dy1, dWidth-1, dHeight-1);
}
}
public boolean isPoint(int x,int y){
int x1 = this.dx1;
int y1 = this.dy1;
int x2 = this.dx1 + dWidth;
int y2 = this.dy1 + dHeight;
return x>x1&&y>y1&&x<x2&&y<y2;
}
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
public int getJ() {
return j;
}
public void setJ(int j) {
this.j = j;
}
public int getOi() {
return oi;
}
public void setOi(int oi) {
this.oi = oi;
}
public int getOj() {
return oj;
}
public void setOj(int oj) {
this.oj = oj;
}
public int getSelected() {
return selected;
}
public void setSelected(int selected) {
this.selected = selected;
}
}
GamePanel类中加入成员变量
private String gameFlag="end";
private int grade=3;
private int w=640;
private int h=360;
private SmallImage[][] sImages;
List<SmallImage> imageList = new ArrayList<SmallImage>();
private SmallImage curSmallImage;
创建切片方法
private void sliceImage(){
int disX = this.w/grade;
int disY = this.h/grade;
SmallImage smallImage;
BufferedImage img = ImageValue.images.get(imageKey);
sImages = new SmallImage[grade][grade];
for(int i=0;i<grade;i++){
for(int j=0;j<grade;j++){
smallImage = new SmallImage(img,i,j,disX, disY);
sImages[i][j]=smallImage;
imageList.add(smallImage);
}
}
}
构造函数调用此方法
public GamePanel(GameFrame mainFrame){
this.setLayout(null);
this.setOpaque(false);
this.mainFrame=mainFrame;
this.panel =this;
ImageValue.init();
init();
createViewImage();
sliceImage();
}
绘制这些切片图形
private void drawSmallImages(Graphics g) {
SmallImage smallImage;
for(int i=0;i<grade;i++){
for(int j=0;j<grade;j++){
smallImage = sImages[i][j];
if(smallImage!=null){
smallImage.draw(g);
}
}
}
}
paint方法中调用,注意drawSmallImages放到前面,因为涉及到边框绘制,如果放到后面拼图的边框会变的很粗,也可以做别的处理,我这里就取巧了。
@Override
public void paint(Graphics g) {
super.paint(g);
drawSmallImages(g);
drawBorder(g);
drawViewBorder(g);
drawViewText(g);
drawGradeText(g);
drawViewImage(g);
}
运行一下:
加入鼠标事件
移动事件,就是在图片的范围内,设置selected属性为1,然后重绘就行。
private void createMouseListener() {
MouseAdapter mouseAdapter = new MouseAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
if(!"start".equals(gameFlag)) return ;
int x = e.getX();
int y = e.getY();
SmallImage smallImage;
for(int i=0;i<grade;i++){
for(int j=0;j<grade;j++){
smallImage = sImages[i][j];
if(smallImage!=null){
if(curSmallImage==smallImage){
continue;
}
if(smallImage.isPoint(x, y)){
smallImage.setSelected(1);
}else{
smallImage.setSelected(0);
}
}
}
}
repaint();
}
};
addMouseMotionListener(mouseAdapter);
addMouseListener(mouseAdapter);
}
注意在构造中调用哦 给几个按钮先添加好方法
@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("start".equals(command)){
restart();
}else if("next".equals(command)){
next();
}else if("gradeDecrease".equals(command)){
gradeDecrease();
}else if("gradeIncrease".equals(command)){
gradeIncrease();
}
}
实现这些方法,都比较简单,就不解释了,上代码:
private void gradeDecrease() {
if(grade<=3){
return ;
}
imageList.clear();
clearImages(sImages);
grade--;
sliceImage();
repaint();
}
private void gradeIncrease() {
if(grade>=10){
return ;
}
imageList.clear();
clearImages(sImages);
grade++;
sliceImage();
repaint();
}
void next(){
gameFlag = "end";
imageKey++;
if(imageKey>16){
imageKey=1;
}
imageList.clear();
clearImages(sImages);
createViewImage();
sliceImage();
repaint();
}
void restart() {
gameFlag = "start";
curSmallImage = null;
clearImages(sImages);
Collections.shuffle(imageList);
addImages();
repaint();
}
private void addImages() {
SmallImage smallImage;
int n=0;
for(int i=0;i<grade;i++){
for(int j=0;j<grade;j++){
smallImage = imageList.get(n);
smallImage.setI(i);
smallImage.setJ(j);
sImages[i][j]=smallImage;
n++;
}
}
}
void clearImages(SmallImage[][] imgs){
for(int i=0;i<grade;i++){
for(int j=0;j<grade;j++){
imgs[i][j]=null;
}
}
}
public void gameWin() {
gameFlag = "end";
UIManager.put("OptionPane.buttonFont", new FontUIResource(new Font("思源宋体", Font.ITALIC, 18)));
UIManager.put("OptionPane.messageFont", new FontUIResource(new Font("思源宋体", Font.ITALIC, 18)));
JOptionPane.showMessageDialog(mainFrame, "你成功了,太棒了!");
}
测试一下鼠标移入事件 处理鼠标点击事件
- 点击后判断是否已经有选择,如果没有则当前图片设置为已选择,并设置selected为2。
- 如果已经有选择的拼图块,则互相交换,交换就是交换 i 和 j即可。
- 交换完成后selected都设置为0,并且存储选择的对象设置为null。
- 每次交换完成后要进行差异比较,看拼图是否完成。
- 右键取消已经选择的处理。
@Override
public void mouseClicked(MouseEvent e) {
if(!"start".equals(gameFlag)) return ;
int buttonType = e.getButton();
if(buttonType==2){
return ;
}else if(buttonType==1){
boolean diffFlag =false;
int x = e.getX();
int y = e.getY();
SmallImage smallImage;
for(int i=0;i<grade;i++){
for(int j=0;j<grade;j++){
smallImage = sImages[i][j];
if(smallImage!=null){
if(smallImage.isPoint(x, y)&& smallImage.getSelected()!=2){
if(curSmallImage!=null){
int ti = curSmallImage.getI();
int tj = curSmallImage.getJ();
curSmallImage.setI(smallImage.getI());
curSmallImage.setJ(smallImage.getJ());
smallImage.setI(ti);
smallImage.setJ(tj);
curSmallImage.setSelected(0);
smallImage.setSelected(0);
curSmallImage = null;
diffFlag =true;
}else {
curSmallImage = smallImage;
smallImage.setSelected(2);
}
}
}
}
}
repaint();
if(diffFlag && diff()){
gameWin();
return;
}
}else if(buttonType==3){
if(curSmallImage!=null){
curSmallImage.setSelected(0);
curSmallImage=null;
repaint();
}
}
}
差异判断,比较每一个小图片的 i与oi,j与oj,如果都相同表示拼图完成 。
private boolean diff() {
SmallImage smallImage;
for(int i=0;i<grade;i++){
for(int j=0;j<grade;j++){
smallImage = sImages[i][j];
if(smallImage!=null){
if(!(smallImage.getI()==smallImage.getOi() &&
smallImage.getJ()==smallImage.getOj())){
return false;
}
}
}
}
return true;
}
到这里基本就完成了,图片切换和难度系数的修改就不说明了。
看到这里的大佬,动动发财的小手 点赞 + 回复 + 收藏,能【 关注 】一波就更好了。
代码获取方式:
帮忙文章【点赞】 +【 收藏】+【关注】+【评论】 后,加我主页微信或者私聊我,我发给你!
更多精彩
1. Java俄罗斯方块 2. Java五子棋小游戏 3. 老Java程序员花一天时间写了个飞机大战 4. Java植物大战僵尸 5. 老Java程序员花2天写了个连连看 6. Java消消乐(天天爱消除) 7. Java贪吃蛇小游戏 8. Java扫雷小游戏 9. Java坦克大战 10. Java迷宫小游戏
相关阅读
1. JavaWeb图书管理系统 2. JavaWeb学生宿舍管理系统 3. JavaWeb在线考试系统
另外
为了帮助更多小白从零进阶 Java 工程师,从CSDN官方那边搞来了一套 《Java 工程师学习成长知识图谱》,尺寸 870mm x 560mm,展开后有一张办公桌大小,也可以折叠成一本书的尺寸,原件129元现价 29 元,先到先得,有兴趣的小伙伴可以了解一下!
|