Proplem (evtl. Klassen?)

Tiding

Aktives Mitglied
Hallo liebe Community,

ich habe ein kleines Problem, bei dem ich nicht weiter komme!
Ich habe mich an den Versuch gewagt, mir meine eigene 3D-Welt erschaffen zu wollen.
Zum einen, könnt ihr mich gerne auf etwagige Dinge aufmerksam machen, die ich offensichtlich falsch
verstanden habe...

Mein explizites Problem besteht allerdings in der Codezeile
Code:
setObject_Vertix(o, i, transformation.getVertix());

Ich hab hier mal die Zusammenhänge gepostet.

Das Problem ist, dass sich die vertices meines Objektes p, das ich der Funktion render() übergeben habe genau dann auch ändern, sobald ich die meines neu erstellten Objektes o ändere. :cry:
Es scheint als ob jedes erstellte Objekt nicht die (so wie ich mir gedacht habe) eigenen Variablen mit erstellt und darin abspeichert und auch wieder daraus abruft, sondern irgendwie, dass global alle Objekte auf ein und dieselben Variablen zugreifen, was ich nicht will. ???:L

Ich würde mich freuen, wenn mir das einer bereit ist, meinen Denkfehler zu beseitigen :bloed: :D

Java:
public class Object{
	int id;
	Vertix[] vertices;
	String type;
	Vektor nullVektor;
	int value;
	
	int priority; 
	
	public Object(int id, Vertix[] vertices, Vektor nullVektor, String type, int value, int priority){
		this.id = id;
		this.vertices = vertices;
		this.nullVektor = nullVektor;
		this.type = type;
		this.value = value;
		this.priority = priority;
	}
	public Object(Object o){
		if(o == null) think();
		else{
			this.id = o.getID();
			this.vertices = o.getVertices();
			this.nullVektor = o.get_nullVektor();
			this.value = o.getValue();
			this.type = o.type;
			this.priority = o.getPriority();
		}
	}
	public Object(){	
	}
	public void think(){
	}
}


//-------------
public Object render(Object p, Matrix View, Matrix Projection){
		 Operationen3D d3 = new Operationen3D();
		Object o = new Object(p);
		Matrix transformation, t1, world = new Matrix();
		
		for(int i = 0; i < p.getVertices().length; i++){
			world = d3.Translation(p.getVertices()[i].toVector());
			t1 = world.transform(View);
			transformation = t1.transform(Projection);

                        //ich weiß, dass mein Problem in dieser Methode liegt und damit wahrscheinlich darin,
                        //wie ich meine erstellte Klasse Object auffasse...
			setObject_Vertix(o, i, transformation.getVertix());
		}
		  return o;
	}
	
	public void setObject_Vertix(Object o, int i, Vertix vertix){
		o.getVertices()[i] = vertix;
	}
 
Zuletzt bearbeitet:

BuddaKaeks

Bekanntes Mitglied
So, was mir ls allererstes auffällt, nenne ein Classe NIE, NIEMALS Object, neen sie GameObject oder so, aber Object ist halt ne Klasse aus dem java.lang Paket, man könnte auch sagen es ist DIE Klasse schlechthin, das führt nur zu irgendwelchen Problemen.

Also Klassen nie so nennen, wie es sie schon in der StandartBibliothek gibt
 

Tiding

Aktives Mitglied
Nun, daran liegt es auf jeden Fall nicht ;).

Aber ich werde den Ratschlag in Zukunft beherzigen! :toll:

Ich nehme auch gerne Tipps bezüglich der Themenwahlt dieses Beitrags hier im Forum an, falls jemand der Meinung ist, dass es woanders besser aufgehoben ist! :bahnhof:

lg
 
Zuletzt bearbeitet:

Tiding

Aktives Mitglied
Um mein Problem zu verdeutlichen habe ich das ganze noch etwas begrenzt:

Java:
public class GameObject{
	int x;
	int y;
	
	public GameObject(int x, int y){
		this.x = x;
		this.y = y;
	}
	public GameObject(){
		this.x = 1;
		this.x = 1;
	}
	
	public int getX(){ return this.x; }
	public int getY(){ return this.y; }
}

//-------------------------------------
public class ProblemToSolve {
	
    public static void main(String[] args) {
        GameObject g = new GameObject(10,10);
        GameObject o = render(g);
    }
    
