Odbijanie piłki od krawędzi

0

Cześć. Robię program, który ma za zadanie odbijać piłeczkę od krawędzi. Przy każdym uderzeniu piłka się zwiększa lub zmniejsza w zależności od danej krawędzi oraz zmienia kolor. Problemem jest to, że przy uderzeniu w dolną krawędź i prawą piłka "wychodzi" poza ekran.

Random random = new Random();
Color k = new Color((int) (Math.random() * 255), (int) (Math.random() * 255), (int) (Math.random() * 255));
int x = 100;
int y = 100;
int srednica = 100;
int dx = 2;
int dy = 1;

public Dimension getPreferredSize() {
    return new Dimension(1000, 500);
}

protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    g.setColor(k);
    g.fillOval(x, y, srednica, srednica);
}

public static void main(String[] args) {


    JFrame window = new JFrame();
    window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    com.company.Main pilka = new com.company.Main();
    window.add(pilka);
    window.setVisible(true);
    window.pack();

    pilka.animacja();

}

private void animacja() {


    while (true) {

        x += dx;
        y += dy;
      


        if (y + 100 / 2 > getHeight() || y < 0) {
            dy = -dy;
            k = new Color((int) (Math.random() * 255), (int) (Math.random() * 255), (int) (Math.random() * 255));
            srednica += 100;
        }
        if (x + 100 / 2 > getWidth() || x < 0) {
            dx = -dx;
            k = new Color((int) (Math.random() * 255), (int) (Math.random() * 255), (int) (Math.random() * 255));
            srednica -= 100;
        }
        repaint();

        try {
            Thread.sleep(3);
        } catch (Exception ex) {
        }


    }
1

Dlaczego wewnątrz ifów masz akurat y + 100 / 2 oraz x + 100 / 2 - skąd te konkretne wartości, czym jest 100?

3

Oki, czyli klasyczne programowanie przez permutację :-)

Podejdźmy do tego inaczej: weź kartkę papieru, narysuj na niej prostokąt oraz kilka okręgów; niech jedne z tych okręgów będą wewnątrz prostokątu, a inne na granicy oraz na zewnątrz; zaznacz środki okręgów (chyba że koordynaty przekazywane do fillOval() oznaczają np. top-left corner, to wtedy zaznacz te punkty); czy dostrzegasz jakieś prawidłowości kiedy okrąg znajduje się wewnątrz prostokątu, a kiedy nie?

0

Chodzi o to, że średnica powinna być zwiększana bo prostokąt po przekątnej ma więcej niż okrąg? I tak fillOval() x, y to górny lewy róg.

1

Co to znaczy prostokąt po przekątnej ma więcej niż okrąg?

0

Głupoty mówię. Problem polega na tym, że odbija właśnie od lewego rogu? Czyli jak uderza w dolną lub prawą krawędź wychodzi za nią.

1

Tak, rozumiem problem; czy narysowałeś przypadki skrajne na kartce? :-P

0

Koślawo ale narysowałem, lecz niestety nie oświeciło mnie to :/

2

241708651_1034583740625148_8594719756672325122_n.jpg

Tried my best :P

3

Wyobrażałem sobie to bardziej jak jeden duży prostokąt z wieloma różnymi przypadkami okręgów wewnątrz oraz na granicy, ale tak też damy radę :-)

Sprawdzanie kolizji fachowo nazywa się hit testing, a w naszej sytuacji możemy sobie wyobrazić funkcję taką jak ta:

fn is_circle_inside_rectangle(
    circle_x, // circle's top-left corner
    circle_y, // circle's top-left corner
    circle_r, // circle's radius
    rect_x, // rectangle's top-left corner
    rect_y, // rectangle's top-left corner
    rect_w, // rectangle's width
    rect_h, // rectangle's height
) -> bool

Taka funkcja przyjmuje parametry okręgu i prostokąta oraz zwraca informację true/false w zależności od tego czy okrąg znajduje się całkowicie wewnątrz prostokąta (true = okrąg jest wewnątrz prostokąta, false = okrąg dotyka krawędzi prostokąta bądź znajduje się poza nim).

Możliwą implementacją byłoby:

fn is_circle_inside_rectangle(...) -> bool {
  // Przypadek 1:
  // "top-left wierzchołek" okręgu wystaje poza prostokąt osią X
  //
  // Jeśli wyobrazimy sobie jakiś okrąg:
  //    ----
  //   /    \
  //   |    |
  //   \    /
  //    ----
  //
  // ... to nasz top-left wierzchołek znajduje się tutaj:
  //   *----
  //   /    \
  //   |    |
  //   \    /
  //    ----
  // 
  // ... a cały przypadek, z nałożonym prostokątem, wygląda tak:
  //
  //     |------|
  //   *-|--    |
  //   / |  \   |
  //   | |------|
  //   \    /
  //    ----
  //
  // Jaka jest tu zależność? Jak się okazuje, całkiem prosta!
  if circle_x < rect_x {
    return false;
  }
  
  // Przypadek 2:
  // "top-left wierzchołek" okręgu wystaje poza prostokąt osią Y
  //
  //     *----
  //     /    \
  //   |------|
  //   | \    | 
  //   |  ----|
  //   |------|
  if circle_y < rect_y {
    return false;
  }

  // Przypadek 3:
  // "bottom-right wierzchołek" wystaje poza prostokąt osią X
  //
  // Zacznijmy od oznaczenia tego wierzchołka:
  //   *----
  //   /    \
  //   |    |
  //   \    /
  //    ----#
  //
  // Aby przekonać się jakie ten wierzchołek ma koordynaty, dorysujmy średnicę:
  //   *----
  //   /\   \
  //   | \\  |
  //   \   \ /
  //    ----#
  //
  // Teraz, skoro `*` znajduje się na pozycji `(circle_x, circle_y)`, a `#`
  // znajduje się w "przeciwległym narożniku", to pozycją `#` musi być:
  //   `(circle_x + 2 * circle_r, circle_y + 2 * circle_r)`
  //
  // (podobnie: pozycją drugiego wierzchołka prostokątu jest `rect_x + rect_w`.)
  //
  // Mamy zatem taką sytuację:
  //     *----
  //     /    \
  //  |-----| |
  //  |  \  | /
  //  |   --|-#
  //  |-----|
  //
  // ... lub, w formie kodu:
  if circle_x + 2 * circle_r > rect_x + rect_w {
    return false;
  }
  
  // Przypadek 4, analogicznie :-)
  if circle_y + 2 * circle_r > rect_y + rect_h {
    return false;
  }
  
  return true;
}

Ogólna forma tego problemu - np. gdyby prostokąt mógł być obrócony - jest bardziej złożona, ale wydaje mi się, że na Twoje potrzeby coś takiego powinno wystarczyć.

0

Wybacz, że tak długo zajęła mi odpowiedź. Ogólnie rozumiem sam zamysł tylko mam problem z zaimplementowaniem tego. Pierwszy i drugi przypadek jest rozwiązany. Ale z dołem jest problem bo właśnie nie wiem jak to zaimplementować :/

