Java Swing雨中雨伞模拟(计算机图形学)

一、背景(计算机图形学)

        在计算机诞生后,如何在计算机中有效地表达、处理以及显示三维信息,很快变成了计算机应用研究中的一个重要问题。针对这一需求,计算机图形学在二十世纪六十年代应运而生。在过去的几十年中,计算机图形学得到了长足的发展,并深深地影响了很多产业的发展和人们的生活、工作和娱乐方式。在硬件上,图形学的发展催生了专用图形处理器GPU(graphics processing unit)的产生与普及。在软件上,图形学的基本绘制流水线已成为操作系统的一部分,为各种计算机平台提供显示和图形处理。应用上,图形学催生了影视特效、三维动画影片、数据可视化、计算机游戏、虚拟现实、计算机辅助设计和制造等一系列产业,并为这些产业的发展提供了核心技术和算法支持。

二、算法设计

2.1  雨滴下落状态获取及设置

创建类Raindrop,在类Raindrop的构造函数中它接受三个参数:x、y 和速度,分别代表雨滴的初始横坐标、纵坐标和下落速度。在构造函数中,我们将这些值都保存到对象的属性当中,并将falling标志为true,表示雨滴一开始是在下落状态的。

public Raindrop(int x, int y, int speed){

    this.x = x;

    this.y = y;

    this.speed = speed;

    falling = true;

}

下面是雨滴下落的方法fall(),每次调用时,将雨滴的纵坐标y加上下落速度speed,模拟雨滴下落的效果。

public void fall(){

    y += speed;

}

雨滴的位置判断方法isInside(),接受一个 Arc2D 圆弧对象作为参数,判断当前雨滴是否在该圆弧内部。这个方法使用 contains() 函数来进行判断,同时加上 5 的偏移量,以圆心为判断点。

public boolean isInside(Arc2D arc) {

    return arc.contains(x + 5, y + 5);

}

其中isFalling()和setFalling()方法,分别用于获取和设置falling属性的值。这个属性标识了当前雨滴是否在下落状态。

public boolean isFalling(){

    return falling;

}

public void setFalling(boolean falling) {

    this.falling = falling;

}

getX()和getY()方法返回当前雨滴的横坐标和纵坐标的值,用于在窗口中绘制雨滴。

2.2  雨滴生成及控制

创建RainGenerator类,在RainGenerator类我们有以下属性:

raindrops表示当前已有的所有雨滴,用ArrayList存储。

frameWidth和frameHeight表示当前窗口的宽度和高度,用于控制雨滴的位置。

构造函数RainGenerator()接受三个参数raindrops、frameWidth和frameHeight分别初始化对应的属性。

public RainGenerator(ArrayList<Raindrop> raindrops, int frameWidth, int frameHeight) {

    this.raindrops = raindrops;

    this.frameWidth = frameWidth;

    this.frameHeight = frameHeight;

}

然后是addRaindrop()方法,这个方法会随机生成一个雨滴,并将其添加到raindrops数组中。雨滴的位置和速度都是随机生成的,同时一个雨滴可能对应多个位置,从而增加雨滴密度。随机位置和密度使得下落效果更自然。

public void addRaindrop() {

    int x = new Random().nextInt(frameWidth - 10); // 保证雨滴不会超出面板

    int y = -20; // 从上方开始落下

    int speed = new Random().nextInt(10) + 5; // 速度在 5~14 之间随机

    int density = new Random().nextInt(4) + 1; // 雨滴密度在 1~4 之间随机

    for (int i = 0; i < density; i++) {

        int x_offset = new Random().nextInt(10) - 0; // 位置随机偏移不超过 5 个像素

        int y_offset = new Random().nextInt(100) - 5;

        raindrops.add(new Raindrop(x + x_offset, y + y_offset, speed));

    }

}

最后是updateRaindrops()方法,这个方法会根据圆弧对象来判断哪些雨滴可以继续下落,哪些雨滴需要停止下落了。对于每个雨滴,如果它目前在下落状态,就让它下落一个单位,同时判断它是否落到了地面或者落到了伞上,如果是,就将这个雨滴的下落状态改为false,同时它不再对整个动画产生影响。

public void updateRaindrops(Arc2D umbrellaArc) {

    synchronized (raindrops) {

        for (Raindrop raindrop : raindrops) {

            if (raindrop.isFalling()) {

                raindrop.fall();

                if (raindrop.getY() >= frameHeight - 10) {

                    raindrop.setFalling(false);

                }

                if (raindrop.isInside(umbrellaArc)) {

                    raindrop.setFalling(false);

                }

            }

        }

    }

}

除此之外,程序还添加了一个getRaindrops()方法,用来获取当前已有的所有雨滴,方便后续的绘制和更新操作。

2.3  雨伞雨滴绘制

创建UmbrellaPanel类,在类中我们有以下属性:

rainGenerator一个RainGenerator对象,用来生成和更新雨滴。

panelWidth和panelHeight表示当前窗口的宽度和高度。

umbrellaX、umbrellaY、umbrellaWidth 和 umbrellaHeight表示雨伞的位置和大小。

构造函数UmbrellaPanel():接受四个参数umbrellas、rainGenerator、panelWidth和panelHeight,分别初始化对应的属性。在构造函数内部,我们将raindrops属性设置为rainGenerator的getRaindrops()方法返回的雨滴数组,这样就可以直接操作雨滴了。

