简介
作者简介:青铜码农,和大多数同学一样从零开始一步步学习,一步步积累。期待您的关注,让我们一起成长~注:本人学疏才浅,文章如有错误之处,敬请指正~
内容简介:实现4399《黄金矿工》小游戏
目录
功能预览:
游戏中一些对象的介绍 :
实现步骤:
1.创建Game类继承JFrame
2.创建图片Background类,以便绘制地图
3.绘制绳索
4.绳索的延长与收回
5.双缓存技术
6.创建金块
?7.抓取判定
?8.抓取返回
9.多个金块
10.创建石头
?11.设置不同的回收速度
12.解决物体位置重叠
13.设置多种金块
14.绳索的钩爪
?16.添加快速抓取功能
17.设置关卡
18.设置游戏状态
?
19.倒计时效果:
20.失败状态:
21.成功状态:
?22.重置游戏
?
23.游戏商店:
功能预览:
游戏中一些对象的介绍 :

实现步骤:

1.创建Game类继承JFrame
在类中创建一个Game方法,以便初始化窗口
void Game() {
setVisible(true);
setSize(800, 824);// 窗口大小
setLocationRelativeTo(null);// 窗口将放置在屏幕中央
setTitle("CodeDragons黄金矿工");// 设置标题
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// 关闭程序
}
2.创建图片Background类,以便绘制地图
import java.awt.*;
/**
* @author WayLon
* @create 2021-07-15 19:19
* 备注:
*/
public class Background {
// 背景图片初始化
Image bg= Toolkit.getDefaultToolkit().getImage("src/images/土壤.jpg");
Image people=Toolkit.getDefaultToolkit().getImage("src/images/人物.png");
public void BackgroundPaint(Graphics g){
g.drawImage(bg, 0, 0, null);
g.drawImage(people, 380, 190, null);
}
}
再返回Game类中创建一个paint()方法
Background bg = new Background();
/**
* 绘制游戏地图
*
* @param g
*/
public void paint(Graphics g) {
bg.BackgroundPaint(g);
}
在Game类中创建一个main()方法以便启动程序
/**
* 启动程序
*/
public static void main(String[] args) {
Game frame = new Game();
frame.Game();
}
运行效果如图:

3.绘制绳索
?说明:绳索会左右摇摆且可以收回来。
创建一个绳索Rope类,并初始化绳索的起点坐标和终点坐标以及绳索的长度,绳索摆动的角度
绳索的终点坐标算法如图:

import java.awt.*;
/**
* @author WayLon
* @create 2021-07-15 19:14
* 备注:
*/
public class Rope {
// 绳索的起点坐标
int x = 505;
int y = 253;
// 绳索的终点坐标
int endx = 400;
int endy = 700;
// 初始化绳索的长度
double length = 200;
// 初始化绳索的角度
double angle = 0;
public void Ropepaint(Graphics g) {
angle=angle+0.005;// 每次调用这个方法就将绳索的角度加0.005,以便实现绳索的不停转动
endx = (int) (x + length * Math.cos(angle*Math.PI));
endy = (int) (y + length * Math.sin(angle*Math.PI));
Graphics2D g2d=(Graphics2D) g;
g2d.setStroke(new BasicStroke(2));// 设置绳索粗细
g2d.drawLine(x, y, endx, endy);// 画绳索
}
}
?运行效果如图:

?如果想让绳索不停的转动,只需要在Game类中的Game()方法添加以下代码:
void Game() {
// 此处为不做改变的代码
while (true) {
repaint();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
效果如图:

先别着急,此时的绳索是360°旋转, 而绳索只能在X轴下方来回摆动,所以可以设置绳索的角度取值范围的为0-π。(且此时的整个画面是不停的闪动的,需要用到双缓存技术去解决,后面再讲这个问题),这里我们只讨论绳索需要在X轴下方来回摆动,则需要在Rope类添加以下代码:
public class Rope {
// 绳索的起点坐标
// 绳索的终点坐标
// 初始化绳索的长度
// 初始化绳索的角度
// 以下为新增代码
// 初始化方向.1表示顺时针0~180度,-1表示逆时针0~180度
int dir = 1;
public void Ropepaint(Graphics g) {
if (angle <= 0) {
dir = 1;
} else if (angle >= 1) {
dir = -1;
}
// 此处省略原有代码
}
}
效果如图:

眼尖的同学在这里会发现,绳索还是超越了地平线。这个问题很好解决,只需要将新增代码修饰为以下代码:
public class Rope {
// 绳索的起点坐标
// 绳索的终点坐标
// 初始化绳索的长度
// 初始化绳索的角度
// 以下为新增代码
// 初始化方向.1表示顺时针0~180度,-1表示逆时针0~180度
int dir = 1;
public void Ropepaint(Graphics g) {
if (angle <= 0.1) {
dir = 1;
} else if (angle >= 0.9) {
dir = -1;
}
// 此处省略原有代码
}
}
?再来看一下效果:

?嗯,这就完美许多了。
4.绳索的延长与收回
实现思路:
首先给绳索定义一个状态state,0表示摇摆,1表示抓取,2表示收回。
初始化状态state为0,=,当玩家按下键盘向下键时, 令state=1,当state=1时,延长绳索,且当绳索的长度小于500时,延长绳索的长度,直到绳索的长度等于500,此时再令state=2,收回绳索,当state=2且绳索的长度大于130时,收回绳索直到绳索长度等于130,也就是原始的绳索长度。当收回绳索的长度等于原始的长度时,令state=0恢复原来摆动的状态,以此循环类推。
为了代码整洁,下面我们将Ropepaint()方法里的这几行代码单独用一个方法封装起来。
void Rope(Graphics g) {
endx = (int) (x + length * Math.cos(angle * Math.PI));
endy = (int) (y + length * Math.sin(angle * Math.PI));
Graphics2D g2d = (Graphics2D) g;
g2d.setStroke(new BasicStroke(2));// 设置绳索粗细
g2d.drawLine(x, y, endx, endy);// 画绳索
}
在Game类中的Game()方法添加键盘事件,如下:
addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_DOWN) {
rope.state = 1;
}
}
});
绳索的延长已收回完整代码如下:
public class Rope {
// 绳索的起点坐标
// 绳索的终点坐标
// 初始化绳索的长度
// 初始化绳索的角度
// 初始化方向.1表示顺时针0~180度,-1表示逆时针0~180度
// 初始化绳索的状态,0表示摇摆,1表示抓取,2表示收回
int state = 0;
void Rope(Graphics g) {
endx = (int) (x + length * Math.cos(angle * Math.PI));
endy = (int) (y + length * Math.sin(angle * Math.PI));
Graphics2D g2d = (Graphics2D) g;
g2d.setStroke(new BasicStroke(2));// 设置绳索粗细
g2d.drawLine(x, y, endx, endy);// 画绳索
}
public void Ropepaint(Graphics g) {
switch (state) {
case 0:// 当state=0时,摆动绳索
if (angle <= 0.1) {
dir = 1;
} else if (angle >= 0.9) {
dir = -1;
}
angle += 0.005 * dir;
Rope(g);
break;
case 1:// 当state=1时,延长绳索抓取
if (length < 650) {// 当绳索的长度小于650时,延长绳索的长度直到绳索的长度等于650
length += 10;
Rope(g);
} else {
state = 2;// 绳索长度等于650时,令state=2
}
break;
case 2:// 当state=2时,收回绳索
if (length > 130) {// 当state=2且绳索的长度大于130时,收回绳索直到绳索长度等于130,也就是原始的绳索长度
length -= 10;
Rope(g);
} else {
state = 0;// 当收回绳索的长度等于原始的长度时,令state=0恢复原来摆动的状态
}
break;
}
}
}
效果图如下:

代码敲到这里,相信很多同学已经被这个闪动的画面闪得不想再继续码下去了,那好,现在我们就来解决这个烦人的问题。
5.双缓存技术
原理讲解:双缓冲技术在很多动画中被采用。主要原理是创建一幅BufferedImage图像,将每一帧画入图像,然后调用drawImage方法,将整个BufferedImage图像一次画到屏幕上去。
代码实现:
首先在Game类中创建一个画布offFlickerImage;再将Game类中的paint方法改写成以下代码:
public void paint(Graphics g) {
offFlickerImage=this.createImage(800,824);
Graphics gImage=offFlickerImage.getGraphics();
bg.BackgroundPaint(gImage);
rope.Ropepaint(gImage);
g.drawImage(offFlickerImage,0,0,null);
}
运行一下,是不是更加完美了呢?
6.创建金块
?因为金块都有一些相同的属性,例如宽高度。所以我们可以创建一个Object类作为Gold类的父类。
Object类:
import java.awt.*;
/**
* @author WayLon
* @create 2021-07-15 22:01
* 备注:
*/
public class Object {
// 物体的坐标
int x;
int y;
// 物体的宽高
int width;
int height;
// 图片
Image image;
// 标记是否能移动
boolean flag;
public void goldPaint(Graphics g) {
g.drawImage(image, x, y, null);
}
public int getWidth() {
return width;
}
}
Gold类:
import java.awt.*;
/**
* @author WayLon
* @create 2021-07-15 22:19
* 备注:
*/
public class Gold extends Object {
public Gold() {
this.x = (int) (Math.random() * 750);
this.y = (int) (Math.random() * 300 + 400);
this.width = 55;
this.height = 55;
this.flag = false;
this.image = Toolkit.getDefaultToolkit().getImage("src/images/金元宝.png");
}
}
在Game类中new一个Gold类对象,再往paint()方法中调用Gold类的drawImage()方法。
public class Game extends JFrame {
Gold gold = new Gold();
public Game() {
// 此处省略不做改变的代码
}
/**
* 启动程序
*/
public static void main(String[] args) {
// 此处省略不做改变的代码
}
/**
* 绘制游戏地图
*
* @param g
*/
public void paint(Graphics g) {
// 此处省略不做改变的代码
gold.goldPaint(gImage);
g.drawImage(offFlickerImage, 0, 0, null);
}
}
运行效果如图:

?7.抓取判定
此模块运用的是Java游戏中的碰撞检测,查看相关知识点可点击→矩形碰撞检测 & 圆形和像素碰撞检测。
修改Rope类中的代码:
public class Rope {
// 绳索的起点坐标
// 绳索的终点坐标
// 初始化绳索的长度
// 初始化绳索的角度
// 初始化方向.1表示顺时针0~180度,-1表示逆时针0~180度
// 初始化绳索的状态,0表示摇摆,1表示抓取,2表示收回
Game frame;
Rope(Game frame) {
this.frame = frame;
}
void judge() {
if (endx > this.frame.gold.x && endx < this.frame.gold.x + this.frame.gold.width &&
endy > this.frame.gold.y && endy < this.frame.gold.y + this.frame.gold.height) {
System.out.println("抓到啦!");
}
}
void Rope(Graphics g) {
// 此处省略不做修改的代码
}
public void Ropepaint(Graphics g) {
judge();
switch (state) {
// 此处省略不做修改的代码
}
}
效果如图:

?8.抓取返回
之前的state状态新增一个3,用来表示抓取返回。
public class Rope {
// 绳索的起点坐标
// 绳索的终点坐标
// 初始化绳索的长度
// 初始化绳索的角度
// 初始化方向.1表示顺时针0~180度,-1表示逆时针0~180度
// 初始化绳索的状态,0表示摇摆,1表示抓取,2表示收回
Game frame;
Rope(Game frame) {
this.frame = frame;
}
void judge() {
if (endx > this.frame.gold.x && endx < this.frame.gold.x + this.frame.gold.width &&
endy > this.frame.gold.y && endy < this.frame.gold.y + this.frame.gold.height) {
state = 3;
this.frame.gold.flag = true;
}
}
void Rope(Graphics g) {
// 此处省略不做修改的代码
}
public void Ropepaint(Graphics g) {
judge();
switch (state) {
case 0:
// 此处省略不做修改的代码
break;
case 1:
// 此处省略不做修改的代码
break;
case 2:
// 此处省略不做修改的代码
break;
case 3:
if (length > 130) {
length -= 10;
Rope(g);
this.frame.gold.x = endx - this.frame.gold.getWidth() / 2;
this.frame.gold.y = endy;
} else {
// 直接移到屏幕外
this.frame.gold.x = -100;
this.frame.gold.y = -100;
state = 0;
}
break;
}
}
}
效果如图:

9.多个金块
利用list集合存储金块。在Game类中添加以下新代码,并删除两行代码(别漏删了,要不然后面会出现其中某一个金块钓不起来的糟糕情况):
public class Game extends JFrame {
// 此处省略不做改变的代码
Gold gold = new Gold();// 删除此行代码
List<Object> goldList = new ArrayList<Object>();// 储存金块,有这个集合后,上面一行代码可删除
{
for (int i = 0; i < 5; i++) {
goldList.add(new Gold());
}
}
public Game() {
// 此处省略不做改变的代码
}
/**
* 启动程序
*/
public static void main(String[] args) {
// 此处省略不做改变的代码
}
/**
* 绘制游戏地图
*
* @param g
*/
public void paint(Graphics g) {
// 此处省略不做改变的代码
for (Object o : goldList) {
o.goldPaint(gImage);
}
gold.goldPaint(gImage);// 删除此行代码
g.drawImage(offFlickerImage, 0, 0, null);// 此处行是不做改变的代码
}
}
修改Rope类中的judge()方法和Ropepaint()方法:
public class Rope {
// 此处为不做修改的代码块
void judge() {
for (Object o : this.frame.goldList) {
if (endx > o.x && endx < o.x + o.width &&
endy > o.y && endy < o.y + o.height) {
state = 3;
o.flag=true;
}
}
}
void Rope(Graphics g) {
// 此处为不做修改的代码块
}
public void Ropepaint(Graphics g) {
judge();
switch (state) {
case 0:// 此处为不做修改的代码块
case 1:// 此处为不做修改的代码块
case 2:// 此处为不做修改的代码块
case 3:
if (length > 130) {// 当state=2且绳索的长度大于130时,收回绳索直到绳索长度等于130,也就是原始的绳索长度
length -= 10;
Rope(g);
for (Object o : this.frame.goldList) {
if (o.flag) {
o.x = endx - o.getWidth() / 2;
o.y = endy;
if (length <= 130) {
// 直接移到屏幕外
o.x = -100;
o.y = -100;
o.flag = false;
state = 0;// 当收回绳索的长度等于原始的长度时,令state=0恢复原来摆动的状态
}
}
}
}
break;
}
}
}
效果如图:

10.创建石头
首先创建一个石头类Stone继承Object类。
import java.awt.*;
/**
* @author WayLon
* @create 2021-07-16 9:56
* 备注:
*/
public class Stone extends Object {
public Stone() {
this.x = (int) (Math.random() * 750);
this.y = (int) (Math.random() * 300 + 400);
this.width = 70;
this.height = 70;
this.flag = false;
this.image = Toolkit.getDefaultToolkit().getImage("src/images/石头.png");
}
}
再修改Game类:
public class Game extends JFrame {
// 此处省略不做修改的代码块
{
// 生成金块
for (int i = 0; i < 5; i++) {
// 此处省略不做修改的代码块
}
// 生成石头
for (int i = 0; i < 2; i++) {
goldList.add(new Stone());
}
}
public Game() {
// 此处省略不做修改的代码块
}
/**
* 启动程序
*/
public static void main(String[] args) {
// 此处省略不做修改的代码块
}
/**
* 绘制游戏地图
*
* @param g
*/
public void paint(Graphics g) {
// 此处省略不做修改的代码块
}
}
效果如图:

?11.设置不同的回收速度
给Object类添加一个变量m,用于表示物体的质量。
public class Object {
// 物体的坐标
// 物体的宽高
// 图片
// 标记是否能移动
// 物体的质量
int m;
public void goldPaint(Graphics g) {
// 此处省略不做改变的代码块
}
public int getWidth() {
// 此处省略不做改变的代码块
}
}
再到Gold类和Stone类中给物体的质量赋值(石头的质量要明显大于金块的质量)。
public class Gold extends Object {
public Gold() {
// 此处省略不做修改的代码块
this.m=35;
}
}
public class Stone extends Object {
public Stone() {
// 此处省略不做改变的代码块
this.m=100;
}
}
再到Rope类中新增以下代码:
public class Rope {
// 此处为不做修改的代码块
Rope(Game frame) {
// 此处为不做修改的代码块
}
void judge() {
// 此处为不做修改的代码块
}
void Rope(Graphics g) {
// 此处为不做修改的代码块
}
public void Ropepaint(Graphics g) {
// 此处为不做修改的代码块
switch (state) {
case 0: // 此处为不做修改的代码块
case 1: // 此处为不做修改的代码块
case 2: // 此处为不做修改的代码块
case 3:
int m=0;
if (length > 130) {
// 此处为不做修改的代码块
}
try {
Thread.sleep(m);
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
}
}
}
效果如图:
12.解决物体位置重叠
在Object类中添加获取矩形的方法:
// 获取矩形
public Rectangle getR() {
return new Rectangle(x, y, width, height)
}
import java.awt.*;
/**
* @author WayLon
* @create 2021-07-15 22:01
* 备注:
*/
public class Object {
// 物体的坐标
int x;
int y;
// 物体的宽高
int width;
int height;
// 图片
Image image;
// 标记是否能移动
boolean flag;
// 物体的质量
int m;
public void goldPaint(Graphics g) {
g.drawImage(image, x, y, null);
}
public int getWidth() {
return width;
}
}
13.设置多种金块
在Gold类中添加以下代码:
public class Gold extends Object {
public Gold() {
// 此处为不做修改的代码块
}
}
class GoldTwo extends Gold {
public GoldTwo() {
this.width = 55;
this.height = 37;
this.m = 110;
this.image = Toolkit.getDefaultToolkit().getImage("src/images/金条.png");
}
}
class GoldThree extends Gold {
public GoldThree() {
this.width = 55;
this.height = 55;
this.m = 65;
this.image = Toolkit.getDefaultToolkit().getImage("src/images/戒指.png");
}
}
且不同的金块按照不同的概率出现。元宝占50%,戒指占35%,金条占15%。
在Game的第一个循环中替换为以下代码:
public class Game extends JFrame {
// 此处为不做修改的代码块
{
for (int i = 0; i < 8; i++) {
double probability = Math.random();
if (probability < 0.15) {
goldList.add(new GoldThree());
} else if (probability < 0.35 && probability > 0.15) {
goldList.add(new GoldTwo());
} else if (probability > 0.35) {
goldList.add(new Gold());
}
}
for (int i = 0; i < 3; i++) {
// 此处为不做修改的代码块
}
}
public Game() {
// 此处为不做修改的代码块
}
/**
* 启动程序
*/
public static void main(String[] args) {
// 此处为不做修改的代码块
}
/**
* 绘制游戏地图
*
* @param g
*/
public void paint(Graphics g) {
// 此处为不做修改的代码块
}
}
?运行效果:

14.绳索的钩爪
public class Rope {
// 此处为不做修改的代码块
// 钩爪的图片
Image hook = Toolkit.getDefaultToolkit().getImage("src/images/钳子.png");
Rope(Game frame) {
// 此处为不做修改的代码块
}
void judge() {
// 此处为不做修改的代码块
}
void Rope(Graphics g) {
// 此处为不做修改的代码块
g2d.drawImage(hook,endx-35,endy-3,null);
}
public void Ropepaint(Graphics g) {
// 此处为不做修改的代码块
}
}

?15.金钱设置
先在Object类中设定一个cost变量;
// 物体的金钱数
int cost;
再到Stone类和Gold类进行初始化。
元宝价值为50;金条为550;戒指为250;石头为11;
再到Background类中,将总钱数写入:
public class Background {
// 总钱数
static int count = 0;
// 背景图片初始化
Image bg = Toolkit.getDefaultToolkit().getImage("src/images/土壤.jpg");
Image people = Toolkit.getDefaultToolkit().getImage("src/images/人物.png");
public static void drawText(Graphics g, int size, Color color, String s, int x, int y) {
g.setColor(color);
g.setFont(new Font("楷体", Font.BOLD, 24));
g.drawString(s, x, y);
}
public void BackgroundPaint(Graphics g) {
g.drawImage(bg, 0, 0, null);
g.drawImage(people, 380, 190, null);
drawText(g, 24, new Color(143, 112, 1), "金钱:$" + count, 30, 250);
}
}
再到Rope类中的case 3:添加一行代码实现加钱:
public class Rope {
Rope(Game frame) {
this.frame = frame;
}
void judge() {
}
void Rope(Graphics g) {
}
public void Ropepaint(Graphics g) {
judge();
switch (state) {
case 0:
case 1:
case 2:
case 3:// 此处为不做改变的代码
if (length <= 130) {
// 直接移到屏幕外
o.x = -100;
o.y = -100;
o.flag = false;
Background.count += o.cost;// 增加此行代码
state = 0;
}
}
}
}// 此处为不做改变的代码
}
}
}
效果如图:

?16.添加快速抓取功能
在物体被抓取的时候,按下键盘的向上键也就是使用魔法水物品,物体将迅速被拉回。
首先需要在Background类中添加魔法水。
public class Background {
// 总钱数
// 此处为不做改变的代码
// 爆竹的数量
static int firecrackersNum = 3;
// 爆竹的状态
static boolean fcFlag = false;
// 背景图片初始化
// 此处为不做改变的代码
Image firecrackers = Toolkit.getDefaultToolkit().getImage("src/images/魔法水.png");
public static void drawText(Graphics g, int size, Color color, String s, int x, int y) {
// 此处为不做改变的代码
}
public void BackgroundPaint(Graphics g) {
// 此处为不做改变的代码
g.drawImage(firecrackers, 580, 250, null);
drawText(g, 24, new Color(143, 112, 1), "*" + firecrackersNum, 630, 290);
}
}
效果如图:
?接下来在Game类中的Game()方法添加新的键盘事件:
public Game() {
// 此处为不做改变的代码
addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_DOWN) {
// 此处为不做改变的代码
}
if (e.getKeyCode() == KeyEvent.VK_UP && rope.state == 3 && Background.firecrackersNum > 0) {// 当按下向上键且绳索的状态为3时和有魔法水的情况下
Background.firecrackersNum--;
Background.fcFlag = true;
}
}
});
}
再修改Rope()类中Ropepaint()方法中的case 3:
public void Ropepaint(Graphics g) {
judge();
switch (state) {
case 0:// 此处为不做改变的代码
case 1:// 此处为不做改变的代码
case 2:// 此处为不做改变的代码
case 3:
// 此处为不做改变的代码
if (length <= 130) {
// 直接移到屏幕外
// 此处为不做改变的代码
Background.count += o.cost;// 新增此行代码
state = 0;
Background.fcFlag = false;// 新增此行代码
}
if (Background.fcFlag) {// 新增此行代码
m = 1;
}
}
}
}
// 此处为不做改变的代码
}
}
效果图如下:

17.设置关卡
当达到目标钱数后,将进入下一关。
在Backgound类中添加以下两个变量:
public class Background {
// 总钱数
// 爆竹的数量
// 爆竹的状态
// 关卡数
static int level = 1;
// 目标钱数
int goal = level * 100;
// 背景图片初始化
public static void drawText(Graphics g, int size, Color color, String s, int x, int y) {
// 此处为不做改变的代码
}
public void BackgroundPaint(Graphics g) {
// 此处为不做改变的代码
}
}
再到Game类中添加新代码:
public class Game extends JFrame {
// 此处为不做改变的代码
/**
* 启动程序
*/
public static void main(String[] args) {
// 此处为不做改变的代码
}
void Game() {
// 此处为不做改变的代码
while (true) {
repaint();
nextLevel();// 此行为新增代码
// 此处为不做改变的代码
}
}
/**
* 下一关
*/
private void nextLevel() {// 此代码块为新增代码
if (Background.count >= bg.goal) {
Background.level++;
dispose();
Game game1 = new Game();
game1.Game();
}
}
/**
* 绘制游戏地图
*
* @param g
*/
public void paint(Graphics g) {
// 此处为不做改变的代码
}
}
?再到Background类中绘制相应的面板以便显示目前关数和目标钱数:
public class Background {
// 总钱数
// 爆竹的数量
// 爆竹的状态
// 关卡数
static int level = 1;
// 目标钱数
int goal = level * 100;
// 背景图片初始化
public static void drawText(Graphics g, int size, Color color, String s, int x, int y) {
// 此处为不做改变的代码
}
public void BackgroundPaint(Graphics g) {
// 此处为不做改变的代码
// 关卡数
drawText(g, 20, new Color(143, 112, 1), "第" + level+"关", 370, 60);
// 目标钱数
drawText(g, 24, new Color(143, 112, 1), "目标钱数:$" + goal, 27, 280);
}
}
public void BackgroundPaint(Graphics g) {
g.drawImage(bg, 0, 0, null);
switch (Game.start) {
case 0:
drawText(g, 110, new Color(0,0,0), "按空格键以开始游戏", 200, 400);
break;
case 1:
g.drawImage(people, 380, 190, null);
drawText(g, 24, new Color(143, 112, 1), "金钱:$" + count, 30, 250);
g.drawImage(firecrackers, 580, 250, null);
drawText(g, 24, new Color(143, 112, 1), "*" + firecrackersNum, 630, 290);
// 关卡数
drawText(g, 20, new Color(143, 112, 1), "第" + level + "关", 370, 60);
// 目标钱数
drawText(g, 24, new Color(143, 112, 1), "目标钱数:$" + goal, 27, 280);
break;
case 2:
break;
case 3:
break;
case 4:
break;
default:
}
}
18.设置游戏状态
游戏的最基本的三种状态:游戏开始前、游戏中、游戏结束,游戏结束又分为以失败结束和以通关结束 接下来是根据游戏的状态进行绘制组件:
先在Game类中添加一个游戏状态的变量:
static int start;// 0未开始 1游戏中 2商店 3游戏失败 4游戏成功
并修改该类中的Game()方法:
void Game() {
// 此处为不做改变的代码
addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
switch (start) {
case 0:
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
start = 1;
}
break;
case 1:
if (e.getKeyCode() == KeyEvent.VK_DOWN) {
rope.state = 1;
}
if (e.getKeyCode() == KeyEvent.VK_UP && rope.state == 3 && Background.firecrackersNum > 0) {// 当按下向上键且绳索的状态为3时
Background.firecrackersNum--;
Background.fcFlag = true;
}
break;
case 2:
break;
case 3:
break;
case 4:
break;
default:
}
}
});
while (true) {
// 此处为不做改变的代码
}
}
背景是在任何时候都会绘制,代码不需要根据start的值进行变化。
魔法水,关卡数,目标钱数都是在游戏开始才绘制,所以将Background类中的BackgroundPaint()方法修改为以下代码:
public void BackgroundPaint(Graphics g) {
g.drawImage(bg, 0, 0, null);
switch (Game.start) {
case 0:
drawText(g, 30, new Color(252, 25, 68), "按空格键以开始游戏", 280, 120);
break;
case 1:
g.drawImage(people, 380, 190, null);
drawText(g, 24, new Color(143, 112, 1), "金钱:$" + count, 30, 250);
g.drawImage(firecrackers, 580, 250, null);
drawText(g, 24, new Color(143, 112, 1), "*" + firecrackersNum, 630, 290);
// 关卡数
drawText(g, 20, new Color(143, 112, 1), "第" + level + "关", 370, 60);
// 目标钱数
drawText(g, 24, new Color(143, 112, 1), "目标钱数:$" + goal, 27, 280);
break;
case 2:
break;
case 3:
break;
case 4:
break;
default:
}
}