	public static GameObject render(GameObject gameObject){
		GameObject newObject = gameObject;
		int x, y;
		
		x = newObject.getX() *2;
		y = newObject.getY() *2;
		
		//Ausgabe: (soweit so gut)
                // GameObject: (10 | 10)
                // New Object: (10 | 10)
		
		setObject_Vertix(newObject, x, y);
		
		//Ausgabe: (nicht mehr so gut)
                // GameObject: (20 | 20)
                // New Object: (20 | 20)
		
		 return newObject;
	}
	
	public static void setObject_Vertix(GameObject o, int x, int y){
		o.x = x;
		o.y = y;
	}
}

Nun dann habe ich mir gedacht, dass
Code:
GameObject newObject = gameObject;
evtl ein Problem darstellt und ich habe folgendes gemacht:

Code:
public GameObject(GameObject gameObject){
		this.x = gameObject.getX();
		this.y = gameObject.getY();

und damit ein neues Objekt mit den gleichen Werten erstellt:

Code:
GameObject newObject = new GameObject(gameObject);

Und siehe da, es funktioniert, wie es funktionieren soll; Ausgabe:
GameObject: (10 | 10)
New Object: (10 | 10)
GameObject: (10 | 10)
New Object: (20 | 20)


Übertrage ich das ganze in mein Programm: Es funktioniert nicht mehr...
Entweder bin ich einfach nur :bloed: oder ich übersehe da irgendwas! Auch in meinem Programm sind "newObject" und "gameObject" verschieden, sprich nur die Variablen werden gleichgesetzt! Und trotzdem will es nicht so wie ich will dass es soll! ;(
 

rme

Top Contributor
Hallo :)

Keine Sorge, dein OOP-Verständnis ist glaube ich vollständig heil. Das Problem liegt glaube ich in dieser Zeile:

Java:
this.vertices = o.getVertices();

Das erzeugt keine tiefe Kopie, sondern sorgt dafür, dass sich beide Variablen exakt auf das gleiche Objekt beziehen (ein Array ist auch ein Objekt). Bei allen Objekten sorgt eine Zuweisung via = dafür, dass sich beide Variablen dann auf den gleichen Speicherbereich beziehen - du erstellst damit sozusagen einen Alias für den Inhalt der Variable. Nur bei den primitiven Datentypen (int, float, short usw.) ist das anders, weil dies keine Objekte sind.

Du möchtest den Speicher ja duplizieren, damit beide Objekte andere Vertices haben können. Genau das musst du in einem Copy-Constructor (und an allen anderen Stellen, wo du den =-Operator benutzt und keinen Alias anlegen möchtest, sondern eine Kopie erstellen möchtest) auch tun. Da [c]vertices[/c] ein Array ist, kannst du dafür die Methode [c]Arrays.copyOf[/c] benutzen, die genau für sowas da ist:

Java:
this.vertices = Arrays.copyOf(o.getVertices(), o.getVertices().length);

Evtl. sollte auch einfach getVertices bereits diese Kopie erstellen, damit man das nicht jedesmal machen muss - das kommt aber auf die spezielle Situation deines Codes an, kannst du ja mal überlegen.
 
Zuletzt bearbeitet:

Tiding

Aktives Mitglied
Ahhh, vielen vielen Dank, darauf wäre ich wahrscheinlich in 100 Jahren noch nicht gekommen :bloed: :toll:

Wieder was dazu gelernt :). Dann mach ich mich mal ans umschreiben.

Ich glaube den selben Fehler habe ich nämlich nicht nur einmal begangen, hehe.. :rtfm:
 
Zuletzt bearbeitet:

rme

Top Contributor
Und wo wir schon dabei sind, du scheinst ja recht lernwillig zu sein ;):

Der ==-Operator funktioniert genauso, wie der =-Operator: Er vergleicht nur, ob beide Objekte auf den gleichen Speicher verweisen. Deshalb kannst du Strings nicht mit == vergleichen, ebenfalls deine Vektoren nicht.. und so weiter. Das geht nur bei primitiven Datentypen.

Und auch der Garbage-Collector, der den Speicher von Objekten wieder freigibt, arbeitet nach diesem Prinzip: Wenn du mit new dafür sorgst, dass ein Objekt im Speicher erzeugt wird, merkt sich der Garbage-Collector, wie viele Variablen noch auf diesen Speicher zeigen. Sobald es 0 sind, wird der Speicher als freigabefähig markiert.

Du merkst, das ist eine recht fundamentale Sache in Java. Ich finde sie nicht schön, denn sie sorgt oft für Stolperfallen, wie du nun auch gemerkt hast :)
 
Zuletzt bearbeitet:

Tiding

Aktives Mitglied
Ja,

scheint so :D!

