首页 > Python资料 博客日记

【掘金高手:谁能拒绝一只可爱的乌萨奇的矿工之旅游戏(上)】

2025-01-06 03:00:05Python资料围观10

这篇文章介绍了【掘金高手:谁能拒绝一只可爱的乌萨奇的矿工之旅游戏(上)】,分享给大家做个参考,收藏Python资料网收获更多编程知识


🌈个人主页: Aileen_0v0
🔥热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法
💫个人格言:“没有罗马,那就自己创造罗马~”

前言

本文主要是根据我小时候玩的一款游戏——黄金矿工,来进行创新的游戏项目编写过程,内容较多,所以分为多篇。

JAVA项目——Usaqi矿工创新版

窗口绘制

package com.sxt;

import javax.swing.*;

public class GameWin extends JFrame {
    void launch(){
        this.setVisible(true);
        this.setSize(500,500);
        this.setLocationRelativeTo(null);
        this.setTitle("AileenGoldMiner");
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }

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


绘制图片

package com.sxt;


import javax.swing.*;
import java.awt.*;


public class GameWin extends JFrame {


   Bg bg = new Bg();
    void launch(){
        this.setVisible(true);
        this.setSize(768,1000);
        this.setLocationRelativeTo(null);
        this.setTitle("AileenGoldMiner");
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }

    @Override
    public void paint(Graphics g){
        bg.paintSelf(g);
    }

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

package com.sxt;

import java.awt.*;

public class Bg {
    Image bg = Toolkit.getDefaultToolkit().getImage("C://Users//admin//IdeaProjects//The Gold Miner//imgs//bg.jpg");
    Image bg1 = Toolkit.getDefaultToolkit().getImage("C://Users//admin//IdeaProjects//The Gold Miner//imgs//bg1.jpg");
    Image usaqi = Toolkit.getDefaultToolkit().getImage("C://Users//admin//IdeaProjects//The Gold Miner//imgs//usaqi.gif");

    void paintSelf(Graphics g){
        g.drawImage(bg1,0,0,null);
        g.drawImage(bg,0,200,null);
        g.drawImage(usaqi,310,50,null);
    }
}


红线绘制

package com.sxt;

import java.awt.*;

public class Line {
    //起点坐标
    int x = 380;
    int y = 180;

    //终点坐标
    int endx = 500;
    int endy = 500;

    void paintSelf(Graphics g){
        g.setColor(Color.red);
        g.drawLine(x,y,endx,endy);
    }
}

package com.sxt;
import javax.swing.*;
import java.awt.*;

public class GameWin extends JFrame {
   Bg bg = new Bg();
   Line line = new Line();
    void launch(){
        this.setVisible(true);
        this.setSize(768,1000);
        this.setLocationRelativeTo(null);
        this.setTitle("AileenGoldMiner");
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }

    @Override
    public void paint(Graphics g){
        bg.paintSelf(g);
        line.paintSelf(g);
    }

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


模拟分析红线摇摆情况

  • 根据绘图我们可以知道,线长不变,起点坐标不变,终点坐标与角度偏移量有关,它可以通过已知条件计算得出,如上图所示,终点坐标表达式为:

endx = x + length ⋅ cos ⁡ ( α ) endy = y + length ⋅ sin ⁡ ( α ) \begin{align*} \text{endx} &= x + \text{length} \cdot \cos(\alpha) \\ \text{endy} &= y + \text{length} \cdot \sin(\alpha) \end{align*} endxendy=x+lengthcos(α)=y+lengthsin(α)

  • 代码实现:

  • 当我们的角度值为1时,红线往右偏;角度值为2时,红线往左偏。这是因为在计算机中,x轴正方向向右,y轴正方向向下,角度起点是x轴正方向,角度顺时针旋转,旋转到y轴的时候是直角,角度制当中是90度,在弧度制中是π/2(约为1.57),当jiaodu为1时,小于1.57,所以在y轴右边,当角度为2时大于1.57,所以在y轴的左边。

  • 为了让红线动起来,我们可以根据游戏使用界面知道这个角度的变化是从0到π变化的,我们可以设置一个取值范围在(0-1)的系数n,这样n*π的范围就是(0-π),所以我们之前设置的角度可以换为(0-π),也就是说,我们只需要操作n就可以实现角度的变化。
  • 现在,想要让红线动起来,只需要不断改变n的值即可,这样旋转角度就会发生变化

  • 先让n+0.005看看效果,我们可以看到线有错位,但是并未转动,这是因为图片只绘制了一次,我们需要实现不停地绘制,可以通过死循环,让它重复绘制。

  • 现在这条红色就会移动了,但是移动速度比较快,可以通过调用内置的sleep方法来限制红线的绘制速度;此外摆动的区域也要尽量控制在地面下方,可以通过控制n来控制摆动的角度,通过变量dir的正负来控制其摆动方向。
  • 代码如下:
package com.sxt;

import java.awt.*;

public class Line {
    //起点坐标
    int x = 380;
    int y = 180;

