前言:游戏跟着尚学堂做的,目前还有一些问题,我会改正的。
文章目录
一、游戏结构
这个小游戏是看的b站上尚学堂出的视频,视频中一共是六个类,然后我又加了个音乐,又创建了一个类。(音乐用的是蛋仔派对的音频,最近比较沉迷蛋仔派对)
1.GameWin是小游戏的主类
2.Gold是金块类,用于存储金块
3.Line类 线类,矿工的绳子
4.Parent金块和石块提取出的父类
5.Sky背景类,存放天空和背景
6.Stone 石块类,存放石块
7.Music类,存放音乐,在主类中调用
imgs包中存放图片和音乐
二、图片准备
图片本来准备在网站上找的,没找到合适的,最后加的尚学堂老师微信要来的图片,总共是九张。
三.实现代码
GameWin类
变量
1.state整个游戏的状态0未开始1运行2商店3失败4胜利
2.image展示的图片
3.gameTime倒计时
4.start是否开始的判断条件
方法
1.launch窗口事件,在launch方法中添加鼠标事件,其中左键1右键3滚轮2。点击左键实现抓取,在商店中点击左键可购买药水。右键使用药水爆破石块或者加速拉取金块,在商店中点击右键进入下一关并且不购买药水。
2.next Level下一关
3.paint绘制方法(重写方法)
4.main主方法
代码实现
package huangjinkuanggong;
import java.util.*;
import java.util.List;
import javax.swing.JFrame;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class GameWin extends JFrame {
static int state;// 整个游戏的状态 0未开始 1 运行 2商店 3失败 4胜利
Sky sky = new Sky();// 背景类
Line line = new Line(this);// 钩子的线类
Image image;// 图片
public static long gameTime = 30;// 倒计时初始时间
public Thread start;// 是否开始
List<Parent> parentList = new ArrayList<>();// 存放父类
{
boolean isPlace = true;// 是否可以放置
for (int i = 0; i < 7; i++) {
// 生成不同金块
Double random = Math.random();
// 先存放金块
Gold gold;
if (random < 0.3)// 概率30%以下生成小金块
{
gold = new GoldMini();
} else if (random < 0.7)// 概率70%以下30%以上生成普通金块
{
gold = new Gold();
} else// 其他以下生成大金块
{
gold = new Goldplus();
}
// 判断是否重叠
for (Parent parent : parentList) {
if (gold.getRec().intersects(parent.getRec())) {
isPlace = false;
}
}
// 添加未重叠元素
if (isPlace) {
parentList.add(gold);
} else {
isPlace = true;
i--;
}
}
// 利用for循环来存放石块
for (int i = 0; i < 3; i++) {
Stone stone = new Stone();
for (Parent parent : parentList) {
if (stone.getRec().intersects(parent.getRec())) {
isPlace = false;
}
}
if (isPlace) {
parentList.add(stone);// 存储多个石块
} else {
isPlace = true;
i--;
}
}
}
void launch() { // 定义窗口事件,无参构造方法
this.setVisible(true); // 窗口可见
this.setSize(768, 1000); // 窗口大小
this.setResizable(false);
this.setLocationRelativeTo(null); // 窗口位置
this.setTitle("黄金矿工"); // 窗口名称
setDefaultCloseOperation(EXIT_ON_CLOSE); // 关闭窗口操作
// 在launch中添加鼠标事件
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
switch (state) {
case 0:
if (e.getButton() == 3) {
if (start == null) {
start = new Thread(() -> {
try {
while (true) {
gameTime -= 1;
Thread.sleep(1000);
}
} catch (Exception ex) {
ex.printStackTrace();
}
});
start.start();
}
state = 1;
}
break;
case 1:
if (e.getButton() == 1 && line.state == 0) // 1左键 2滑轮 3右键
{
line.state = 1;
}
// 设置右键事件
if (e.getButton() == 3 && line.state == 3) {
if (Sky.yaoBasic > 0) {
Sky.yaoBasic--;
Sky.yaoState = true;
}
}
;
case 2:
if (e.getButton() == 1)// 是否选择购买药水
{
System.out.println("购买");
state = 1;
sky.shop = true;// 调整状态
}
if (e.getButton() == 3)// 不购买,进入下一个关
{
System.out.println("不购买");
state = 1;
System.currentTimeMillis();
}
break;
case 3:
// 设置重开
if (e.getButton() == 1) {
state = 0;
sky.reGame();
line.reGame();
}
break;
case 4:
// 当点击左键时可以重新开始
if (e.getButton() == 1) {
state = 0;
sky.reGame();
line.reGame();
}
break;
default:
}
}
});
// 用死循环来实现窗口的重新绘制
while (true) {
repaint();
// 调用下一关
nextLevel();
// 降低刷新率,在循环里面设置
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void nextLevel() {
if (GameWin.gameTime <= 0) {
System.out.println(sky.total);
System.out.println(sky.goal);
if (sky.total >= sky.goal) {
if (sky.level == 5) {
state = 4;
System.out.println("通关");
} else {
sky.level++;
GameWin.state = 2;
System.out.println("商店:" + state);
}
} else {
// 失败,状态调为失败,展示失败页面
GameWin.state = 3;
}
dispose();
System.out.println(state);
// 刷新窗体
GameWin.gameTime = 30;
GameWin gameWin1 = new GameWin();
gameWin1.launch();
}
}
public void paint(Graphics g) {
image = this.createImage(768, 1000);
// 为了放置金块和石块的闪动,先绘制一个窗口
Graphics grap = image.getGraphics();
sky.paint(grap);
if (state == 1) {
line.paint(grap);
// 绘制金块
for (Parent gold : parentList) {
gold.parin(grap);
}
}
g.drawImage(image, 0, 0, null);
}
public static void main(String[] args) throws InterruptedException {
Music audioPlayWave = new Music("/huangjinkuanggong/src/imgs/张紫宁 - 蛋仔星派对 (和声伴奏)(2023-05-28 20-27-09 314).wav");
audioPlayWave.start();
@SuppressWarnings("unused")
int musicOpenlab =1;
GameWin gameWin = new GameWin();
gameWin.launch();
}
}
2.Parent类
定义多次用到的变量
代码实现
package huangjinkuanggong;
import java.awt.*;
public class Parent {
// 坐标
int x, y;
// 宽高
int w, h;
// 图片
Image img;
// 标记
boolean flag;
// 重量
int m;
// 钱
int money;
// 类型 1是金块,2是石头
int type;
// 绘制方法
void parin(Graphics g) {
g.drawImage(img, x, y, null);
}
public int getW() {
return w;
}
// 获取矩形
public Rectangle getRec(){
return new Rectangle(x,y,w,h);
}
}
3.Gold类(包含大金块,金块,小金块)
Gold类继承Parent类中的方法和类中定义的金块属性
代码实现
package huangjinkuanggong;
import java.awt.*;
public class Gold extends Parent {
Gold() {
this.x = (int) (Math.random() * 500);// 生成金块的横坐标
this.y = (int) (Math.random() * 550 + 250);//生成金块的纵坐标
this.w = 105;// 金块的宽
this.h = 105;// 金块的高
this.flag = false;// 是否背勾中了
this.m = 180;// 金块的重量
this.money = 200;// 金块的价钱
this.type = 1;
this.img = Toolkit.getDefaultToolkit().getImage("src/imgs/gold1.gif");
}
}
class GoldMini extends Gold {
GoldMini() {
this.w = 72;// 金块的宽
this.h = 72;// 金块的高
this.m = 70;// 金块的重量
this.money = 100;// 金块的价钱
this.img = Toolkit.getDefaultToolkit().getImage("src/imgs/gold0.gif");
}
}
class Goldplus extends Gold {
Goldplus() {
this.w = 175;// 金块的宽
this.x = (int) (Math.random() * 550);
this.h = 175;// 金块的高
this.m = 230;// 金块的重量
this.money = 500;// 金块的价钱
this.img = Toolkit.getDefaultToolkit().getImage("src/imgs/gold2.gif");
}
}
4.Stone类
类中有关于石块的相关参数,继承Parent中的方法和属性
代码实现
package huangjinkuanggong;
import java.awt.*;
public class Stone extends Parent {
Stone() {
this.x = (int) (Math.random() * 700);
this.y = (int) (Math.random() * 550 + 300);
this.w = 53;
this.h = 53;
this.flag = false;
this.m = 150;
this.money = 50;
this.type = 2;
this.img = Toolkit.getDefaultToolkit().getImage("src/imgs/rock1.png");
}
}
5.Line类
类中的变量
x,y,endx,endy分别表示起点横坐标,纵坐标,终点横坐标纵坐标
length,maxLength,minLength表示绳子初始长度,最大最小长度
fangxiang表示绳子摇摆的方向,n是线的角度百分比
state绳子状态0摇摆状态1乡下抓取2抓取返回3绳子抓取到物体
gouzi钩爪图片
类中的方法
logic检测钩子是否碰撞物体,碰撞检测
lines绘制钩子和绳子
paint 完成绳子状态
reGame充值元素
代码实现
package huangjinkuanggong;
import java.awt.*;
public class Line {
int x = 380, y = 180;// 起点坐标
int endx = 500, endy = 500;// 终点坐标
// 线段长度
double length = 50;
double n = 0;
double maxLength = 750;
double minLength = 50;
// 方向
int fangxiang = 1;
// 状态
int state = 0;
// 添加钩爪图片
Image gouzi = Toolkit.getDefaultToolkit().getImage("src/imgs/hook.png");
GameWin gameWin;
Line(GameWin gameWin) {
this.gameWin = gameWin;
}
//抓取判断
void logic() {
for (Parent obj : this.gameWin.parentList) {
if (endx > obj.x && endx < obj.x + obj.w && endy > obj.y && endx < obj.x + obj.h) {
state = 3;// 碰撞检测
obj.flag = true;
}
}
}
// 绘制
void lines(Graphics g) {
// 动态获取钩子摆动的坐标
endx = (int) (x + (length * Math.cos(n * Math.PI)));
endy = (int) (y + (length * Math.sin(n * Math.PI)));
g.setColor(Color.BLACK);
g.drawLine(x - 1, y - 1, endx, endy);
g.drawLine(x + 1, y + 1, endx, endy);
g.drawLine(x, y, endx, endy);
g.drawImage(gouzi, endx - 36, endy - 2, null);// 让钩子在中间
}
public void paint(Graphics g) {
logic();//调用判断
if (state == 0) {
if (n < 0.1) {
fangxiang = 1;
} else if (n > 0.9) {
fangxiang = -1;
}
n = n + 0.005 * fangxiang;
lines(g);
} else if (state == 1) {
if (length < maxLength) {
length = length + 10;
lines(g);
} else {
state = 2;
}
} else if (state == 2) {
if (length > minLength) {
length = length - 10;
lines(g);
} else {
state = 0;
}
} else if (state == 3) {
int m = 1;
if (length > minLength) {
length = length - 10;
lines(g);
for (Parent object : this.gameWin.parentList) {
if (object.flag) {
m = object.m;
object.x = endx - object.getW()/2+18 ;// 把金块放钩子中间
object.y = endy+8;
if (length <= minLength) {
object.x = -150;
object.y = -150;
object.flag = false;
Sky.yaoState = false;
Sky.total += object.money;
state = 0;
}
if (Sky.yaoState) {
if (object.type == 1) {
m = 1;
}
if (object.type == 2) {
object.x = -150;
object.y = -150;
object.flag = false;
Sky.yaoState = false;
state = 2;
}
}
}
}
try {
Thread.sleep(m);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 重置元素
void reGame() {
n = 0;// 线的角度百分比
length = 50;// 线的长度
}
}
6.Sky背景类
代码实现
类中变量
level 关卡数
goal 目标金额
total 总金额
yaoBasic 初始药水值
yaoState 药水状态 使用或未使用
price 药水价格
shop 是否购买药水
加一些的图片
类中方法
paint 绘制不同状态的窗口样式
draw 提取公共的代码
reGame 重置元素
package huangjinkuanggong;
import java.awt.*;
public class Sky {
// 定义
static int level = 1;
// 目标金额
int goal = level * 200 + 100;
// 总金额
static int total = 0;
static int yaoBasic = 3;// 初始药水值
// 药水状态
static boolean yaoState = false;
int price = (int) Math.random() * 100 + 10;
// 是否购买药水
boolean shop = false;
// 开始和结束
Image yao = Toolkit.getDefaultToolkit().getImage("src/imgs/water.png");
Image bg = Toolkit.getDefaultToolkit().getImage("src/imgs/bg1.jpg");
Image bg1 = Toolkit.getDefaultToolkit().getImage("src/imgs/bg.jpg");// 抓矿背景
Image bg2 = Toolkit.getDefaultToolkit().getImage("src/imgs/bg.jpg");// 商店背景
Image miner = Toolkit.getDefaultToolkit().getImage("src/imgs/people.png");
public void paint(Graphics g) {
g.drawImage(bg, 0, 0, null);
g.drawImage(bg1, 0, 200, null);
switch (GameWin.state) {
case 0:
g.drawImage(bg2, 0, 200, null);
draw(g, 50, "准备开始,点击右键", 150, 400, Color.BLACK);
break;
case 1:
g.drawImage(miner, 310, 50, null);
draw(g, 30, "目标得分" + goal, 30, 110, Color.black);
draw(g, 30, "总金额:" + total, 30, 150, Color.BLACK);
// 药水绘制
g.drawImage(yao, 480, 90, null);
draw(g, 30, "x" + yaoBasic, 550, 150, Color.BLACK);
// 计算时间
draw(g, 30, "剩余时间:" + GameWin.gameTime, 520, 90, Color.BLACK);
//repaint
break;
case 2:
g.drawImage(bg2, 0, 200, null);
draw(g, 60, "商店", 220, 280, Color.black);
System.out.println("...........");
g.drawImage(yao, 340, 500, null);
// 药水价格
draw(g, 30, "价格" + price, 337, 650, Color.black);
// //是否购买
// draw(g,30,"是否购买",300,450,Color.black);
if (shop && total >= price) {
total -= price;
yaoBasic++;
shop = false;
System.currentTimeMillis();
}
break;
case 3:
g.drawImage(bg2, 0, 200, null);
draw(g, 50, "你失败啦!", 250, 350, Color.RED);// rapaint
draw(g, 50, "总金额!" + total, 200, 450, Color.BLACK);
break;
case 4:
g.drawImage(bg2, 0, 200, null);
draw(g, 50, "您通关啦!", 250, 350, Color.RED);// rapaint
draw(g, 50, "总金额!" + total, 200, 450, Color.BLACK);
break;
default:
}
}
public static void draw(Graphics g, int size, String str, int x, int y, Color color) {
g.setColor(color);
g.setFont(new Font("楷体", Font.BOLD, size));
g.drawString(str, x, y);
}
// 重置元素
void reGame() {
// 定义关卡数
int level = 1;
// 目标金额
int goal = level * 200 + 100;
// 总金额
int total = 0;
int yaoBasic = 3;// 初始药水值
// 药水状态
boolean yaoState = false;
}
}
7.Music类
这个是在网上找的方法,用音频转换器转换音乐为wav格式
代码实现
package huangjinkuanggong;
import java.io.File;
import java.io.IOException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;
public class Music extends Thread {
private String fileName;
private final int EXTERNAL_BUFFER_SIZE = 524288;
public Music(String wavFile) {
this.fileName = wavFile;
}
@SuppressWarnings("unused")
public void run() {
File soundFile = new File(fileName); // 播放音乐的文件名
if (!soundFile.exists()) {
System.err.println("Wave file not found:" + fileName);
return;
}
while (true) { // 设置循环播放
AudioInputStream audioInputStream = null; // 创建音频输入流对象
try {
audioInputStream = AudioSystem.getAudioInputStream(soundFile); // 创建音频对象
} catch (UnsupportedAudioFileException e1) {
e1.printStackTrace();
return;
} catch (IOException e1) {
e1.printStackTrace();
return;
}
AudioFormat format = audioInputStream.getFormat(); // 音频格式
SourceDataLine auline = null; // 源数据线
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
try {
auline = (SourceDataLine) AudioSystem.getLine(info);
auline.open(format);
} catch (LineUnavailableException e) {
e.printStackTrace();
return;
} catch (Exception e) {
e.printStackTrace();
return;
}
if (auline.isControlSupported(FloatControl.Type.PAN)) {
FloatControl pan = (FloatControl) auline.getControl(FloatControl.Type.PAN);
}
auline.start();
int nBytesRead = 0;
byte[] abData = new byte[EXTERNAL_BUFFER_SIZE];
try {
while (nBytesRead != -1) {
nBytesRead = audioInputStream.read(abData, 0, abData.length);
if (nBytesRead >= 0)
auline.write(abData, 0, nBytesRead);
}
} catch (IOException e) {
e.printStackTrace();
return;
} finally {
auline.drain();
//auline.close();
}
}
}
}