Java小玩意:黄金矿工

2022-09-22 11:50:14 浏览数 (1)

Java小玩意:黄金矿工

这个小项目没有什么技术难点,但是还是比较适合像我一样的小白来练手用。

一、 先看效果

二、分析

这个小项目主要有6大Java类:主类(GameWin)、背景类(Bg)、“线”类(Line)、金块类(Gold)、石头类(Rock)以及金块和石头类的父类(Object)。

其中,金块类(Gold)中又分:普通金块(Gold)、小金块(GoldMini)、大金块(GoldPlus)。由于三种金块的行为和逻辑一样,所以“大金块”和“小金块”都继承自“普通金块”。

三、实现

1. 背景类(Bg.java)

代码语言:javascript复制
import java.awt.*;

public class Bg {
    // 关卡数
    static int level = 1;
    // 目标得分
    int goal = (level * 500)   550;
    // 总分
    static int count = 0;
    // 药水数量
    static int waterNum = 3;
    // 药水状态
    static boolean waterFlag = false;
    // 开始时间
    long startTime;
    // 结束时间
    long endTime;
    // 药水价格
    int price = (int) (Math.random() * 10);
    // 是否进入商店 f不购买
    boolean shop = false;

    // 载入图片
    Image bg = Toolkit.getDefaultToolkit().getImage("imgs/bg.jpg");
    Image bg1 = Toolkit.getDefaultToolkit().getImage("imgs/bg1.jpg");
    Image peo = Toolkit.getDefaultToolkit().getImage("imgs/peo.png");
    Image water = Toolkit.getDefaultToolkit().getImage("imgs/water.png");

    // 绘制
    void paintSelf(Graphics g) {
        g.drawImage(bg, 0, 200, null);
        g.drawImage(bg1, 0, 0, null);
        switch (GameWin.state) {
            case 0:
                drawWord(g, 80, Color.black, "按鼠标右键开始", 100, 400);
                drawWord(g, 60, Color.red, "游戏规则:", 100, 500);
                drawWord(g, 30, Color.black, "点击鼠标左键放出抓钩", 100, 560);
                drawWord(g, 30, Color.black, "抓住金块或石头可以点击鼠标右键", 100, 620);
                drawWord(g, 30, Color.black, "使用“大力神药”快速收回金块或者爆破石头", 100, 680);
                break;
            case 1:
                g.drawImage(peo, 310, 50, null);
                drawWord(g, 30, Color.black, "分数:"   count, 30, 150);
                g.drawImage(water, 550, 40, null);
                drawWord(g, 30, Color.black, "×"   waterNum, 610, 90);
                // 关卡数&目标得分
                drawWord(g, 20, Color.black, "关卡:"   level, 30, 60);
                drawWord(g, 30, Color.black, "目标:"   goal, 30, 115);
                // 实时赋值
                endTime = System.currentTimeMillis();
                long time = 20 - (endTime - startTime) / 1000;
                drawWord(g, 30, Color.black, "时间:"   (time > 0 ? time : 0), 520, 150);
                break;
            case 2:
                g.drawImage(water, 330, 380, null);
                drawWord(g, 30, Color.black, "价格:"   price, 300, 500);
                drawWord(g, 30, Color.black, "是否购买“大力神药”?", 280, 550);
                drawWord(g, 30, Color.black, "点击鼠标左键确认购买,右键退出商店", 100, 700);
                
                if (shop) {
                    count = count - price;
                    waterNum  ;
                    shop = false;
                    GameWin.state = 1;
                    startTime = System.currentTimeMillis();
                };
                break;
            case 3:
                drawWord(g, 80, Color.cyan, "失败", 300, 350);
                drawWord(g, 80, Color.black, "分数:"   count, 200, 450);
                drawWord(g, 60, Color.black, "点击鼠标左键重新开始", 90, 550);
                break;
            case 4:
                drawWord(g, 80, Color.red, "恭喜过关", 250, 350);
                drawWord(g, 80, Color.black, "分数:"   count, 200, 450);
                drawWord(g, 60, Color.black, "点击鼠标左键重新开始", 90, 550);
                break;
            default:
        };
    };

    // t倒计时完成,f未完成倒计时
    boolean gameTime() {
        long time = (endTime - startTime) / 1000;
        if (time >= 20) {
            return true;
        };
        return false;
    };

    // 重置元素
    void reGame() {
        // 关卡数
        level = 1;
        // 目标得分
        goal = (level * 500)   550;
        // 总分
        count = 0;
        // 药水数量
        waterNum = 3;
        // 药水状态
        waterFlag = false;
    };

    // 绘制字符串
    public static void drawWord(Graphics g, int size, Color color, String str, int x, int y) {
        g.setColor(color);
        g.setFont(new Font("宋体", Font.BOLD, size));
        g.drawString(str, x, y);
    };
};

“背景类”中处理所有显示在窗口上的内容,包括背景、文字等信息

但是有一点需要注意:

代码语言:javascript复制
 // 载入图片
    Image bg = Toolkit.getDefaultToolkit().getImage("imgs/bg.jpg");
    Image bg1 = Toolkit.getDefaultToolkit().getImage("imgs/bg1.jpg");
    Image peo = Toolkit.getDefaultToolkit().getImage("imgs/peo.png");
    Image water = Toolkit.getDefaultToolkit().getImage("imgs/water.png");

使用这种方法导入图片,在导出 jar包时,并不能将图片资源一起打包,如果运行jar包,需要将图片等资源放在和代码文件相同的相对路径下

2. “线”(Line.java)

这个“线”就是游戏画面中左右摇摆的红色线。

代码语言:javascript复制
import java.awt.*;

public class Line {
    // 起点坐标
    int x = 380;
    int y = 180;
    // 终点坐标
    int endx = 500;
    int endy = 500;
    // 线段长度
    double length = 100;
    double MIN_length = 100;
    double MAX_length = 750;
    double n = 0;
    // 方向
    int dir = 1;
    // 状态: 0摇摆,1抓取,2回收,3抓取返回
    int state = 0;
    // 钩爪
    Image hook = Toolkit.getDefaultToolkit().getImage("imgs/hook.png");

    GameWin frame;

    Line(GameWin frame) {
        this.frame = frame;
    };

    // 判断是否抓取成功
    void logic() {
        for (Object obj : this.frame.objectList) {
            if (endx > obj.x && endx < obj.x   obj.width && endy > obj.y && endy < obj.y   obj.height) {
                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.red);
        g.drawLine(x - 1, y - 1, endx - 1, endy);
        g.drawLine(x, y, endx, endy);
        g.drawLine(x   1, y   1, endx   1, endy);
        g.drawImage(hook, endx - 36, endy - 2, null);
    };

    void paintSelf(Graphics g) {
        logic();
        switch (state) {
            case 0:
                if (n < 0.1) {
                    dir = 1;
                }; else if (n > 0.9) {
                    dir = -1;
                };
                n = n   0.02 * dir;
                lines(g);
                break;
            case 1:
                if (length < MAX_length) {
                    length = length   50;
                    lines(g);
                }; else {
                    state = 2;
                };
                break;
            case 2:
                if (length > MIN_length) {
                    length = length - 50;
                    lines(g);
                }; else {
                    state = 0;
                };
                break;
            case 3:
                int m = 1;
                if (length > MIN_length) {
                    length = length - 20;
                    lines(g);
                    for (Object obj : this.frame.objectList) {
                        if (obj.flag) {
                            m = obj.m;
                            obj.x = endx - obj.getWidth() / 2;
                            obj.y = endy;
                            if (length <= MIN_length) {
                                obj.x = -150;
                                obj.y = -150;
                                obj.flag = false;
                                Bg.waterFlag = false;
                                // 加分
                                Bg.count  = obj.count;
                                state = 0;
                            };
                            if (Bg.waterFlag) {
                                if (obj.type == 1) {
                                    m = 1;
                                };
                                if (obj.type == 2) {
                                    obj.x = -150;
                                    obj.y = -150;
                                    obj.flag = false;
                                    Bg.waterFlag = false;
                                    state = 2;
                                };
                            };
                        };
                    };
                };
                try {
                    Thread.sleep(m);
                }; catch (InterruptedException e) {
                    e.printStackTrace();
                };
                break;
            default:
        };
    };

    // 重置线
    void reGame() {
        n = 0;
        length = 100;
    };
};

3. ”块“的父类(Object.java)

由于”金块“和”石块“在处理的逻辑和其具有的属性基本都是相同的,所以使他们继承自相同的父类,简化代码。

代码语言:javascript复制
import java.awt.*;

public class Object {
    // 坐标
    int x;
    int y;
    // 宽高
    int width;
    int height;
    // 图片
    Image img;
    // 标记 是否能移动
    boolean flag = false;
    // 质量
    int m;
    // 积分
    int count;
    // 类型 1 金块 2 石块 3
    int type;

    void paintSelf(Graphics g) {
        g.drawImage(img, x, y, null);
    };

    public int getWidth() {
        return width;
    };

    // 获取矩形
    public Rectangle getRec() {
        return new Rectangle(x, y, width, height);
    };
};

其中 getRec()方法在主类中会用到,其作用是解决各种块的重叠问题

m为质量,要区别不同的块的拉取速度,所以引入质量这个属性

4. 石块(Rock.java)

代码语言:javascript复制
import java.awt.*;

public class Rock extends Object {
    Rock() {
        this.x = (int) (Math.random() * 700);
        this.y = (int) (Math.random() * 550   300);
        this.width = 71;
        this.height = 71;
        this.flag = false;
        this.m = 100;
        this.count = 15;
        this.type = 2;
        this.img = Toolkit.getDefaultToolkit().getImage("imgs/rock1.png");
    };
};

5. 金块(Gold.java)

代码语言:javascript复制
import java.awt.*;

public class Gold extends Object {

