Detekcja zderzeń X kul

0

Posiadam obecnie coś takiego

Link do projektu w Eclipse: http://dl.dropbox.com/u/1280777/BallApplication.zip

Póki co mam panel na który wrzucam dowolną ilość kul. Jedynym elementem od którego mogą się odbić są ściany.

  1. Pytanie jak zrobić, aby kule odbijały się też od siebie. Z detekcją samego zdarzenia nie ma problemu ponieważ wiadomo, że jeśli odległości jednego środka od drugiego jest mniejsza, równa sumie ich promieni to doszło do zderzenia, ale jak wyliczyć kąt odbicia?

2)W klasie Ball w ogóle nie trzymam czegoś takiego jak kąt po jakim się porusza kula, a jedynie wartość składowych x i y oraz ich zwroty. Czy na podstawie tych informacji jestem w stanie wyliczyć nowe wartości po zderzeniu?

czyli np Ball1 porusza się zgodnie z wektorem Ball1[3, 3] zaś Ball2[-1,2], wiem, że się zderzyły, czego jeszcze potrzebuję, aby wyliczyć nowe wartości tych wektorów?

package pl.animation.ball;

import java.awt.geom.Ellipse2D;
import java.awt.geom.RectangularShape;

public class Ball {
	// wielkość kuli
	public double ELLIPSE_SIZE = 20.0;

	// zmiana prędkości składowej x
	private int shiftDx = 4;
	// zmiana prędkości składowej y
	private int shiftDy = 3;

	// aktualne położenie x
	private int x;
	// aktualne położenie y
	private int y;

	// czy wektor x ma wartość ujemną (kula porusza się w prawo czy w lewo)
	private boolean dx = true;
	// czy wektor y ma wartość ujemną (kula porusza się w dół czy w górę)
	private boolean dy = true;

	// waga
	private int weight = 1;

	public Ball(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}

	public int getX() {
		return x;
	}

	public void setX(int x) {
		this.x = x;
	}

	public int getY() {
		return y;
	}

	public void setY(int y) {
		this.y = y;
	}

	public void moveTo(int x, int y) {
		this.x = x;
		this.y = y;
	}

	public void moveByParam(int panelWidth, int panelHeight) {
		if (dx) {
			this.x = this.x + shiftDx;
		} else {
			this.x = this.x - shiftDx;
		}

		if (dy) {
			this.y = this.y + shiftDy;
		} else {
			this.y = this.y - shiftDy;
		}
		if (checkCollisionWithBorder(panelWidth, panelHeight)) {
			// tak dla testów
			// shiftDy = (new Random().nextInt(10))+1;
		}
		;
	}

	private boolean checkCollisionWithBorder(int panelWidth, int panelHeight) {
		boolean result = false;

		if ((isDx()) && (getX() + 20 >= panelWidth)) {
			setDx(false);
			result = true;
		}

		if (!(isDx()) && (getX() < 0)) {
			setDx(true);
			result = true;
		}

		if ((isDy()) && (getY() + 20 >= panelHeight)) {
			setDy(false);
			result = true;
		}

		if (!(isDy()) && (getY() < 0)) {
			setDy(true);
			result = true;
		}
		return result;
	}

	public boolean isDx() {
		return dx;
	}

	public void setDx(boolean dx) {
		this.dx = dx;
	}

	public boolean isDy() {
		return dy;
	}

	public void setDy(boolean dy) {
		this.dy = dy;
	}

	public RectangularShape getShape() {
		return new Ellipse2D.Double((double) x, (double) y, ELLIPSE_SIZE,
				ELLIPSE_SIZE);
	}

	public double getSpeed() {
		return Math.sqrt(Math.pow((double) shiftDx, 2.0)
				+ Math.pow((double) shiftDy, 2));
	}
}


 
1

Pierwsza sprawa - czemu podzieliłeś wektor na dx, dy oraz shiftDx, shiftDy? Przecież te dwie ostatnie zmienne mogą być ujemne i na dodatek ułatwi to obliczenia.
Co do reszty, to skoro masz wektory ruchu, to wyliczenie kąta kierunku jest banalne (np. tangens alpha = shiftDy/ShiftDx). Podobnie na podstawie położenia x i y obu kul dostajesz odcinek, do którego prostopadła w połowie długości jest styczną. Obie kule od tej stycznej się odbijają pod kątem jaki tworzy z nią kąt alpha dla jednej i drugiej kuli wg powszechnie znanej zasady, że kąt odbicia jest identyczny jak kąt uderzenia. Na tej podstawie wyliczysz kąt odbicia i od Ciebie zależeć będzie czy długość wektora kierunku nie zmniejszy się (odbicie doskonale sprężyste), czy zmniejszy.
Trochę obliczeń, ale efekt murowany.

0

