Problema Collisioni gioco mini-golf JAVA

Riuzaki
Ciao raga sto realizzando per l'università un video gioco in java che simula il mini-golf...sto gestendo le collisioni della pallina con i muri del campo (intesi come quadrati visto che sono in 2D)...questo è il codice che ho scritto per far muovere la pallina e per farla urtare ma non riesco a capire come mai non funzioni (non funziona perché la pallina attraversa i muri senza che l'algoritmo si accorga che una delle pareti di un muro è stata collisa.

spiego come ho cercato di risolvere il problema:
Ho una lista di muri dei quali ho come informazione i 4 vertici della base che poggia a terra. Ora quanto la pallina si muove ovviamente non si sposta di un pixel alla volta quindi devo tenere traccia del percorso che fa per capire con quale muro e con quale parete di quel muro urta.
Lo faccio trovando la linea designata da due vertici di un muro che si interseca con la linea designata dalle due posizioni della pallina nei due istanti di tempo differenti...AIUTO! :)

questo è il codice:

public class Ball extends AbstractBall{

	  public class check{
		   private int x;
		   private int y;
		   private boolean crash = false;
		   
		   
			public boolean isCrash() {
				return crash;
			}
			
			public void setCrash(boolean crash) {
				this.crash = crash;
			}
			
			public int getY() {
				return y;
			}
			
			public void setY(int y) {
				this.y = y;
			}
			
			public int getX() {
				return x;
			}
			
			public void setX(int x) {
				this.x = x;
			}
	}
	  
	public Ball(float x, float y, float z) {
		super(x, y, z);
	}
	


	//funzione che fa muovere la pallina
	public synchronized void move(List<Wall> wall) {
		long currentTime = System.currentTimeMillis();
		long delta = currentTime - Time;
		
		vel += acc * (delta / 1000.f);
		
		if(vel > 0){
			x += versore.getX() * (vel * (delta / 1000.f));
			y += versore.getY() * (vel * (delta / 1000.f));
		}
		else{
			vel = acc = 0;
			Time = System.currentTimeMillis();
			return;
		}
		if(vel > 0)
			resetAcc();
		
		check temp = new check();
		Punto p1 = new Punto(precx, precy);
		Punto p2 = new Punto((int)x, (int)y);
		Linea l = new Linea(p1, p2);
		
		if( foundIntersections(temp, l, wall) ){
			x = Math.abs(temp.getX()) ;
			y = Math.abs(temp.getY());
			
			if(!temp.isCrash())
			{
				System.out.println(" urto verticale ");
				versore.set(versore.getX()*-1, versore.getY(), 0);
			}
			else
			{
				System.out.println(" urto orizzontale ");
			    versore.set(versore.getX(), versore.getY()*-1, 0);
			}
		}
		
		
			this.precx = x;
			this.precy = y;
			this.precz = z;
		Time = System.currentTimeMillis();
	}


	private synchronized boolean foundIntersections(check temp, Linea l, List<Wall> wall) {
		boolean intersectBool = false;
		int distMin = Integer.MAX_VALUE;
		
		for(int i = 0; i < wall.size(); i++){
			
			Punto A = new Punto(wall.get( i).getA());
			Punto B = new Punto(wall.get( i).getB());
			Punto C = new Punto(wall.get( i).getC());
			Punto D = new Punto(wall.get( i).getD());
			
			Linea l1 = new Linea(A, B);
			
			Linea l2 = new Linea(A, C);
			
			Linea l3 = new Linea(D, B);
			
			Linea l4 = new Linea(D, C);
			
			Punto p = new Punto(0,0);
			
			if(intersectPoint(l, l1,p))
			{
				if((int) Math.sqrt((l.getP1().getX() - p.getX())*(l.getP1().getX() - p.getX())+(l.getP1().getY() - p.getY())*(l.getP1().getY() - p.getY())) < distMin){
						temp.setCrash(true);
						temp.setX((int)p.getX());
						temp.setY((int)p.getY());
						distMin = (int) Math.sqrt((l.getP1().getX() - p.getX())*(l.getP1().getX() - p.getX())+(l.getP1().getY() - p.getY())*(l.getP1().getY() - p.getY()));
					}
				if(!intersectBool)
					intersectBool= true;
			}
			if(intersectPoint(l, l2,p))
			{
				if((int) Math.sqrt((l.getP1().getX() - p.getX())*(l.getP1().getX() - p.getX())+(l.getP1().getY() - p.getY())*(l.getP1().getY() - p.getY())) < distMin){
						temp.setCrash(false);
						temp.setX((int)p.getX());
						temp.setY((int)p.getY());
						distMin = (int) Math.sqrt((l.getP1().getX() - p.getX())*(l.getP1().getX() - p.getX())+(l.getP1().getY() - p.getY())*(l.getP1().getY() - p.getY()));
					}
				if(!intersectBool)
					intersectBool= true;			
			}
			if(intersectPoint(l, l3,p))
			{
				if((int) Math.sqrt((l.getP1().getX() - p.getX())*(l.getP1().getX() - p.getX())+(l.getP1().getY() - p.getY())*(l.getP1().getY() - p.getY())) < distMin){
						temp.setCrash(false);
						temp.setX((int)p.getX());
						temp.setY((int)p.getY());
						distMin = (int) Math.sqrt((l.getP1().getX() - p.getX())*(l.getP1().getX() - p.getX())+(l.getP1().getY() - p.getY())*(l.getP1().getY() - p.getY()));
					}
				if(!intersectBool)
					intersectBool= true;
			}
			if(intersectPoint(l, l4, p))
			{
				if((int) Math.sqrt((l.getP1().getX() - p.getX())*(l.getP1().getX() - p.getX())+(l.getP1().getY() - p.getY())*(l.getP1().getY() - p.getY())) <distMin){
						temp.setCrash(true);
						temp.setX((int)p.getX());
						temp.setY((int)p.getY());
						distMin = (int) Math.sqrt((l.getP1().getX() - p.getX())*(l.getP1().getX() - p.getX())+(l.getP1().getY() - p.getY())*(l.getP1().getY() - p.getY()));
					}
				if(!intersectBool)
					intersectBool= true;
			}
		}
		System.out.println(" distanza " + distMin);
		if(intersectBool)System.out.println("ha intersecato!");
		return intersectBool;
	}