Mit "==" vergleicht man keine Strings, das sollte ich eigentlich wissen. :autsch:

Eine Frage: wie funktioniert denn dann die copyOf Methode?
 

rme

Top Contributor
Das mit dem String-Vergleich habe ich bei dir auch nicht gesehen, ich habe es nur erwähnt, weil du jetzt die Erklärung tiefer verstehst, denke ich :)

copyOf legt mit [c]new[/c] ein Array an und benutzt System.arrayCopy, um die Werte reinzukopieren.

arrayCopy könnte man mit einer Schleife nachbauen, aber das wäre langsamer. System.arrayCopy benutzt schnellen Maschinencode, um ein Array zu kopieren.

Was mir dabei gerade auffällt: Das wird dein Problem nicht lösen, falls der Inhalt des Arrays keine primitiven Datentypen sind. Denn dann hast du zwar das Array kopiert, aber der Inhalt waren ja nur Alias-Namen für deine Objekte..

Ergo: Du musst das selbst machen, also ein neues Array der gleichen Größe anlegen und in einer Schleife die Objekte kopieren, indem du die Vertices einzeln klonst. Falls in deinem Vertex auch Objekte vorhanden sind, müssen diese ebenfalls geklont werden usw.
 

Tiding

Aktives Mitglied
Glücklicherweise muss ich das nicht, da es ein float[][] array ist :D

Aber warum meintest du ich müsse das bei String type ebenfalls machen?
 

rme

Top Contributor
Bist du dir sicher?

Java:
Vertix[] vertices;

d.h. das hier wird nicht klappen:

Java:
this.vertices = Arrays.copyOf(o.getVertices(), o.getVertices().length);

weil das Array vom Typ Vertex ist, also kein primitiver Datentyp. Du kannst dir das so vorstellen, dass in dem Array nicht die eigentlichen Objekte gespeichert sind, sondern nur die Info, wo sich die Objekte im Speicher befinden. copyOf erzeugt dann eine Kopie des Arrays - dann hast du ein Array, dessen Inhalte sich aber noch auf die gleichen Objekte beziehen, weil nur die Info kopiert wurde, wo sie zu finden sind.

Die Lösung müsste ungefähr so aussehen:
Java:
this.vertices = new Vertex[o.getVertices().length];
for(int i = 0; i < o.getVertices().length; i++) {
  this.vertices[i] = new Vertex(o.getVertices()[i]);
}

Der Vertex-Konstruktor muss dann ebenfalls alle primitiven Datentypen via = kopieren und alle anderen via Arrays.copyOf bzw. durch weitere solche Konstruktoren kopieren.. auf diese Weise bekommt man das einigermaßen in den Griff.

Bei Strings ist das nicht anders, wenn im Programm steht:
Java:
String a = "Schöner String";
String b = a;

Dann wird die Zeichenkette "Schöner String" im Speicher abgelegt. Danach die Zuweisung an a: Da wird einfach nur festgelegt, dass a nun ein Alias für diesen Speicherbereich ist. In der 2. Zeile wird mit b = a dafür gesorgt, dass b nun ebenfalls ein Alias für den Speicherbereich von a ist. Wenn man etwas an a ändert, ist b davon auch betroffen, weil sie sich den Speicher teilen / Aliase für den gleichen Speicher sind.

Aber bei Strings ist das ausnahmsweise egal, weil man Strings in Java gar nicht ändern kann :)

Nachtrag: Dass du deine Vertices ebenfalls immutable (Fachbegriff für unveränderbar, so wie bei String) sein lässt, ist natürlich eine alternative Lösung. Dann bist du von all diesen Problemen verschont, musst aber anders damit rechnen. Eine Methode zur Translation darf dann nicht das Objekt verschieben, indem es das Array ändert - sondern muss ein neues Objekt mit den geänderten Daten zurückgeben. Das wäre eher der Java-Weg, sowas zu lösen. String macht das ja auch so - beispielsweise gibt toLowerCase einen neuen String zurück, statt den eigentlichen zu ändern. So erspart man sich viel Ärger.
 
Zuletzt bearbeitet:

Tiding

Aktives Mitglied
Nunja, das funktioniert allerdings auch schon mit dem copyOf-Konstruktor;

Java:
public class Vertix {
	float x;
	float y;
	float z;
	int COMPONENTS = 3;
	
	public Vertix(float x, float y, float z){
		this.setX(x);
		this.setY(y);
		this.setZ(z);
	}
	public Vertix(){
		
	}
	public float getX() { return this.x; }
	public float getY() { return this.y; }
	public float getZ() { return this.z; }
	