Pozwoliłem sobie rozrysować całą sytuację w załączniku jest odpowiedni plik (talent plastyczny znikomy :D)

Czerwona długa linia to:

Podobnie na podstawie położenia x i y obu kul dostajesz odcinek, do którego prostopadła w połowie długości jest styczną

Mógłbyś mi opisać skąd dokładnie wziąłeś

tangens alpha = shiftDy/ShiftDx

Oczyszczony kod kuli:

package pl.animation.ball;

import java.awt.geom.Ellipse2D;
import java.awt.geom.RectangularShape;

public class Ball {
	// wielkość kuli
	public double ELLIPSE_SIZE = 20.0;

	// zmiana prędkości składowej x
	private int shiftDx = 4;
	// zmiana prędkości składowej y
	private int shiftDy = 3;

	// aktualne położenie x
	private int x;
	// aktualne położenie y
	private int y;

	// waga
	private int weight = 1;

	public Ball(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}

	public int getX() {
		return x;
	}

	public void setX(int x) {
		this.x = x;
	}

	public int getY() {
		return y;
	}

	public void setY(int y) {
		this.y = y;
	}

	public void moveTo(int x, int y) {
		this.x = x;
		this.y = y;
	}

	public void moveByParam(int panelWidth, int panelHeight) {
		
			this.x = this.x + shiftDx;	
			this.y = this.y + shiftDy;
			
		if (checkCollisionWithBorder(panelWidth, panelHeight)) {
			// tak dla testów
			// shiftDy = (new Random().nextInt(10))+1;
		}
		;
	}

	private boolean checkCollisionWithBorder(int panelWidth, int panelHeight) {
		boolean result = false;

		if ((shiftDx > 0) && (getX() + 20 >= panelWidth)) {
			shiftDx *= -1;
			result = true;
		}

		if ((shiftDx < 0) && (getX() < 0)) {
			shiftDx *= -1;
			result = true;
		}

		if ((shiftDy > 0) && (getY() + 20 >= panelHeight)) {
			shiftDy *= -1;
			result = true;
		}

		if ((shiftDy < 0) && (getY() < 0)) {
			shiftDy *= -1;
			result = true;
		}
		return result;
	}


	public RectangularShape getShape() {
		return new Ellipse2D.Double((double) x, (double) y, ELLIPSE_SIZE,
				ELLIPSE_SIZE);
	}
	
	public double getSpeed() {
		return Math.sqrt(Math.pow((double) shiftDx, 2.0)
				+ Math.pow((double) shiftDy, 2));
	}
}
 
1

OK, obie czerwone linie na tym obrazku powinny być wobec siebie pod kątem prostym. U Ciebie wyszedł nie bardzo, ale zakładam, że to przez wadliwe proporcje obrazka, który się najwyraźniej skrócił w pionie.

Mógłbyś mi opisać skąd dokładnie wziąłeś

Pewnie. Z definicji funkcji trygonometrycznych dla trójkąta prostokątnego. :)
Wektor (o obu dodatnich składnikach - zakładając, że jest to wektor 2D), to nic innego jak przeciwprostokątna takiego trójkąta. Obie przyprostokątne są wtedy po prostu składowymi wektora.

Zauważ, że z tej samej własności korzystasz do obliczenia długości wektora, czyli przeciwprostokątnej takiego trójkąta. Używasz przecież Pitagorasa w getSpeed().

W załączniku wkleiłem obrazek pokazujący jak geometrycznie obliczyć wektor odbicia dla obu kul.
Zauważ, że kąty odbicia są identyczne po obu stronach styku wektora w stosunku do stycznej przyłożonej w miejscu środka kuli.
Dla wyeliminowania robienia przez Ciebie założeń z obrazka, obie kule celowo narysowałem różnych wielkości.
Gdyby uwzględniać w takim odbiciu wielkość kul jako ich masę, to długość wektorów wynikowych (ale nie kąt) mogłyby zależeć od stosunku wielkości jednej kuli do drugiej. Można też użyć wzorów fizycznych na pęd (masz prędkość, kierunek i masę określoną na podstawie średnicy, przy założeniu tego samego ciężaru właściwego dla obu kul).

To co narysowałem musiałbyś zakodować, co nie jest szczególnie łatwe (ale trudne też niespecjalnie).

Uwagi.

  1. Mnożenie przez -1, to nic innego jak zmiana znaku. Jeżeli masz zmienną x, to zmiana znaku wygląda tak: x = -x; Różnica taka, że negowanie znaku trwa 20 razy krócej (bo to tylko zmiana jednego bitu i inkrementacja/dekrementacja) od mnożenia.

  2. Zamiast x = x + dx pisz x +=dx. Szansa pomylenia dwóch identycznych wyrażeń (x) po obu stronach spada wtedy do zera.

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