    Gold() {
        this.x = (int) (Math.random() * 700);
        this.y = (int) (Math.random() * 550   300);
        this.width = 52;
        this.height = 52;
        this.flag = false;
        this.m = 30;
        this.count = 250;
        this.type = 1;
        this.img = Toolkit.getDefaultToolkit().getImage("imgs/gold1.gif");
    };
};

class GoldMini extends Gold {
    GoldMini() {
        this.width = 36;
        this.height = 36;
        this.m = 30;
        this.count = 150;
        this.img = Toolkit.getDefaultToolkit().getImage("imgs/gold0.gif");
    };
};

class GoldPlus extends Gold {
    GoldPlus() {
        this.x = (int) (Math.random() * 650);
        this.width = 105;
        this.height = 105;
        this.m = 100;
        this.count = 500;
        this.img = Toolkit.getDefaultToolkit().getImage("imgs/gold2.gif");
    };
};

6. 主类(GameWin.java)

代码语言:javascript复制
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;

public class GameWin extends JFrame {
    // 0未开始,1运行中,2商店,3失败,4胜利
    static int state = 0;
    // 存储金块、石块
    List<Object> objectList = new ArrayList<>();

    Bg bg = new Bg();

    Line line = new Line(this);

    {
        // 是否可以放置
        boolean isPlace = true;
        for (int i = 0; i < 11; i  ) {
            double random = Math.random();
            Gold gold; // 存放当前生成的金块
            if (random < 0.3) {
                gold = new GoldMini();
            }; else if (random < 0.7) {
                gold = new Gold();
            }; else {
                gold = new GoldPlus();
            };
            for (Object obj : objectList) {
                if (gold.getRec().intersects(obj.getRec())) {
                    // 不可放置
                    isPlace = false;
                };
            };
            if (isPlace) {
                objectList.add(gold);
            }; else {
                isPlace = true;
                i--;
            };
        };
        for (int i = 0; i < 5; i  ) {
            Rock rock = new Rock();
            for (Object obj : objectList) {
                if (rock.getRec().intersects(obj.getRec())) {
                    isPlace = false;
                };
            };
            if (isPlace) {
                objectList.add(rock);
            }; else {
                isPlace = true;
                i--;
            };
        };
    };

    Image offScreenImage;
    void launch() {
        this.setVisible(true);
        this.setSize(768, 1000);
        this.setLocationRelativeTo(null);
        this.setTitle("黄金矿工");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                super.mouseClicked(e);
                switch (state) {
                    case 0:
                        if (e.getButton() == 3) {
                            state = 1;
                            bg.startTime = System.currentTimeMillis();
                        };
                        break;
                    case 1:
                        if (e.getButton() == 1 && line.state == 0) {
                            line.state = 1;
                        };
                        if (e.getButton() == 3 && line.state == 3 && Bg.waterNum > 0) {
                            Bg.waterFlag = true;
                            Bg.waterNum--;
                        };
                        break;
                    case 2:
                        if (e.getButton() == 1) {
                            bg.shop = true;

                        };
                        if (e.getButton() == 3) {
                            state = 1;
                            bg.startTime = System.currentTimeMillis();
                        };
                        break;
                    case 3:
                    case 4:
                        if (e.getButton() == 1) {
                            state = 0;
                            bg.reGame();
                            line.reGame();
                        };
                        break;
                    default:
                };
            };
        };);

        // 红线摇摆
        while (true) {
            repaint();
            nextLevel();
            try {
                Thread.sleep(50);
            }; catch (InterruptedException e) {
                e.printStackTrace();
            };
        };
    };


    // 下一关
    public void nextLevel() {
        if (bg.gameTime() && state == 1) {
            if (Bg.count >= bg.goal) {
                if (Bg.level == 10) {
                    state = 4;
                }; else {
                    state = 2;
                    Bg.level  ;
                };
            }; else {
                state = 3;
            };
            dispose();
            GameWin gameWin1 = new GameWin();
            gameWin1.launch();
        };
    };

    @Override
    public void paint(Graphics g) {
        offScreenImage = this.createImage(768, 1000);
        Graphics gImage = offScreenImage.getGraphics();

        bg.paintSelf(gImage);
        if (state == 1) {
            // 绘制金块、石块
            for (Object obj : objectList) {
                obj.paintSelf(gImage);
            };
            // 绘制红线
            line.paintSelf(gImage);
        };
        g.drawImage(offScreenImage, 0, 0, null);
    };

    public static void main(String[] args) {
        GameWin game = new GameWin();
        game.launch();
    };
};

四、总结

源码:GoldMiner

安装包:GoldMiner-setup.exe

经过我简单测试,存在一个Bug,运行时有几率出现”红线和抓钩“消失的情况,需要刷新窗口才可正常运行,由于这段时间要准备期末考试,所以也就没有时间再去深究这个。但是我猜测可能的原因是因为:在 Line.java类中,state属性没有初始化的原因,但是 state是int型,创建对象时会默认初始化为0,不知道是不是这个原因。

最后,再次重申一遍,这个小项目没有什么技术难点,用到的方法也基本都是一些基础的东西,适合像我一样的小白练手用,大家也不要太过纠结方法实现的”好坏“,毕竟,我也还是一个小白。

当然,我还是欢迎一切积极、善意的讨论与指正 ^_^

0 人点赞