在构造函数中,我们为JPanel添加了MouseListener和KeyListener,分别用于响应鼠标和键盘事件。当鼠标按下时,调用moveUmbrella()方法来移动雨伞的位置。当释放鼠标或者按下键盘按键后,调用updateUmbrellaStatus()方法来更新雨滴的状态。同时,还设置了窗口的首选大小和键盘焦点。moveUmbrella()方法用于移动雨伞的位置,并保证不会超出窗口范围。

private void moveUmbrella(int x, int y) {

    umbrellaX = Math.min(Math.max(x, 0), panelWidth - umbrellaWidth);

    umbrellaY = Math.min(Math.max(y, 0), panelHeight - umbrellaHeight);

    repaint();

}

paintComponent()方法用于绘制整个界面,包括背景、雨伞和雨滴。首先绘制一个背景渐变色,然后绘制雨伞和伞面,最后根据雨滴的位置绘制对应的雨滴。

getUmbrellaArc()方法返回一个 `Arc2D` 对象,该对象用于表示雨伞的弧形。这个方法被用于在updateUmbrellaStatus()方法中判断哪些雨滴有被挡住。

public Arc2D.Double getUmbrellaArc() {

    return new Arc2D.Double(umbrellaX, umbrellaY, umbrellaWidth, umbrellaHeight, 0, 180, Arc2D.OPEN);

}

updateUmbrellaStatus()方法用于更新雨滴的状态。对于每个雨滴,如果它在雨伞的范围内,就将它的下落状态标记为false,表示它不再继续下落了。同时,这个方法还调用repaint()方法来重新绘制整个界面。

public void updateUmbrellaStatus(){

    synchronized (raindrops) {

        for (Raindrop raindrop : raindrops) {

            if (raindrop.isInside(getUmbrellaArc())) {

                raindrop.setFalling(false);

            }

        }

    }

    repaint();

}

startRain()方法是程序的主要逻辑,它使用一个线程来不断往雨滴数组中添加和更新雨滴的状态。在每次更新完雨滴的状态之后,会调用updateUmbrellaStatus()方法来判断哪些雨滴被挡住了。

三、程序及功能说明

3.1  主程序功能说明

在MainFrame类中,我们有以下属性:

frameWidth和frameHeight表示窗口的宽度和高度。

umbrellaPanel一个UmbrellaPanel对象,用来显示雨中雨伞的动画。

构造函数MainFrame()接受两个参数frameWidth和frameHeight分别初始化对应的属性。在构造函数内部,我们创建了一个UmbrellaPanel对象,并将它添加到主窗口的中间位置。然后调用startRain()方法来启动雨滴的生成和更新线程。

public MainFrame(int frameWidth, int frameHeight) {

    this.frameWidth = frameWidth;

    this.frameHeight = frameHeight;

    setTitle("雨中雨伞模拟");

    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    umbrellaPanel = new UmbrellaPanel(new ArrayList<>(), new RainGenerator(new ArrayList<>(), frameWidth, frameHeight),

            frameWidth, frameHeight);

    add(umbrellaPanel, BorderLayout.CENTER);

    // 开始雨滴生成和更新线程

    umbrellaPanel.startRain();  

    pack();

    setLocationRelativeTo(null);

    setVisible(true);

}

main()方法只创建一个MainFrame对象,并传入窗口的宽度和高度。

public static void main(String[] args) {

    MainFrame frame = new MainFrame(600, 800);

}

在主程序中,直接调用主函数,生成雨滴雨伞实现雨伞遮雨功能。

四、结果分析

4.1  雨中雨伞绘制

                   图 4-1 雨滴下落模拟                                              图 4-2 背景图

        在此过程中我们采用蓝色渐变微背景,在此背景下雨滴密度不高,且在此背景下的雨滴效果并不明显更像是星空图,因此将背景转换白色更易观察,为使得雨滴在白色背景下显示更加清晰,此刻将雨滴颜色更改为黑色。

  

              图 4-3 白色背景下的雨滴                               图 4-4 白色背景的雨中雨伞

        在白色背景下的雨中雨伞跟为真实此时雨伞也为黑色,可通过键盘、鼠标等来移动雨伞模拟雨伞在雨中运动时雨滴是否能及时更新下落状态,以及而二者的同时运行是否能够顺畅。并且在改变雨滴大小后可模拟在暴雨情况下的雨中雨伞,而雨滴过大时则无法展现雨中雨伞的情形。

                  图 4-5 加大雨滴效果                                         图 4-6 雨中雨伞模拟

五、总  结

        在此次课设中,实现了雨滴、雨伞的绘制,以及背景的设置,雨滴落在雨伞上面时雨滴的变化以及雨滴的随机生成,并且在最终结果上加入了鼠标以及键盘对雨伞位置的控制,鼠标通过点击屏幕移动雨伞,键盘通过上下左右四个键位移动雨伞,结果显示雨伞可以阻挡雨滴的下落,但在边缘处容易产生遗漏,不能实现完完全全的遮雨效果,仍需进一步的改进。

        在进一步的改进和优化中可尝试加入更换场景,添加雨滴落到雨伞上时的动画,进一步加强雨中雨伞模拟的真实性,并且还可进一步随机选择雨水大小以及是否受到风的影响,以及对风力大小的控制,这些都可以进一步加强真实感,并且在下一步中,可以尝试进行人物拿伞在雨中的姿态变化以及雨伞的移动。

代码开源: GGzili/GuGu: Use Java to draw an umbrella simulation in the rain to realize the umbrella movement and the disappearance of the umbrella when the raindrops collide (github.com)icon-default.png?t=N4P3https://github.com/GGzili/GuGu

  • 28
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值