    //终点坐标
    int endx = 500;
    int endy = 500;

    //线长
    double length = 100;

    //为了使其从x轴右边开始转动,所以n设为0
    double n = 0;

    //红线摆动方向限定 -> 1表示角度增加往左边移动,-1表示角度减小往右边移动
    int dir = 1;

    void paintSelf(Graphics g){
        //红线摆动范围限定 ->根据之前限定可知n的范围是(0-1),
        // 所以n最小我们可以设为0.1,最大可设为0.9.
        if(n<0.01){
            dir = 1;
        } else if (n > 0.9) {
            dir = -1;
        }

        n = n + 0.005*dir;

        endx = (int) (x + length*Math.cos(n*Math.PI)); //因为角度是double类型所以要强制转换成int类型
        endy = (int) (y + length*Math.sin(n*Math.PI));

        g.setColor(Color.red);
        g.drawLine(x,y,endx,endy);
    }
}

package com.sxt;
import javax.swing.*;
import java.awt.*;

public class GameWin extends JFrame {
   Bg bg = new Bg();
   Line line = new Line();
    void launch(){
        this.setVisible(true);
        this.setSize(768,1000);
        this.setLocationRelativeTo(null);
        this.setTitle("AileenGoldMiner");
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        while(true){
            repaint();
            //限制摆动速度
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    @Override
    public void paint(Graphics g){
        bg.paintSelf(g);
        line.paintSelf(g);
    }

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


红线抓取

  • 我们可以设置一个状态参数,通过控制这个状态参数来实现状态的改变。(状态:0表示摇摆,1表示抓取,2表示收回)


  • 以下是完整的红线的绘制及操作的逻辑代码
package com.sxt;

import java.awt.*;

public class Line {
    //起点坐标
    int x = 380;
    int y = 180;

    //终点坐标
    int endx = 500;
    int endy = 500;

    //线长
    double length = 100;

    //为了使其从x轴右边开始转动,所以n设为0
    double n = 0;

    //红线摆动方向限定 -> 1表示角度增加往左边移动,-1表示角度减小往右边移动
    int dir = 1;

    //状态: 0->摇摆  1->抓取  2->收回
    int state;

    void paintSelf(Graphics g){
        //线的状态设置
        switch(state){
            case 0://左右摇摆
                if (n < 0.1) {dir = 1;}
                else if (n > 0.9){dir = -1;}
                n = n + 0.005*dir;
                endx = (int) (x + length*Math.cos(n*Math.PI)); //因为角度是double类型所以要强制转换成int类型
                endy = (int) (y + length*Math.sin(n*Math.PI));
                g.setColor(Color.red);
                g.drawLine(x,y,endx,endy);
                break;
            case 1://延长线长度
                if(length < 500){//如果线长小于500就可以延长
                    length = length + 10;
                    endx = (int) (x + length*Math.cos(n*Math.PI)); //因为角度是double类型所以要强制转换成int类型
                    endy = (int) (y + length*Math.sin(n*Math.PI));
                    g.setColor(Color.red);
                    g.drawLine(x,y,endx,endy);
                } else {state = 2 ;}//延长后无其他操作,可以将其状态置为0,让它继续执行左右摇摆以及红线的绘制
                break;
            case 2://红线收回
                if(length > 100){
                    length = length - 10;
                    endx = (int) (x + length*Math.cos(n*Math.PI)); //因为角度是double类型所以要强制转换成int类型
                    endy = (int) (y + length*Math.sin(n*Math.PI));
                    g.setColor(Color.red);
                    g.drawLine(x,y,endx,endy);
                }else{
                    state = 0;
                }
        }

    }
}

  • 为了减少代码的冗余量,我们将其操作的共性全部抽取到一个新的函数中
    void lines(Graphics g){
        endx = (int) (x + length*Math.cos(n*Math.PI)); //因为角度是double类型所以要强制转换成int类型
        endy = (int) (y + length*Math.sin(n*Math.PI));
        g.setColor(Color.red);
        g.drawLine(x,y,endx,endy);
    }
    void lines(Graphics g){
        endx = (int) (x + length*Math.cos(n*Math.PI)); //因为角度是double类型所以要强制转换成int类型
        endy = (int) (y + length*Math.sin(n*Math.PI));
        g.setColor(Color.red);
        g.drawLine(x,y,endx,endy);
    }

    void paintSelf(Graphics g){
        //线的状态设置
        switch(state){
            case 0://左右摇摆
                if (n < 0.1) {dir = 1;}
                else if (n > 0.9){dir = -1;}
                n = n + 0.005*dir;
                lines(g);
                break;
            case 1://延长线长度
                if(length < 500){//如果线长小于500就可以延长
                    length = length + 10;
                    lines(g);
                } else {state = 2 ;}//延长后无其他操作,可以将其状态置为0,让它继续执行左右摇摆以及红线的绘制
                break;
            case 2://红线收回
                if(length > 100){
                    length = length - 10;
                    lines(g);
                }else{
                    state = 0;
                }
        }

运行效果


创建金块

  • 由于金块和石块有很多共同的属性,所以我们可以先创建一个Object类作为他们的父类

实现代码

package com.sxt;

import java.awt.*;

/**
 * 石块和金块的父类,用于抽取共性
 */

public class Object {
    //坐标
    int x;
    int y;

    //宽高
    int width;
    int height;

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

package com.sxt;

import java.awt.*;

public class Gold extends Object{
    Gold(){
        this.x = 300;
        this.y = 500;
        this.width = 32;
        this.height = 52;
        this.img  = Toolkit.getDefaultToolkit().getImage("C://Users//admin//IdeaProjects//The Gold Miner//imgs//gold1.gif");
    }
}

package com.sxt;


import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;


public class GameWin extends JFrame {


   Bg bg = new Bg();
   Line line = new Line();
   Gold gold = new Gold();
    void launch(){
        this.setVisible(true);
        this.setSize(768,1000);
        this.setLocationRelativeTo(null);
        this.setTitle("AileenGoldMiner");
        this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                super.mouseClicked(e);
                if(e.getButton() == 1){
                    line.state=1;
                }
            }
        });


        while(true){
            repaint();

            //限制摆动速度
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    @Override
    public void paint(Graphics g){
        bg.paintSelf(g);
        line.paintSelf(g);
        gold.paintSelf(g);
    }

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

双缓存技术解决物体闪动问题

  • 根据我们的运行结果,我们可以看到存在物体闪动问题,现在让我们先分析出现这个现象的原因。

  • 根据这段代码,我们可以知道我们的绘制顺序是:背景->人物->金块;所以我们需要将它们依次画入到一个画布中,变成一个整体一起传入窗体中,就不会出现闪动问题。

package com.sxt;


import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;


public class GameWin extends JFrame {


   Bg bg = new Bg();
   Line line = new Line();
   Gold gold = new Gold();

   //定义一个画布
    Image offScreenImage;

    void launch(){
        this.setVisible(true);
        this.setSize(768,1000);
        this.setLocationRelativeTo(null);
        this.setTitle("AileenGoldMiner");
        this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                super.mouseClicked(e);
                if(e.getButton() == 1){
                    line.state=1;
                }
            }
        });


        while(true){
            repaint();
            //限制摆动速度
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    @Override
    public void paint(Graphics g){
        //定义画布大小,使其与窗口大小一致
        offScreenImage = this.createImage(768,1000);
        Graphics gImage = offScreenImage.getGraphics();
        //将背景,人物,金块依次画入到画布中
        bg.paintSelf(gImage);
        line.paintSelf(gImage);
        gold.paintSelf(gImage);
        //将画布导入到窗体中
        g.drawImage(offScreenImage,0,0,null);
    }

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


现在我们的537,就不会闪动啦~


抓取判定

  • 其实金块在程序中实际上是一个矩形,红线末尾是一个点,抓取判定(碰撞检测)实质上就是检测点是否在矩形中。

    • 横坐标的范围是x加上宽度
    • 纵坐标的范围是y加上高度
    • 相当于判断这个红线的endxendy是否在这个区域当中。
  • 根据上面的分析,为了检查我们的检测是否成功,我们可以假设当红线进入到金块的范围中时,程序就会打印出1,代码如下:

  • 实现代码:

    //判断红线的终点是否在金块矩形的范围内
    void logic(){
        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(1);
        }
    }


抓取返回

        //线的状态设置
        switch(state){
            case 0://左右摇摆
                if (n < 0.1) {dir = 1;}
                else if (n > 0.9){dir = -1;}
                n = n + 0.005*dir;
                lines(g);
                break;
            case 1://延长线长度
                if(length < 500){//如果线长小于500就可以延长
                    length = length + 10;
                    lines(g);
                } else {state = 2 ;}//延长后无其他操作,可以将其状态置为0,让它继续执行左右摇摆以及红线的绘制
                break;
            case 2://红线收回
                if(length > 100){
                    length = length - 10;
                    lines(g);
                }else{
                    state = 0;
                }
                break;
            case 3://红线碰到晶块,红线返回的情况
                if(length > 100){
                    length = length - 10;
                    lines(g);
                    //金块偏移
                    this.frame.gold.x = endx - 26;
                    this.frame.gold.y = endy;
                }else {
                    //金块移除 - >将金块移动到屏幕外
                    this.frame.gold.x = -150;
                    this.frame.gold.y = -150;
                    state = 0;
                }
                break;
        }



版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!

标签:

相关文章

本站推荐