	public synchronized boolean intersectPoint(Linea L1, Linea L2, Punto p) {
		
		double y = 0;
		double x = 0;
		
		if ( L1.getM() != L2.getM() ) {
			if ( !Double.isInfinite( L1.getM() ) && !Double.isInfinite( L2.getM() ) ) {
				x = ( L2.getN() - L1.getN() ) / ( L1.getM() - L2.getM() );
				y = L2.getM() * x + L2.getN();
			}
			else {
				if ( Double.isInfinite( L1.getM() ) ) {
					x = L1.getP1().getX();
					y = L2.getM() * x + L2.getN();
				}
				if ( Double.isInfinite( L2.getM() ) ) {
					x = L2.getP1().getX();
					y = L1.getM() * x + L1.getN();
				}
			}
			p.setX( x );
			p.setY( y );
			if(isInTheDirection(L1, p) && isInTheDirection(L2, p))
				return true;
			return false;
		}
		else
			return false;
	}
	
	private synchronized boolean isInTheDirection(Linea L1, Punto p){
		
		int minX = (int) Math.min(L1.getP1().getX(), L1.getP2().getX()); 
		int maxX = (int) Math.max(L1.getP1().getX(), L1.getP2().getX());
		int minY = (int) Math.min(L1.getP1().getY(), L1.getP2().getY()); 
		int maxY = (int) Math.max(L1.getP1().getY(), L1.getP2().getY());
		
		if((minX < p.getX() && p.getX() < maxX) && (minY < p.getY() && p.getY() < maxY))
			return true;
		return false;
	}
}



Risposte
apatriarca
La classe Ball viene usata da più thread in contemporanea? Come mai tutti i metodi sono synchronized? Che scopo ha la classe astratta AbstractBall? Perché misuri il tempo passato all'interno della classe Ball? Immagino ci siano altre classi che vengono aggiornate in un singolo frame e in questo modo finisci per usare un tempo diverso per ogni oggetto aggiornato nel frame. Perché separi la componente scalare e quella vettoriale della velocità? Perché usi uno scalare invece che un vettore per l'accelerazione? Perché non permetti velocità o accelerazioni negative? Perché hai 3 componenti nel vettore ma ne aggiorni solo due? Perché usi posizioni intere nel calcolo delle intersezioni? Perché prendi il valore assoluto della posizione restituita dal metodo per trovare l'intersezione nel metodo di movimento? Che significato ha isCrash? Perché ti complichi la vita lavorando su rettangoli quando puoi lavorare su ogni segmento separatamente direttamente? Perché converti sempre tutto su int nella conversione? Quali valori possono assumere le espressioni che converti ad int? Sei certo che nessuna di quelle restituisce un valore nell'intervallo [0, 1] in caso di intersezione?

Ti ho scritto tutte queste domande perché trovo abbastanza difficile seguire tutto il tuo codice e le motivazioni per cui le intersezioni non vengono misurate correttamente potrebbe essere dovuto ad errori dovuti alla conversioni a interi, ad un errore di programmazione in una qualsiasi delle righe o ad altro (problemi con il calcolo dei tempi per esempio o nella visualizzazione). Se vuoi posso dare un'occhiata al programma intero. Vedi tu se preferisci caricarlo da qualche parte o mandarmelo per posta o altro.