19.倒计时效果:
在Background类中创建两个变量:
// 游戏开始时间
long startTime = 40;
// 游戏结束时间
long endTime;
并在case 1:中添加以下代码:
// 倒计时
endTime = System.currentTimeMillis();
long t = 40 - (endTime - startTime) / 1000;
drawText(g, 24, new Color(143, 112, 1), "时间:" + t, 30, 220);
返回Game类中Game()方法中的case 0 :初始化startTime:
void Game() {
// 此处为不做改变的代码
addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
switch (start) {
case 0:
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
start = 1;
bg.startTime = System.currentTimeMillis();// 游戏开始时顺便给起始时间赋值
}
break;
case 1:
// 此处为不做改变的代码
break;
case 2:
break;
case 3:
break;
case 4:
break;
default:
}
}
});
while (true) {
// 此处为不做改变的代码
}
}
运行效果:

20.失败状态:
先在Background类中创建一个gameTime()方法,以便判断游戏开始后,游戏时间是否大于所设时间,如果是,则代表已经超时了,游戏结束,反之游戏还有剩余时间。
/**
* 倒计时
*
* @return true代表倒计时完成,false表示正在倒计时
*/
boolean gameTime() {
long tim = (endTime - startTime) / 1000;
if (tim > 40) {
return true;
}
return false;
}
在Game类中的nextLevel()方法中修改通关成功的条件:
private void nextLevel() {
if (start == 1 && bg.gameTime()) {// 通过成功就进入下一关
if (Background.count >= bg.goal) {
Background.level++;
dispose();
Game game1 = new Game();
game1.Game();
} else {
start = 3;// 不成功则进入失败状态
}
}
}
返回 Background类中的BackgroundPaint()方法的case 3:添加以下代码:
case 3:
drawText(g, 80, new Color(252, 25, 68), "游戏失败,再接再厉!", 280, 120);// 显示失败信息
drawText(g, 50, new Color(12, 207, 103), "所得金钱:$" + count, 305, 150);// 显示所得钱数
break;
运行效果:

21.成功状态:
修改Game类中的nextLevel()方法:
private void nextLevel() {
if (start == 1 && bg.gameTime()) {// 通过成功就进入下一关
if (Background.count >= bg.goal) {
if (Background.level == 5) {
start = 4;
} else {
Background.level++;
}
dispose();
Game game1 = new Game();
game1.Game();
} else {
start = 3;// 不成功则进入失败状态
}
}
}
返回 Background类中的BackgroundPaint()方法的case 4:添加以下代码:
case 4:
drawText(g, 80, new Color(120, 157, 210), "恭喜你,成功过关!", 280, 120);// 显示失败信息
drawText(g, 50, new Color(174, 164, 162), "所得金钱:$" + count, 305, 150);// 显示所得钱数
break;
运行效果:

?22.重置游戏
在Rope类中添加Reset()方法:
/**
* 重置绳索
*/
void Reset() {
angle = 0;
length = 130;
}
在Background类中添加以下方法:
/**
* 重置游戏
*/
void Reset() {
// 总钱数
count = 0;
// 爆竹的数量
firecrackersNum = 3;
// 爆竹的状态
fcFlag = false;
// 关卡数
level = 1;
// 目标钱数
goal = level * 100;
}
金块石头的重置实在Game类中的nextLevel中,同时,在进入下一关的时候需要重置时间:
private void nextLevel() {
if (start == 1 && bg.gameTime()) {// 通过成功就进入下一关
if (Background.count >= bg.goal) {
if (Background.level == 5) {
start = 4;
} else {
Background.level++;
bg.startTime = System.currentTimeMillis();// 重置时间
}
} else {
start = 3;// 不成功则进入失败状态
}
dispose();
Game game1 = new Game();
game1.Game();
}
}
接下来在Game类中Game()方法的case 4:添加以下代码:
void Game() {
// 此处为不做改变的代码
addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
switch (start) {
case 0:
// 此处为不做改变的代码
break;
case 1:
// 此处为不做改变的代码
break;
case 2:
break;
case 3:
break;
case 4:
if (e.getKeyCode()==KeyEvent.VK_SPACE){
start=0;
bg.Reset();
rope.Reset();
}
break;
default:
}
}
});
while (true) {
// 此处为不做改变的代码
}
}
运行效果:

23.游戏商店:
?修改Background类:
public class Background {
// 总钱数
static int count = 0;
// 爆竹的数量
static int firecrackersNum = 3;
// 爆竹的状态
// 关卡数
// 目标钱数
// 游戏开始时间
// 游戏结束时间
// 魔法水价格
int price = 150;
// 是否进入商店
boolean enter = false;
// 背景图片初始化
public static void drawText(Graphics g, int size, Color color, String s, int x, int y) {
// 此处为不做改变的代码
}
public void BackgroundPaint(Graphics g) {
g.drawImage(bg, 0, 0, null);
switch (Game.start) {
case 0:
// 此处为不做改变的代码
case 1:
// 此处为不做改变的代码
case 2:
g.drawImage(firecrackers, 220, 70, null);
drawText(g, 50, new Color(252, 25, 68), "价格:" + price, 280, 100);
if (count < 150) {
drawText(g, 50, new Color(174, 164, 162), "余额不足", 260, 165);
} else {
drawText(g, 50, new Color(12, 207, 103), "是否购买?", 280, 130);
drawText(g, 50, new Color(174, 164, 162), "是:空格键 否:Esc键", 260, 165);
}
if (enter) {
count = count - price;
firecrackersNum++;
enter = false;
Game.start = 1;
startTime = System.currentTimeMillis();
}
break;
case 3:
// 此处为不做改变的代码
case 4:
// 此处为不做改变的代码
default:
}
}
/**
* 倒计时
*
* @return true代表倒计时完成,false表示正在倒计时
*/
boolean gameTime() {
// 此处为不做改变的代码
}
/**
* 重置游戏
*/
void Reset() {
// 此处为不做改变的代码
}
}
在Game类中的Game()方法添加键盘事件:
void Game() {
// 此处为不做改变的代码
addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
switch (start) {
case 0:
// 此处为不做改变的代码
case 1:
// 此处为不做改变的代码
case 2:
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
bg.enter = true;
}
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
start = 1;
bg.startTime = System.currentTimeMillis();
}
break;
case 3:
case 4:
// 此处为不做改变的代码
default:
}
}
});
while (true) {
// 此处为不做改变的代码
}
}
?运行效果:

至此,此小游戏完美结束。有脑洞的同学可自行添加功能。
?我是码龙,如果我的文章对你有帮助,请点个?赞👍🏻 支持我一下,谢谢~
|