Игра "Поймай Каплю". Почему капля появляется, после клика на нее, в одном и том же месте?

''' Я начинающий программист. При изучении видеокурса столкнулся с проблемой. По идее капля должна появляться в рандомном месте, но при первом нажатии на нее, появляется в левом крайнем углу и при последующих нажатиях появляется в одном и том же месте. Помогите решить проблему и если не сложно объяснить по какой причине так происходит. '''

package ru.geekbrains.catch_the_drop;

import javax.imageio.ImageIO;

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

public class GameWindow extends JFrame {

    private static GameWindow game_window;
    private static long last_frame_time;
    private static Image background;
    private static Image game_over;
    private static Image drop;
    private static float drop_left = 200;
    private static float drop_top = -100;
    private static float drop_v = 200;
    private static int score;

    public static void main(String[] args) throws IOException {
        background = ImageIO.read(GameWindow.class.getResourceAsStream("background.png"));
        game_over = ImageIO.read(GameWindow.class.getResourceAsStream("game_over.png"));
        drop = ImageIO.read(GameWindow.class.getResourceAsStream("drop.png"));
        game_window = new GameWindow();
        game_window.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        game_window.setLocation(200, 100);
        game_window.setSize(906, 468);
        game_window.setResizable(false);
        last_frame_time = System.nanoTime();
        GameField game_field = new GameField();

        game_field.addMouseListener(new MouseAdapter() {

 @Override
            public void mousePressed(MouseEvent e) {
                int x = e.getX();
                int y = e.getY();
                float drop_right = drop_left + drop.getWidth(null);
                float drop_bottom = drop_top + drop.getHeight(null);
                boolean is_drop = x >= drop_left && x <= drop_right && y >= drop_top && y <= drop_bottom;
                if (is_drop) {
                    drop_top = -100;
                    drop_left = (int) Math.random() * (game_field.getWidth() - drop.getWidth(null));
                    drop_v = drop_v + 20;
                    score++;
                    game_window.setTitle("Score: " + score);

                }
            }
        });

        game_window.add(game_field);
        game_window.setVisible(true);
    }
    private static void onRepaint (Graphics g) {
        long current_time = System.nanoTime();
        float delta_time = (current_time - last_frame_time) * 0.000000001f;
        last_frame_time = current_time;
        drop_top = drop_top + drop_v * delta_time;
        g.drawImage(background, 0, 0, null);
        g.drawImage(drop, (int) drop_left, (int) drop_top, null);
        if (drop_top > game_window.getHeight()) g.drawImage(game_over, 280, 120, null);

    }

    private static class GameField extends JPanel {

        @Override
        protected void paintComponent(Graphics g) {
            
            super.paintComponent(g);
            onRepaint(g);
            repaint();

        }
    }

    }

Ответы (1 шт):

Автор решения: FatCatStudent

Вот часть кода, которая отвечает за расположение капли при клике мышкой:

drop_left = (int) Math.random() * (game_field.getWidth() - drop.getWidth(null));

В документации, про метод Math.random(), написано:

Returns a double value with a positive sign, greater than or equal to 0.0 and less than 1.0.

Т.е. метод возвращает вещественное число между нулём и единицей, включая ноль. [0;1)

Порядок расчётов в данном случае:

  1. Выполняется Math.random()
  2. Выполняется приведение типа (int) Math.random()
  3. Результат приведения типа умножается на оставшееся выражение (game_field.getWidth() - drop.getWidth(null))

Ошибка во втором шаге. Math.random возвращает число от 0 до 1, например 0.14, а в результате приведении дробного типа к целому, дробная часть числа просто отбрасывается. Т.е. от 0.14 дробную часть (.14) отбросили, остался только 0. А т.к. метод Math.random возвращает всегда число между 0 и 1, результат приведения типа всегда будет 0. В итоге получаем :

drop_left = 0 * (game_field.getWidth() - drop.getWidth(null))); = drop_left = 0;

Поэтому капля всегда в левом углу. Нужно поменять код так, чтобы результат всего выражения приводился к целочисленному типу данных

→ Ссылка