``
private void animacja() {

    while (true) {

        x += dx;
        y += dy;

        int circleX = x;
        int circleY = y;
        int circleR = srednica;

        int rectangleX = 0;
        int rectangleY = 0;
        int rectangleW = getWidth();
        int rectangleH = getHeight();


        if(circleY + 2 * circleR < rectangleY + rectangleH) {
            if (y + 100 > getHeight()) {
                dy = -dy;
                k = new Color((int) (Math.random() * 255), (int) (Math.random() * 255), (int) (Math.random() * 255));
                srednica += 100;
            }
        }
        if(circleX + 2 * circleR < rectangleX + rectangleW) {
            if (x + 100 > getWidth()) {
                dx = -dx;
                k = new Color((int) (Math.random() * 255), (int) (Math.random() * 255), (int) (Math.random() * 255));
                srednica -= 100;
            }
        }
            if (x < 0) {
                dx = -dx;
                k = new Color((int) (Math.random() * 255), (int) (Math.random() * 255), (int) (Math.random() * 255));
                srednica -= 100;
            }

            if (y < 0) {
                dy = -dy;
                k = new Color((int) (Math.random() * 255), (int) (Math.random() * 255), (int) (Math.random() * 255));
                srednica += 100;
            }
       }
        repaint();
1

Przyjrzyj się dokładniej operatorom porównywania: kiedy >, a kiedy <.

1

Ok, to po kolei:

int circleR = srednica;

r zwyczajowo oznacza promień - jeśli Twoja zmienna srednica rzeczywiście zawiera średnicę, to robiąc 2 * circleR wyliczasz podwójną średnicę (zamiast zamieniać promień na średnicę, czym było założenie tego mnożenia); zamień nazwę circleR na circleD i zamień 2 * circleD na samo circleD.

if(circleY + 2 * circleR < rectangleY + rectangleH) {

Błędny operator porównania: powinno być: >.

if (y + 100 > getHeight()) {

W jakim celu umieściłeś ten warunek?

if(circleX + 2 * circleR < rectangleX + rectangleW) {

Błędny operator porównania: powinno być >.

if (x + 100 > getWidth()) {

W jakim celu umieściłeś ten warunek?

0

Dobra ta zmienna średnica to jest nic innego jak złe nazewnictwo. Z tego co się orientuje filloval() przyjmuje wartość promienia. Czyli kwestia zmiany nazwy zmiennej. Zmiana operatora z < na > nic nie daje bo albo piłka leci za ekran albo odbija się w taki sam sposób jak przedtem czyli wychodzi po za nasz obszar.

if (x + 100 > getWidth()) {

Sprawdza nam czy piłka jest już przy krawędzi. Jeśli tak to zmienia nam kierunek, kolor i zwiększa się lub analogicznie dla innej ściany zmniejsza się.

1

Sprawdza nam czy piłka jest już przy krawędzi.

Skoro to sprawdza czy piłka jest przy krawędzi, to co robi ta linijka? :-)

if(circleY + 2 * circleR < rectangleY + rectangleH) {
0

    Random random = new Random();
    Color k = new Color((int) (Math.random() * 255), (int) (Math.random() * 255), (int) (Math.random() * 255));
    int x = 100;
    int y = 100;
    int radius = 100;
    int dx = 2;
    int dy = 1;


    public Dimension getPreferredSize() {
        return new Dimension(1000, 500);
    }


    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(k);
        g.fillOval(x, y, radius, radius);

    }


    public static void main(String[] args) {


        JFrame window = new JFrame();
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        com.company.Main pilka = new com.company.Main();
        window.add(pilka);
        window.setVisible(true);
        window.pack();

        pilka.animacja();

    }


    private void animacja() {


        while (true) {

            x += dx;
            y += dy;

            int circleX = x;
            int circleY = y;
            int circleR = radius;

            int rectangleX = 0;
            int rectangleY = 0;
            int rectangleW = getWidth();
            int rectangleH = getHeight();


            if (circleY + circleR > rectangleY + rectangleH) {
                dy = -dy;
                k = new Color((int) (Math.random() * 255), (int) (Math.random() * 255), (int) (Math.random() * 255));
                radius += 100;
            }
            if (circleX + circleR > rectangleX + rectangleW) {
                dx = -dx;
                k = new Color((int) (Math.random() * 255), (int) (Math.random() * 255), (int) (Math.random() * 255));
                radius -= 100;
            }
            if (x < 0) {
                dx = -dx;
                k = new Color((int) (Math.random() * 255), (int) (Math.random() * 255), (int) (Math.random() * 255));
                radius -= 100;
            }
            if (y < 0) {
                dy = -dy;
                k = new Color((int) (Math.random() * 255), (int) (Math.random() * 255), (int) (Math.random() * 255));
                radius += 100;

            }
            repaint();


            try {
                Thread.sleep(3);
            } catch (Exception ex) {
            }


        }

    }

Edit: if (circleX + 2 * circleR > rectangleX + rectangleW) też próbowałem użyć.

0

Spróbuj przerzucić instrukcje poruszania sprzed if-ów, po nie:

while (true) {
    /* ... */

    if (circleY + circleR > rectangleY + rectangleH) {
        /* ... */
    }

    if (circleX + circleR > rectangleX + rectangleW) {
        /* ... */
    }

    if (x < 0) {
        /* ... */
    }

    if (y < 0) {
        /* ... */
    }

    x += dx; // tu
    y += dy; // tu

    repaint();

    /* ... */
}

Also:

Z tego co się orientuje filloval() przyjmuje wartość promienia

Skoro fillOval() przyjmuje wartość promienia, to trzeba by przywrócić 2 * circleR :-P

1 użytkowników online, w tym zalogowanych: 0, gości: 1