apatriarca
Java è perfettamente adeguato ad essere usato per la realizzazione di giochi di questa complessità o anche superiore. Il problema del tuo programma è però che hai “esagerato” nella progettazione del tuo programma. Il numero di classi e la quantità di codice è decisamente superiore a quella necessaria per implementare le funzionalità presenti. Ma è abbastanza normale progettare male un certo tipo di applicazione quando è la prima volta che si progetta un'applicazione dello stesso tipo.

Vediamo per prima cosa il pacchetto riservato alla fisica. Contiene un'interfaccia chiamata MotionManager che rappresenta un oggetto che si muove e una classe che rappresenta un vettore in 3 dimensioni. Nonostante si tratti di un pacchetto molto ridotto ha già tanti problemi:
1. Il mondo è in 2D e non ha quindi alcun senso considerare 3 dimensioni e utilizzi quindi inutilmente 8 byte in più per ogni vettore.
2. Dividi forse inutilmente la lunghezza del vettore e la sua direzione e richiedi che siano aggiornate separatamente. Non è quindi certo che la lunghezza del vettore rappresentato da (x, y, z) sia quella memorizzata nella classe.
3. Siccome le operazioni sui vettori in 3D non servono all'interno del gioco, la maggior parte dei metodi della classe sono del tutto inutilizzati nel programma.
4. I nomi che hai dato alle operazioni in VectorR3 non sono corretti in inglese (o sono almeno strani). Il prodotto vettoriale è per esempio normalmente chiamato cross product e positivo o negativo si scrivono positive e negative e non positif e negatif. Il prodotto scalare è chiamato normalmente dot product.
5. Non è chiaro il motivo per cui venga definito un vettore di rotazione tridimensionale. Le rotazioni non sono infatti vettori. Formano piuttosto un gruppo (di Lie) di dimensione 1 nel caso di rotazioni nel piano e di dimensione 3 nel caso di rotazioni nello spazio tridimensionale. Mi rendo conto che in fisica si descrivono spesso le rotazioni in questo modo, ma è formalmente sbagliato. In questo caso è poi inutile perché le rotazioni nel piano possono essere rappresentate dai numeri complessi di norma unitaria.
6. Il calcolo dell'angolo non è molto robusto.
7. MotionManager NON è un manager siccome non gestisce nulla. Il nome andrebbe quindi cambiato.
8. MotionManager non implementa alcuna formula fisica, né contiene campi. Implementando questa classe non si garantisce quindi niente sul comportamento della classe che la implementa.
9. MotionManager non viene mai usata direttamente.
10. C'è un solo oggetto che si muove in tutto il gioco e per implementarlo usi 4-5 classi.
Un design migliore avrebbe potuto essere quello di definire una classe VectorR2 o usare i numeri complessi (che Java non supporta però nativamente). L'utilità di MotionManager è invece a mio parere molto discutibile. In altri contesti, potrebbe essere utile implementare diversi metodi di integrazione, ma in questo caso mi pare inutile.

Per quanto riguarda poi AbstractBall e Ball ne ho già parlato nello scorso post quello che trovo di strano. Guarderò poi il resto.

Riuzaki
cerco di fare tesoro di quello che hai scritto e di cominciare a modificare la progettazione del mio progetto. diciamo che io vorrei realizzarlo in 3D alla fine, ed è per quest che vedi sempre una terza variabile "z"...per ora lo sto facendo in 2D ...ma se funziona in 2D credo non sia difficile farlo funzionare in un 3D semplice dove ipotizzo che la pallina non possa saltare e quindi gli urti nel piano X,Z sono perfettamente coincidenti rispetto a quelli del piano X,Y che sto rappresentando adesso.

apatriarca
Lo stato del gioco non deve necessariamente fare uso della stessa rappresentazione usata per visualizzarlo. Non importa se il gioco è visualizzato in 2D o in 3D, se rimane a due dimensioni è comunque comodo rappresentare il problema nelle sole 2 dimensioni. E' infatti sempre possibile aggiungere la terza dimensione quando si deve visualizzare la scena. Ti faccio un esempio un po' diverso. Supponi di voler rappresentare un oggetto che è vincolato a muoversi su di una circonferenza di raggio R e centro C. Puoi rappresentare l'oggetto con le sue coordinate in 2D, oppure semplicemente con l'angolo che forma rispetto alla retta orizzontale. Quale delle due rappresentazioni sia più conveniente dipende da molti fattori, ma entrambe sono possibili. Se anche si decidesse poi di visualizzare il problema in 3D, il problema rimarrebbe lo stesso e aggiungere una direzione complicherebbe solo tutti i calcoli.

Rispondi
Per rispondere a questa discussione devi prima effettuare il login.