下载解压即可玩:扫雷
项目代码下载:java项目
目录
图片:
除了一些基本的功能,还添加的有各种点击音效,背景音效,弹窗等
然后就是一个挂,可以自动标记雷的位置
怎么做
我本人是在b站上的找的尚学堂的视频,而且基本都是同一个视频,但是质量确实可以
如何想练手java的话,试着做些小游戏很是不错的
这种游戏最好的就是跟着视频敲,看文章和书籍差别有点大了
做完之后可以把程序封装成一个exe文件,发送给朋友玩,比如一些地方直接放上朋友的头像,直接录他的话放游戏里之类的
java程序封装exe文件的话,建议看这个文章:java封装exe文件
里面有一些特殊问题的处理,而其他文章对这些问题都是冰山一角
代码细节
基本是按照视频中的程序框架写的,用了很多的类,其中基本将所有的数据放在了一个GameUtil.java类,然后定义了底层类和顶层类,一些相关的代码写在相关的类,实际没怎么特别关注这东西
遇到和自己理解不对,或者有问题的就可以csdn搜一下,这还是很重要的,比如
public void paint(Graphics g) {
这个类为什么能自动执行,static加和不加有什么区别等
除此之外还写了个外挂,其实就是点击某处,自动标记雷的位置,代码也是很简单
void Plug_in(){
for(int i=0;i<GameUtil.MAX_W;i++)
for(int j=0;j<GameUtil.MAX_H;j++)
if(GameUtil.mp[i][j]==-1&&GameUtil.MAP_TOP[i][j]==1){
GameUtil.MAP_TOP[i][j]=2; //找到未开为标记的雷,将其标记
return;
}
}
然后添加了播放音乐的功能,实现了点击音效和背景音效的功能。
具体实现:java播放音乐
感受
1.做完之后的最大收货,应该是了解了java类与类的一些关系,以及会了页面刷新,页面打印的使用,因为一年前用c做过推箱子,给我的感觉是两个语言做小游戏还是很像很像的,只是c是写在了一个程序里400行,而java是写在5,6个类的450行
2.在便捷方面,java有优势但是不大,这可能还是扫雷这个游戏小了些,再大些比如王者荣耀的英雄类,眩晕类,技能类,应该是有着不可替代的作用
3.然后最重要的感受,可能还是当初做推箱子的感受,学这些东西不是很难,学一个打印类可能就1天2天。主要是还是一些程序逻辑,也就是主干很像一个算法题,可能说是很多算法题。
4.还有就是页面打印这个问题,我的程序和视频中使用的应该都是以判断关键变量为主,这个我觉得还是比较麻烦了,页面就多了没法搞。
全部代码
有多个类
GameWin.java类 :
package com.sxt;
import sun.plugin2.message.GetAppletMessage;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*; //包含JFrame类
public class GameWin extends JFrame{
MapBottom mapBottom=new MapBottom();
BottomRay bottomRay=new BottomRay();
//点击
MusicPlay musicPlay1=new MusicPlay("imgs/music/1.wav");
//无效点击
MusicPlay musicPlay2=new MusicPlay("imgs/music/2.wav");
//背景
MusicPlay musicPlay3=new MusicPlay("imgs/music/3.wav");
//胜利音效
MusicPlay musicPlay4=new MusicPlay("imgs/music/4.wav");
Level level=new Level();
void launch(){
musicPlay3.musicMain(3);
this.setVisible(true); //开启窗口
this.setSize(GameUtil.width, GameUtil.height); //大小
this.setLocationRelativeTo(null); //居中
this.setTitle("扫雷"); //标题
this.setDefaultCloseOperation(EXIT_ON_CLOSE); //程序结束后关闭窗口
this.addMouseListener(new MouseAdapter() { //鼠标事件
@Override
public void mousePressed(MouseEvent e) { //点击事件
super.mouseClicked(e);
GameUtil.MOUSE_X=e.getX();
GameUtil.MOUSE_Y=e.getY();
if(GameUtil.difficulty==-1) {
musicPlay2.musicMain(1);
if(e.getButton()==1){
if(GameUtil.query((GameUtil.OFFSET*2+GameUtil.MAX_W*GameUtil.SQUARE_LENGTH-3*GameUtil.SQUARE_LENGTH)/2,(GameUtil.OFFSET*2+GameUtil.MAX_W*GameUtil.SQUARE_LENGTH-3*GameUtil.SQUARE_LENGTH)/2+3*GameUtil.SQUARE_LENGTH,
4*GameUtil.OFFSET,4*GameUtil.OFFSET+2*GameUtil.SQUARE_LENGTH,GameUtil.MOUSE_X,GameUtil.MOUSE_Y)){
GameUtil.difficulty=0;
}else if(GameUtil.query((GameUtil.OFFSET*2+GameUtil.MAX_W*GameUtil.SQUARE_LENGTH-3*GameUtil.SQUARE_LENGTH)/2,(GameUtil.OFFSET*2+GameUtil.MAX_W*GameUtil.SQUARE_LENGTH-3*GameUtil.SQUARE_LENGTH)/2+3*GameUtil.SQUARE_LENGTH,
7*GameUtil.OFFSET,7*GameUtil.OFFSET+2*GameUtil.SQUARE_LENGTH,GameUtil.MOUSE_X,GameUtil.MOUSE_Y)){
GameUtil.difficulty=1;
}else if(GameUtil.query((GameUtil.OFFSET*2+GameUtil.MAX_W*GameUtil.SQUARE_LENGTH-3*GameUtil.SQUARE_LENGTH)/2,(GameUtil.OFFSET*2+GameUtil.MAX_W*GameUtil.SQUARE_LENGTH-3*GameUtil.SQUARE_LENGTH)/2+3*GameUtil.SQUARE_LENGTH,
10*GameUtil.OFFSET,10*GameUtil.OFFSET+2*GameUtil.SQUARE_LENGTH,GameUtil.MOUSE_X,GameUtil.MOUSE_Y)){
GameUtil.difficulty=2;
}
if(GameUtil.difficulty>=0){
bottomRay.init();
GameUtil.width=2*GameUtil.OFFSET+GameUtil.SQUARE_LENGTH*GameUtil.MAX_W;
GameUtil.height=4*GameUtil.OFFSET+GameUtil.SQUARE_LENGTH*GameUtil.MAX_H;
}
}
}else{
if(GameUtil.query((GameUtil.OFFSET*2+(GameUtil.MAX_W-1)*GameUtil.SQUARE_LENGTH)/2,(GameUtil.OFFSET*2+(GameUtil.MAX_W-1)*GameUtil.SQUARE_LENGTH)/2+GameUtil.SQUARE_LENGTH-5,
GameUtil.OFFSET + 15,GameUtil.OFFSET + GameUtil.SQUARE_LENGTH + 10,GameUtil.MOUSE_X,GameUtil.MOUSE_Y)){
bottomRay.init();
} else if (GameUtil.state >= 1) {
musicPlay1.musicMain(1);
} else if (e.getButton() == 1) {
musicPlay2.musicMain(1);
GameUtil.LEFT = true;
} else if(e.getButton()==2){
if(GameUtil.query(50,100,50,100,GameUtil.MOUSE_X,GameUtil.MOUSE_Y)){
mapBottom.Plug_in();
}else {
musicPlay1.musicMain(1);
GameUtil.difficulty = -1;
}
}
else if (e.getButton() == 3) {
musicPlay2.musicMain(1);
GameUtil.RIGHT = true;
}
}
}
});
while(true){
repaint(); //刷新页面
try {
Thread.sleep(40); //每隔多少时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void paint(Graphics g) { //后绘制,也为主
Image offScreenImage = this.createImage(GameUtil.width, GameUtil.height); //创建画布
Graphics gImage = offScreenImage.getGraphics(); //获取画笔
gImage.drawImage(GameUtil.background,0,0,GameUtil.width,GameUtil.height,null);
if(GameUtil.difficulty==-1){
mapBottom.print_menu(gImage);
}else {
this.setSize(GameUtil.width,GameUtil.height); //大小
mapBottom.printSelf(gImage);
if (GameUtil.state == 2) {level.end();}
else if (level.Victory()) {
level.victory();
if(GameUtil.numbei==2) {
musicPlay4.musicMain(1);
GameUtil.numbei++;
}
}
}
g.drawImage(offScreenImage, 0, 0, null); //手动打印,放进去g的话可能出来后会自动打印,放画布的话还需手动打印
}
public static void main(String[] args) {
GameWin gameWin=new GameWin();
gameWin.launch();
}
}
MapBottom.java类:
package com.sxt;
import java.awt.*;
/**
* 底层地图
*
*/
public class MapBottom {
BottomRay bottomRay=new BottomRay();
void dfs(int x,int y){
if(GameUtil.sure(x,y)==false||GameUtil.MAP_TOP[x][y]!=1) return;
if(GameUtil.mp[x][y]>=1) GameUtil.MAP_TOP[x][y]=0;
if(GameUtil.mp[x][y]!=0) return;
GameUtil.MAP_TOP[x][y]=0;
for(int i=-1;i<=1;i++)
for(int j=-1;j<=1;j++)
dfs(x+i,y+j);
}
void mouse(){
int tmpx=GameUtil.MAP_W(GameUtil.MOUSE_X);
int tmpy=GameUtil.MAP_H(GameUtil.MOUSE_Y);
if(GameUtil.sure(tmpx,tmpy)) {
if (GameUtil.LEFT) {
dfs(tmpx,tmpy);
if(GameUtil.mp[tmpx][tmpy]==-1) GameUtil.state=2;
GameUtil.MAP_TOP[tmpx][tmpy]=0;
GameUtil.LEFT = false;
} else if (GameUtil.RIGHT) {
int tmp=GameUtil.MAP_TOP[tmpx][tmpy];
if(tmp==0 && GameUtil.mp[tmpx][tmpy]!=0){ //右键已开点
int sum=0;
for(int i=-1;i<=1;i++)
for(int j=-1;j<=1;j++)
if(GameUtil.sure(tmpx+i,tmpy+j)){
sum+=GameUtil.MAP_TOP[tmpx+i][tmpy+j]==2?1:0;
}
if(sum==GameUtil.mp[tmpx][tmpy]){
for(int i=-1;i<=1;i++)
for(int j=-1;j<=1;j++)
if(GameUtil.sure(tmpx+i,tmpy+j) && GameUtil.MAP_TOP[tmpx+i][tmpy+j]==1){
dfs(tmpx+i,tmpy+j);
if(GameUtil.mp[tmpx+i][tmpy+j]==-1) GameUtil.state=2;
GameUtil.MAP_TOP[tmpx+i][tmpy+j]=0;
}
}
}else if (tmp >= 1){
if(GameUtil.MAP_TOP[tmpx][tmpy]==1) GameUtil.SUM++;
else GameUtil.SUM--;
GameUtil.MAP_TOP[tmpx][tmpy] = 3 - tmp;
}
GameUtil.RIGHT = false;
}
}
}
void printSelf(Graphics g){ //绘制网格
mouse();
g.setColor(Color.blue);
for(int i=0;i<=GameUtil.MAX_W;i++){
g.drawLine(GameUtil.OFFSET+i*GameUtil.SQUARE_LENGTH,3*GameUtil.OFFSET,GameUtil.OFFSET+i*GameUtil.SQUARE_LENGTH,3*GameUtil.OFFSET+GameUtil.SQUARE_LENGTH*GameUtil.MAX_H);
}
for(int i=0;i<=GameUtil.MAX_H;i++){
g.drawLine(GameUtil.OFFSET,3*GameUtil.OFFSET+i*GameUtil.SQUARE_LENGTH,GameUtil.OFFSET+GameUtil.SQUARE_LENGTH*GameUtil.MAX_W,3*GameUtil.OFFSET+i*GameUtil.SQUARE_LENGTH);
}
for(int i=0;i<GameUtil.MAX_W;i++){
for(int j=0;j<GameUtil.MAX_H;j++) {
if(GameUtil.MAP_TOP[i][j]==1){
g.drawImage(GameUtil.top, //打印遮板
GameUtil.change_W(i),
GameUtil.change_H(j),
GameUtil.SQUARE_LENGTH,
GameUtil.SQUARE_LENGTH,
null);
}else if(GameUtil.MAP_TOP[i][j]==2) { //打印旗帜
g.drawImage(GameUtil.flag,
GameUtil.change_W(i),
GameUtil.change_H(j),
GameUtil.SQUARE_LENGTH,
GameUtil.SQUARE_LENGTH,
null);
} else if(GameUtil.mp[i][j] == -1) { //打印炸弹
g.drawImage(GameUtil.lei,
GameUtil.change_W(i) + 2,
GameUtil.change_H(j) + 2,
GameUtil.SQUARE_LENGTH - 4,
GameUtil.SQUARE_LENGTH - 4,
null);
}else{ //打印数字
g.drawImage(GameUtil.images[GameUtil.mp[i][j]],
GameUtil.change_W(i) + 15,
GameUtil.change_H(j) + 10,
GameUtil.SQUARE_LENGTH - 30,
GameUtil.SQUARE_LENGTH - 20,
null);
}
}
}
//打印笑脸
g.drawImage(GameUtil.head[GameUtil.state],
(GameUtil.OFFSET*2+GameUtil.MAX_W*GameUtil.SQUARE_LENGTH-GameUtil.SQUARE_LENGTH)/2,
GameUtil.OFFSET+15,
GameUtil.SQUARE_LENGTH-5,
GameUtil.SQUARE_LENGTH-5,
null);
//打印剩余旗子数
GameUtil.drawWord(g,""+Math.max(GameUtil.bomb_MAX-GameUtil.SUM,0),GameUtil.OFFSET+20,GameUtil.OFFSET*2,Color.BLUE,30);
//打印时间:
GameUtil.drawWord(g,""+(GameUtil.END_TIME-GameUtil.START_TIME)/1000,
GameUtil.OFFSET+(GameUtil.MAX_W-1)*GameUtil.SQUARE_LENGTH,GameUtil.OFFSET*2,Color.BLUE,30);
if(GameUtil.state==0) GameUtil.END_TIME=System.currentTimeMillis();
}
void print_menu(Graphics g) {
g.drawImage(GameUtil.menu[0],
(GameUtil.OFFSET*2+GameUtil.MAX_W*GameUtil.SQUARE_LENGTH-3*GameUtil.SQUARE_LENGTH)/2,
4*GameUtil.OFFSET,
3*GameUtil.SQUARE_LENGTH,
2*GameUtil.SQUARE_LENGTH,
null);
g.drawImage(GameUtil.menu[1],
(GameUtil.OFFSET*2+GameUtil.MAX_W*GameUtil.SQUARE_LENGTH-3*GameUtil.SQUARE_LENGTH)/2,
7*GameUtil.OFFSET,
3*GameUtil.SQUARE_LENGTH,
2*GameUtil.SQUARE_LENGTH,
null);
g.drawImage(GameUtil.menu[2],
(GameUtil.OFFSET*2+GameUtil.MAX_W*GameUtil.SQUARE_LENGTH-3*GameUtil.SQUARE_LENGTH)/2,
10*GameUtil.OFFSET,
3*GameUtil.SQUARE_LENGTH,
2*GameUtil.SQUARE_LENGTH,
null);
GameUtil.drawWord(g,"单击滚轮重新选择难度",GameUtil.MAX_W*GameUtil.SQUARE_LENGTH-GameUtil.SQUARE_LENGTH*4,GameUtil.SQUARE_LENGTH+GameUtil.SQUARE_LENGTH*GameUtil.MAX_H,Color.BLUE,20);
}
void Plug_in(){
for(int i=0;i<GameUtil.MAX_W;i++)
for(int j=0;j<GameUtil.MAX_H;j++)
if(GameUtil.mp[i][j]==-1&&GameUtil.MAP_TOP[i][j]==1){
GameUtil.MAP_TOP[i][j]=2; //找到未开为标记的雷,将其标记
return;
}
}
}
GameUtil.java类:
package com.sxt;
import java.awt.*;
import java.awt.image.ImageObserver;
import java.awt.image.ImageProducer;
/**
* 工具类
* 存放静态参数
*/
public class GameUtil {
static int MAX_W_array[]={11,16,21};
static int MAX_H_array[]={11,14,16};
static int bomb_MAX_array[]={7,15,45};
//宽
static int MAX_W=11;
//高
static int MAX_H=11;
//雷区偏移量
static int OFFSET=45;
//格子边长
static int SQUARE_LENGTH=50;
static int width=2*GameUtil.OFFSET+GameUtil.SQUARE_LENGTH*GameUtil.MAX_W;
static int height=4*GameUtil.OFFSET+GameUtil.SQUARE_LENGTH*GameUtil.MAX_H;
//雷的数量
static int bomb_MAX=20;
//-1表示雷,0表示空,1-8表示雷数
static int MAP_BOTTOM[][]=new int[MAX_W+30][MAX_H+30];
//0表示开,1表示未开,2表示插旗
static int MAP_TOP[][]=new int[MAX_W+30][MAX_H+30];
//鼠标坐标
static int MOUSE_X;
static int MOUSE_Y;
//状态
static boolean LEFT=false;
static boolean RIGHT=false;
//状态,0表示游戏中,1表示胜利,2表示失败
static int state=0;
//旗子数
static int SUM=0;
//表格图
static int mp[][]=new int[MAX_W+30][MAX_H+30];
//开始结束时间
static long START_TIME=0;
static long END_TIME=0;
//难度,-1表示选择页面,0-2表示难度
static int difficulty=-1;
//重置参数
static boolean INIT=false;
static int change_W(int x){
return OFFSET+x*SQUARE_LENGTH;
}
static int change_H(int y){
return OFFSET*3+y*SQUARE_LENGTH;
}
static int MAP_W(int x){return x>GameUtil.OFFSET?(x-OFFSET)/SQUARE_LENGTH:-1; }
static int MAP_H(int y){ return y>GameUtil.OFFSET*3?(y-OFFSET*3)/SQUARE_LENGTH:-1; }
static boolean sure(int x,int y){return 0<=x && x<GameUtil.MAX_W && 0<=y && y<=GameUtil.MAX_H;}
//判断是否在一个矩形中
static boolean query(int l,int r,int lower,int u,int x,int y){ return l<=x && x<=r && lower<=y && y<=u;}
//炸弹图
static Image lei= Toolkit.getDefaultToolkit().getImage("imgs/炸弹.png");
//数字图
static Image[] images=new Image[10];
static {
for(int i=1;i<10;i++) images[i]=Toolkit.getDefaultToolkit().getImage("imgs/number/"+i+".png");
}
//遮板
static Image top=Toolkit.getDefaultToolkit().getImage("imgs/遮板.png");
//旗子
static Image flag=Toolkit.getDefaultToolkit().getImage("imgs/旗帜.png");
//笑脸图
static Image head[]=new Image[3];
static {
for(int i=0;i<3;i++) head[i]=Toolkit.getDefaultToolkit().getImage("imgs/头像/"+i+".png");
}
//难度
static Image menu[]=new Image[4];
static {
menu[0]=Toolkit.getDefaultToolkit().getImage("imgs/menu/简单.png");
menu[1]=Toolkit.getDefaultToolkit().getImage("imgs/menu/普通.png");
menu[2]=Toolkit.getDefaultToolkit().getImage("imgs/menu/困难.png");
menu[3]=Toolkit.getDefaultToolkit().getImage("imgs/menu/难度.png");
}
//背景
static Image background=Toolkit.getDefaultToolkit().getImage("imgs/background.png");
//打印字符串
static void drawWord(Graphics g,String str,int x,int y,Color color,int Size){
g.setColor(color);
g.setFont(new Font("仿宋",Font.BOLD,Size));
g.drawString(str,x,y);
}
//胜利次数
static int numbei=0;
}
Level.java类:
package com.sxt;
import javax.swing.*;
public class Level {
void end(){
if(GameUtil.INIT) return;
GameUtil.INIT=true;
for(int i=0;i<GameUtil.MAX_W;i++)
for(int j=0;j<GameUtil.MAX_H;j++)
if(GameUtil.mp[i][j]==-1) GameUtil.MAP_TOP[i][j] = 0;
JOptionPane.showMessageDialog( null,"请点击上方头像", "失败",JOptionPane.PLAIN_MESSAGE);
}
boolean Victory(){
int sum=0;
for(int i=0;i<GameUtil.MAX_W;i++)
for(int j=0;j<GameUtil.MAX_H;j++)
if(GameUtil.MAP_TOP[i][j]==0) sum++;
return sum>=GameUtil.MAX_W*GameUtil.MAX_H-GameUtil.bomb_MAX;
}
void victory(){
if(GameUtil.INIT) return;
GameUtil.INIT=true;
GameUtil.numbei++;
JOptionPane.showMessageDialog( null,"请点击上方头像", "胜利",JOptionPane.PLAIN_MESSAGE);
GameUtil.state=1;
}
}
BottomRay.java类:
package com.sxt;
public class BottomRay {
void init(){
GameUtil.MAX_W = GameUtil.MAX_W_array[GameUtil.difficulty];
GameUtil.MAX_H = GameUtil.MAX_H_array[GameUtil.difficulty];
GameUtil.bomb_MAX = GameUtil.bomb_MAX_array[GameUtil.difficulty];
GameUtil.INIT=false;
GameUtil.START_TIME=GameUtil.END_TIME=System.currentTimeMillis();
GameUtil.LEFT=GameUtil.RIGHT=false;
GameUtil.state=0;
GameUtil.SUM=0;
for(int i=0;i<GameUtil.MAX_W;i++)
for(int j=0;j<GameUtil.MAX_H;j++)
GameUtil.mp[i][j]=0;
int cnt = 0;
while (cnt < GameUtil.bomb_MAX) {
int x = (int) (Math.random() * GameUtil.MAX_W);
int y = (int) (Math.random() * GameUtil.MAX_H);
if (GameUtil.mp[x][y] == 0){
cnt++;
GameUtil.mp[x][y] = -1;
for(int dx=x-1;dx<x+2;dx++) {
for (int dy = y-1; dy < y+2; dy++) {
if (0<=dx&&dx<GameUtil.MAX_W && 0<=dy&&dy<=GameUtil.MAX_H && GameUtil.mp[dx][dy]!=-1)
GameUtil.mp[dx][dy]++;
}
}
}
}
for(int i=0;i<GameUtil.MAX_W;i++)
for(int j=0;j<GameUtil.MAX_H;j++)
GameUtil.MAP_TOP[i][j]=1;
}
}
MusicPlay.java类:
package com.sxt;
import java.applet.*;
import java.io.*;
import java.net.*;
class MusicPlay {
AudioClip aau;
String url;
MusicPlay(String url){
this.url=url;
playMusic();
}
void playMusic(){
try {
URL cb;
//可以在项目里创建一个Source folder包,将音乐文件放到这个包里,再把路径给它
File f = new File(url); //url这里放音乐路径。
cb = f.toURL();
aau = Applet.newAudioClip(cb);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
//控制音乐的方法,调用这个方法要给一个int参数
void musicMain(int n) {
//用switch循环
switch (n) {
//如果是1就开始播放
case 1:
aau.play();
break;
//如果是2,就停止播放
case 2:
aau.stop();
break;
case 3:
//循环播放
aau.loop();
break;
default:
break;
}
}
}