	public Vektor toVector(){
		return new Vektor(this.getX(),this.y,this.z,1);
	}
	public void setX(float x) {	this.x = x;	}
	public void setY(float y) {	this.y = y;	}
	public void setZ(float z) {	this.z = z;	}
}

Hm, du hast recht, ich habe das gerade verwechselt!
Allerdings funktioniert es trotzdem ._.

Meine Operationen sind wie folgt angelegt:
Java:
public class Operationen3D implements Functions{
	
	public Matrix LookAt(Vektor pEye, Vektor pAt, Vektor pUp){
		Matrix m = new Matrix();
		Vektor xaxis, yaxis, zaxis = new Vektor();
		
		zaxis = new Vektor(pAt.getX() - pEye.getX(), pAt.getY() - pEye.getY(), pAt.getZ() - pEye.getZ()).normalize();
		xaxis = pUp.cross(zaxis).normalize();
		yaxis = zaxis.cross(xaxis);

		m.getMatrix()[0][0] = xaxis.getX();
		m.getMatrix()[1][0] = yaxis.getX();
		m.getMatrix()[2][0] = zaxis.getX();
		m.getMatrix()[3][0] = 0;

		m.getMatrix()[0][1] = xaxis.getY();
		m.getMatrix()[1][1] = yaxis.getY();
		m.getMatrix()[2][1] = zaxis.getY();
		m.getMatrix()[3][1] = 0;

		m.getMatrix()[0][2] = xaxis.getZ();
		m.getMatrix()[1][2] = yaxis.getZ();
		m.getMatrix()[2][2] = zaxis.getZ();
		m.getMatrix()[3][2] = 0;

		m.getMatrix()[0][3] = -xaxis.dot(pEye);
		m.getMatrix()[1][3] = -yaxis.dot(pEye);
		m.getMatrix()[2][3] = -zaxis.dot(pEye);
		m.getMatrix()[3][3] = 1;
	
		 return m;
	}
	
	public Matrix Projection(float fovy, float aspect, float zn, float zf){
		Matrix m = new Matrix();
		float yScale, xScale;
		
		yScale =  1 / (float) Math.tan(fovy / 2);
		xScale = yScale / aspect;
		
		m.getMatrix()[0][0] = xScale;
		m.getMatrix()[1][0] = 0;
		m.getMatrix()[2][0] = 0;
		m.getMatrix()[3][0] = 0;

		m.getMatrix()[0][1] = 0;
		m.getMatrix()[1][1] = yScale;
		m.getMatrix()[2][1] = 0;
		m.getMatrix()[3][1] = 0;

		m.getMatrix()[0][2] = 0;
		m.getMatrix()[1][2] = 0;
		m.getMatrix()[2][2] = zf / (zf - zn);
		m.getMatrix()[3][2] = 0;

		m.getMatrix()[0][3] = 0;
		m.getMatrix()[1][3] = 0;
		m.getMatrix()[2][3] = -zn * zf / (zf - zn);
		m.getMatrix()[3][3] = 1;
		
		 return m;
	}
	
	public Matrix Translation(Vektor v){
		Matrix m = new Matrix();
		
		m.getMatrix()[0][3] = v.getX();
		m.getMatrix()[1][3] = v.getY();
		m.getMatrix()[2][3] = v.getZ();
		
		 return m;
	}
}

Damit dürfte ich das von dir Beschriebene ja eigentlich verwirklicht haben, oder fehlt mir da wieder etwas an Verständnis? ???:L
 

rme

Top Contributor
Es funktioniert, weil du die set-Methoden des Vertex-Objekts nur im Konstruktor aufrufst. Lies mal meinen Nachtrag - du benutzt die Vertex-Klasse nur lesend, deshalb ist es kein Problem. Falls du aber mal setX() oder so aufrufst, geht es kaputt, weil alle Alias-Variablen davon dann betroffen sind. Also wenn du zwei Vertex-Arrays hast, wobei eines mit copyOf vom anderen kopiert wurde, sorgt ein setX() auf ein Element dafür, dass das im anderen Array auch betroffen ist.

Schnellste Lösung: mach die Set-Methoden private, dann ist deine Vertex-Klasse nach außen unveränderlich und du hast solche Probleme nicht :)
 

Tiding

Aktives Mitglied
Na, jetzt kommt wohl das größte noch zu bewältigende Problem: Das Verständnis der Funktionsweise einer Grafikkarte, na viel Spass mir ;)!
 

Neue Themen


Oben