菜鸟之路day02-04拼图小游戏开发一一JAVA基础综合项目
作者:blue
时间:2025.1.20-1.22
[TOC]
0.概述
1.项目来源:黑马程序员:BV17F411T7Ao,感谢阿玮老师,讲的实在是太详细了
2.菜鸟之路day02-day04:
第一天我学习了面向对象的进阶知识,继承,多态,接口,内部类,等知识
第二天我开始做项目,项目完成了85%
第三天上午完善了项目,总耗时估计在24h左右,包括学习知识,代码编写,版本管理,文档编写
3.在项目中,我删除了登录和注册模块,因为我认为在一个单机游戏中,这两个模块有些许突兀(不是懒,听我狡辩!)
4.由于我在菜鸟之路day01时学习了Git,所以在这个项目中,我运用了git来管理这个项目,并将源码上传到了Gitee,读者可根据我的11次提交循序渐进的学习此项目
5.在本文中我仅展示部分核心代码,读者想获取完整代码,应该去我的仓库里clone,仓库是public的
6.图片均为视频提供,里面的美女我一个都不认识(真的!!!!)
7.本项目仅为java基础练习项目,此类项目并不是java语言的特长,用前端知识也确实能写出比这个项目更好的拼图游戏,但是我们毕竟是在练习java这个后端语言嘛,所以项目有它存在的意义,理解万岁。
仓库地址:https://gitee.com/zhang-tenglan/puzzlegame.git
在第11次提交时,我才删掉了注册和登录界面,所以若读者对这两个界面有兴趣,可以git第10次提交
此为我项目的结构图:
1.界面搭建
1.1创建窗体
创建ui包,在包内新建三个JavaBean类,三个类均继承JFrame(窗体类)这个父类,分别代表游戏的三个界面。
定义空参构造方法:1.设置窗口宽高 2.将窗口显示设置为true,表示可视
package com.bluening.ui;
import javax.swing.*;
import java.awt.*;
//游戏主界面
public class GameJFrame extends JFrame{
//空参构造,做初始化
public GameJFrame() {
setSize(603,680);
setVisible(true);
}
}
1.2窗体基础设置
还是以GameJFrame这个类为例
package com.bluening.ui;
import javax.swing.*;
import java.awt.*;
//游戏主界面
public class GameJFrame extends JFrame{
//空参构造,做初始化
public GameJFrame() {
//设置界面宽高
this.setSize(603,680);
//设置界面的标题
this.setTitle("拼图游戏单机版");
//设置界面置顶
this.setAlwaysOnTop(true);
//设置界面居中
this.setLocationRelativeTo(null);
//设置关闭模式
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
//让窗体显示
this.setVisible(true);
}
}
1.3创建主界面菜单
主界面与菜单结构,先建立各自对象,然后再建立对象间的联系
private void initJMenuBar() {
JMenuBar jMenuBar = new JMenuBar();
JMenu functionJMenu = new JMenu("功能");
JMenu aboutJMenu = new JMenu("关于我");
JMenuItem replayItem = new JMenuItem("重新游戏");
JMenuItem reLoginItem = new JMenuItem("重新登录");
JMenuItem closeItem = new JMenuItem("关闭游戏");
JMenuItem myBlogItem = new JMenuItem("我的博客");
//给菜单添加条目
functionJMenu.add(replayItem);
functionJMenu.add(reLoginItem);
functionJMenu.add(closeItem);
aboutJMenu.add(myBlogItem);
//给菜单栏添加JMenu
jMenuBar.add(functionJMenu);
jMenuBar.add(aboutJMenu);
//给窗体添加菜单栏
this.setJMenuBar(jMenuBar);
}
1.4添加图片素材,并将图片素材加载到GameJFrame中
结构:
private void initImage() {
int number = 1;
for (int i=0;i<4;i++){
for (int j=0;j<4;j++){
//创建ImageIcon对象
ImageIcon icon = new ImageIcon("image/animal/animal3/"+number+".jpg");
//创建JLabel对象(JLabel一个管理容器)
JLabel jLabel = new JLabel(icon);
//指定图片位置
jLabel.setBounds(105*j,105*i,105,105);
//把JLabel添加到界面当中
getContentPane().add(jLabel);
//加完一张添加下一个图片
number++;
}
}
}
1.5打乱图片
创建了随机生成二维数组的方法,利用随机二维数组,打乱图片
private void initData() {
int[] tempArr = {
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
//打乱
Random rd = new Random();
for(int i=0;i<tempArr.length;i++){
int index = rd.nextInt(tempArr.length);
int temp = tempArr[index];
tempArr[index] = tempArr[i];
tempArr[i] = temp;
}
for (int i = 0; i < tempArr.length; i++) {
data[i/4][i%4] = tempArr[i];
}
}
2.如何实现交互
2.1事件
①事件源:按钮 图片 窗体
②事件:某些操作 如:鼠标单击,鼠标划入
③绑定监听:当事件源上发生了某件事,则执行某段代码
KeyListener:键盘监听
MouseListener:鼠标监听
ActionListener:动作监听
2.4实现事件监听的两种方式
2.4.1匿名内部类
package Test;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
/*
在这个代码中,我将以匿名内部类的方式实现事件监听
*/
public class ActionListenerTest {
public static void main(String[] args) {
//创建一个窗体对象
JFrame jFrame = new JFrame();
//设置界面宽高
jFrame.setSize(603, 680);
//设置界面的标题
jFrame.setTitle("拼图游戏单机版");
//设置界面置顶
jFrame.setAlwaysOnTop(true);
//设置界面居中
jFrame.setLocationRelativeTo(null);
//设置关闭模式
jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
//取消默认的居中放置
jFrame.setLayout(null);
//设置按钮
JButton jb1 = new JButton("点我吧");
jb1.setBounds(0,0,105,105);
jb1.addActionListener(
//匿名内部类
new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Random r = new Random();
jb1.setLocation(r.nextInt(500),r.nextInt(500));
}
}
);
jFrame.getContentPane().add(jb1);
//让窗体显示
jFrame.setVisible(true);
}
}
2.4.2本类作为事件监听接口的实现类
package Test;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
//本类用来测试事件监听
//我们重写接口ActionListener中的actionPerformed,自己就是addActionListener的实现类
public class MyJFrame extends JFrame implements ActionListener {
//设置一个按钮
JButton jb1 = new JButton("点我啊");
JButton jb2 = new JButton("我是另一个按钮");
public MyJFrame(){
//设置界面宽高
this.setSize(603,680);
//设置界面的标题
this.setTitle("拼图游戏单机版");
//设置界面置顶
this.setAlwaysOnTop(true);
//设置界面居中
this.setLocationRelativeTo(null);
//设置关闭模式
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
//取消默认的居中放置
this.setLayout(null);
jb1.setBounds(0,0,105,105);
jb1.addActionListener(this);//自己就是addActionListener的实现类
jb2.setBounds(200,200,105,105);
jb2.addActionListener(this);
//将按钮加入窗体中
this.getContentPane().add(jb1);
this.getContentPane().add(jb2);
//让窗体显示
this.setVisible(true);
}
//重写接口中的方法
@Override
public void actionPerformed(ActionEvent e) {
//判断当前按钮是谁
//获取被点击的按钮对象
Object jb = e.getSource();
if(jb==jb1){
jb1.setSize(200,200);
} else if (jb==jb2) {
Random rd = new Random();
jb2.setLocation(rd.nextInt(500),rd.nextInt(500));
}
}
}
3.美化界面
针对GameJFrame类中的initImage方法做一个界面美化
private void initImage() {
//先加载的图片在上面,后加载的图片在下面
for (int i=0;i<4;i++){
for (int j=0;j<4;j++){
//获取图片编号
int number = data[i][j];
//创建ImageIcon对象
ImageIcon icon = new ImageIcon("image/animal/animal3/"+number+".jpg");
//创建JLabel对象(JLabel一个管理容器)
JLabel jLabel = new JLabel(icon);
//指定图片位置
jLabel.setBounds(105*j+83,105*i+134,105,105);
//给图片添加边框
jLabel.setBorder(new BevelBorder(BevelBorder.LOWERED));
//把JLabel添加到界面当中
this.getContentPane().add(jLabel);
}
}
//添加背景图片
JLabel Background = new JLabel(new ImageIcon("image/background.png"));
Background.setBounds(40,40,508,560);
this.getContentPane().add(Background);
}
4.实现图片移动业务逻辑
4.1图片根据方向键移动
步骤:
1.本类实现KeyListener接口,并重写所有抽象方法
2.给整个界面添加键盘监听事件
3.统计一下空白方块对应的数字0在二维数组中的位置
4.在KeyReleased方法当中实现移动的逻辑
@Override
public void keyReleased(KeyEvent e) {
//实现键盘方向键控制移动,其实就是交换[0][0]位置,及其周边位置的图片
int code = e.getKeyCode();
if(code==37){
//左
if(y+1<4){
data[x][y]=data[x][y+1];
data[x][y+1]=0;
y++;
initImage();
}
}
else if(code==38){
//上
if(x+1<4){
data[x][y]=data[x+1][y];
data[x+1][y]=0;
x++;
initImage();
}
}
else if(code==39){
//右
if(y-1>=0){
data[x][y]=data[x][y-1];
data[x][y-1]=0;
y--;
initImage();
}
}
else if(code==40){
//下
if(x-1>=0){
data[x][y]=data[x-1][y];
data[x-1][y]=0;
x--;
initImage();
}
}
}
4.2查看全图功能
在代码中,我设置按住ctrl键不松,就可以查看原图
@Override
//这里我们实现的逻辑是按住ctrl不松,查看完整的图片
public void keyPressed(KeyEvent e) {
if(victory()){
return;
}
int code = e.getKeyCode();
if(code==17){
//清空图片
this.getContentPane().removeAll();
//创建对象
JLabel jLabel = new JLabel(new ImageIcon(path+"all.jpg"));
jLabel.setBounds(83,134,420,420);
this.getContentPane().add(jLabel);
//添加背景图片
JLabel Background = new JLabel(new ImageIcon("image/background.png"));
Background.setBounds(40,40,508,560);
this.getContentPane().add(Background);
//刷新
this.getContentPane().repaint();
}
}
4.3作弊码
我设置了按m,一键通关,原理是改变data数组,然后重新初始化图片
//作弊码,一件通关
else if(code==77){
data = new int[][]{
{
1,2,3,4},
{
5,6,7,8},
{
9,10,11,12},
{
13,14,15,0}
};
initImage();
}
4.4判断胜利
利用一个按顺序的win数组和data数组比对,如果完全一样的话,则打印出胜利,并禁止移动
//判断胜利
public boolean victory(){
for(int i=0;i<data[0].length;i++){
for(int j=0;j<data.length;j++){
if(win[i][j]!=data[i][j]){
return false;
}
}
}
return true;
}
4.4计数器
设置step变量,每按一步自增一次,让其显示出来
//计数器
JLabel stepCount = new JLabel("步数:"+step);
stepCount.setBounds(50,30,100,20);
this.getContentPane().add(stepCount);
5.菜单业务实现
为条目设置动作监听,类继承ActionListener接口,重写其中方法,针对不同条目执行对应代码
@Override
public void actionPerformed(ActionEvent e) {
Object item = e.getSource();
if(item==replayItem){
//重新游戏
initData();
step=0;
initImage();
} else if (item==reLoginItem) {
//重新登录
this.setVisible(false);
new LoginJFrame();
} else if (item==closeItem) {
//关闭
System.exit(0);//直接关闭虚拟机
}else if(item==myBlogItem) {
//博客
//创建一个弹框对象
JDialog jDialog = new JDialog();
JLabel jLabel = new JLabel(new ImageIcon("image/about.png"));
//设置宽高
jLabel.setBounds(0,0,380,539);
//把图片添加到弹框中
jDialog.getContentPane().add(jLabel);
//给弹框设置大小
jDialog.setSize(420,580);
//让弹框置顶
jDialog.setAlwaysOnTop(true);
//让弹框居中
jDialog.setLocationRelativeTo(null);
//弹框不关闭无法操作下面界面
jDialog.setModal(true);
//让弹框显示出来
jDialog.setVisible(true);
}
}
6.完成图片切换功能
设置更换条目,添加事件监听,随机生成,改变路径,重新加载图片
else if (item==girl) {
//更换美女图片
//生成随机路径
Random rd = new Random();
int num = rd.nextInt(8)+1;
path = "image/girl/girl"+num+"/";
initData();
step = 0;
initImage();
}