IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> Java游戏项目——仿4399小游戏《黄金矿工》 -> 正文阅读

[游戏开发]Java游戏项目——仿4399小游戏《黄金矿工》

简介

作者简介:青铜码农,和大多数同学一样从零开始一步步学习,一步步积累。期待您的关注,让我们一起成长~注:本人学疏才浅,文章如有错误之处,敬请指正~

内容简介:实现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) {
        // 此处为不做改变的代码
        }
    }

?运行效果:

至此,此小游戏完美结束。有脑洞的同学可自行添加功能。

?我是码龙,如果我的文章对你有帮助,请点个?👍🏻 支持我一下,谢谢~

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-08-12 17:00:38  更:2021-08-12 17:01:29 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/1 13:41:15-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码