Java ID/Name von Objekten angeben lassen

Nynex

Ensign
Registriert
Jan. 2011
Beiträge
236
Hallo alle zusammen! :)

Ich sitze gerade an einem 2D Java Spiel, dass ich nach einem Tutorial auf Youtube aufgebaut habe.
Wer möchte, kann sich ein von mir erstelltes Video dazu ansehen: http://www.youtube.com/watch?v=SadBxKaN19g

Ich bin jetzt an einem Punkt angelangt, wo es darum geht Items im Spiel aufheben zu können. (Was auch im Video schon zusehen ist) Dazu habe ich mir eine Klasse "PickableItem" erstellt und die funktioniert so weit super! Ich kann im Spiel einen Gegenstand hinzufügen und wenn ich über ihn laufe (Die X/Y Koordinaten des Spielers und des Items übereinstimmen) kann ich ihn mit "I" aufheben. Das Problem ist jedoch, wenn ich zwei Gegenstände, sprich ein weiteres Objekt der Klasse "PickableItem" erzeuge, kommt das Spiel jetzt natürlich durcheinander. Denn der Befehl zum "einsammeln" des Gegenstandes lautet "Level.removeEntitiy(Game.Rock)" (Rock ist natürlich der Name des Items, den ich vorher angegeben habe).
Damit ich aber nun mehrere Objekte der selben Klasse aufheben kann, muss jedes Objekt eine individuelle ID (In diesem Falle halt "Rock" etc.) bekommen. Ich möchte es nun schaffen, dass wenn ich über den Gegenstand laufe mir der Name bzw. die ID des Objektes wiedergegeben wird und der Befehl dann lauten kann "Level.removeEntitiy(Game.ID)". Ich hoffe es ist verständlich was ich meine. Denn um mehrere Items "einzusammeln" kann ich ja nicht jedes mal manuell den Namen des aufzuhebenden Gegenstandes angeben.

Es wäre super, wenn mir da jemand weiterhelfen könnte! :)

Grüße, Daniel
 
Füge der Klasse PickableItem doch einfach ein Attribut hinzu. Etwa in der Art:
Code:
public class PickableItem {
private int id;
private static int countingID = 0;

public PickableItem() {
id = countingID;
countingID++;
}

public int getID() {
return id;
}

}

Bei jeder neuen Instanz des Objekts wird ihm eine neue ID zugewiesen. Anschließend wird die Klassenvariable countingID hochgezählt, sodass bei der nächsten neuen Instanz eine garantiert nicht verwendete ID zugewiesen wird.

Der Zugriff funktioniert per PickableItem.getID().

Das Spiel ist eine tolle Idee! :)
 
Warum implementierst Du nicht hashcode() und equals() und verwendest level.remove(PickableItem) um Objekte zu entfernen?
 
@ e-Laurin
Danke für die Idee! :)
Nur leider funktioniert das so nicht. Die Methode "PickableItem.getID()" wird nämlich von einer anderen Klasse aus aufgerufen. Dadurch muss die Methode "static" sein. Und dann müssen auch die Werte darin static sein. Und am Ende verändert sich der Wert nicht mehr, da er ja static ist. Wahrscheinlich gibt es auch dafür ne Lösung aber ich habe gerade ein Brett vorm Kopf!
 
static-Werte können sich immer ändern, es sein denn, sie sind als final deklariert (siehe e-Laurins Beispiel).

So wie du das schreibst, klingt das so also ob das (Basis-)Programm keine oder kaum Objekte verwendet bzw. nicht dafür ausgelegt ist. Und nur weil "PickableItem.getID()" von einer anderen Klasse aus aufgerufen wird, muss diese Methode nicht static sein. Die Aufrufende Klasse muss nur auf eine Instanz von PickableItem zugreifen können. Ohne den Code/deine Programmstruktur zu kennen ist es natürlich schwer Tipps zu geben.
 
Du kannst die Methode ja auch anders nennen, zB getIdentifier(). ID ist die Abkürzung von Identifier. Sie muss ja nicht unbedingt getID() heißen.
Was Darlis sagt, stimmt. Zeig uns doch einfach, wie die Klasse aussieht. Vielleicht ist ja auch schon etwas passendes für deine Zwecke darin vorhanden.
 
Ok dann hier mal der nötige Code dazu:

1. Es gibt die Klasse "Game". Das ist praktisch die Hauptklasse, wo ich ein PickableItem hinzufüge.
Das sieht dann da so aus: (Ich füge hier mal 2 hinzu.)
Code von Klasse "Game":

Code:
public static PickableItem Rock;
public static PickableItem Rock2;
// wie man sieht ist es dort schon als static beschrieben.
Rock = new PickableItem(level, 400, 400);
Rock2 = new PickableItem(level, 410, 410);  
//level gibt an in welchem level das Objekt hinzugefügt wird. 
//Die nachfolgenden Zahlen sind die Koordinaten x und y.
level.addEntity(Rock);
level.addEntity(Rock2);

2. Nun sind die Objekte hinzugefügt und sie checken ob die X und Y Koordinaten mit dem Player übereinstimmen:
Code von Klasse "PickableItem":

Code:
public void checkEnvironment(InputHandler input) {
    if(Game.player.x == x && Game.player.y == y) {
    		pickUp = true;
    		} else {
    		pickUp = false;
    }
}

3. Und die Klasse "GameEvents" reagiert entsprechend:
Code von Klasse "GameEvents":

Code:
if (PickableItem.pickUp == true) {  
		Font.render("b", screen, x + 72, y + 44, Colours.get(-1, 135, -1, 000), 1); 
//Hier wird das im Video zu sehende "I" gezeichnet
		if (input.pickUp.isPressed()) {
			level.removeEntity(Game.??????);
			PickableItem.pickUp = false;
		}
	}

Dort wo die vielen Fragezeichen sind soll der Ausdruck hin der angibt, um welches Objekt es sich genau handelt. Es gibt ja nunmal 2 (Rock und Rock2) Will ich "Rock" löschen würde da stehen:

Code:
level.removeEntity(Game.Rock);

Da ich aber nicht immer nur "Rock" einsammeln will sondern auch "Rock2". Soll wenn ich über "Rock2" gehe dort "Rock2" stehen. Das dies nur über Umwege klappt, ist mir schon klar. Aber ich habe keinen Plan ob das so überhaupt irgendwie geht. Vielleicht gehe ich hier grundliegend falsch ran. Denn wenn ich 2 Objekte hinzufüge und wie im Video darüber laufe wird mir nicht bei jedem Objekt das blaue "I" angezeigt... Das Programm kommt komplett durcheinander was, was ist.
Ergänzung ()

@ e-Laurin Nein :D Ich meine das nicht so, dass es diese Methode schon gibt. Sie wird bloß von wo anders aus aufgerufen. Es ist egal wie ich sie nenne, das Problem bleibt das selbe.
 
Zuletzt bearbeitet:
Code:
public static PickableItem Rock;
public static PickableItem Rock2;
Wenn du im Spiel 200 Steine hast, meinst es wäre praktikabel diese Zeile 200 mal zu kopieren? ;)
Nimm lieber so etwas:
Code:
public static List<PickableItem> pickableItems = new ArrayList<PickableItem>();
...
rocks.add(new PickableItem(level, 400, 400));
rocks.add(new PickableItem(level, 410, 410));

Wenn du pickUp zum Typ PickableItem änderst und pickUp = this; setzt, kannst du über level.removeEntity(PickableItem.pickUp); das gerade aufgehobene Objekt entfernen.
Das ist zwar keine optimale Lösung, wäre aber der erste Schritt um das Programm etwas objektorientierter und damit dynamischer zu machen.
 
Code:
public static PickableItem Rock;
public static PickableItem Rock2;


Problem gefunden. Eigentlich sind das sogar mehrere Probleme:
1. Du hast die als static markiert. Wozu eigentlich?
2. Aus 1. folgend: Ich bin mir ziemlich sicher, dass du ein paar Steine mehr pro Level verteilen willst. Eigentlich weißt du ja jetzt noch nicht mal, wie viele Steine es am Ende sein werden. Willst du jeden auf diese Art per Hand programmieren? Da wird man ja blöde. ^^



Was man ändern kann:
Ich empfehle die Nutzung einer LinkedList. Das ist eine Datenstruktur ähnlich eines Arrays, allerdings gibt es keine Maximalgröße. Listen können beliebig viele Elemente bzw. Objekte enthalten.
zB so:
Code:
public LinkedList<PickableItem> rockList; // deklarieren
rockList = new LinkedList<PickableItem>(); // initialisieren

PickableItem rock = new PickableItem();
rockList.add(rock); // hinzufügen
rockList.add(new PickableItem()); // hinzufügen

rockList.remove(rock); // bestimmtes Element aus der Liste entfernen
rockList.clear(); // alle Elemente aus der Liste entfernen
<PickableItem> ist ein Generic, dass der LinkedList nur erlaubt, PickableItems zu enthalten. Alle anderen Typen werden gesperrt. Ohne das Generic kann man in so einer Liste sonst alles mögliche reinpacken.



Die Arbeit mit Klassen ist dir klar, aber die fehlt noch eindeutig Wissen über Objektorientierung. Das wäre jetzt deine nächste Bausstelle. Hast du OO erst mal drauf, wirst du fast nie wieder irgendwo static schreiben. ;)


Edit:
Darlis, menno. Du hast quasi genau dasselbe geschrieben wie ich. ^^
 
Zuletzt bearbeitet:
@ Darlis: Wie? pickUp ist doch nur ein boolean. Wie soll ich das auf ein PickableItem abändern?
Ergänzung ()

Ja das mit der Liste ist super ;) Aber das mit den Steinen war ja erstmal ein Test... darum hätte ich mich noch gekümmert ;) Und ich habe sie als static makieren müssen weil Eclipse angefangen hat zu meckern, als ich das hier schreiben wollte:

Code:
Rock = new PickableItem(level, 400, 400);
 
Das geht indem du boolean durch PickupItem ersetzt:
Code:
public class PickupItem {

	public static PickupItem pickUp  = null;

	public void checkEnvironment(InputHandler input) {
		if(Game.player.x == x && Game.player.y == y) {
			pickUp = this;
		} else {
			pickUp = null;
		}
	}
}

Darlis, menno. Du hast quasi genau dasselbe geschrieben wie ich. ^^
Aber du hast es besser beschrieben. :)
 
Zuletzt bearbeitet: (Variablenname vergessen)
Okey super das hat schonmal geklappt! :) Allerding muss ich immer erst "Rock2" aufheben bevor es möglich ist "Rock" aufzunehmen... Woran liegt das? Und wenn ihr mir jetzt noch sagen könntet wie ich Informationen aus dem jeweiligen Objekt rauslesen kann wäre das der Hammer! Sprich wenn ich noch eine Münze ins Spiel hinzufüge, soll beim Aufsammeln davon das Geld des Players um einen erhöht werden und wenn es ein Stein ist, den ich aufnehme, soll dieser ins Inventar getan werden. (Wie das dann von statten geht ist ja erstmal unwichtig.) Nur ich will Werte aus dem Objekt rauslesen wenn z.B der Name "Coin" ist passiert das und das und wenn der Name "Rock" ist passiert das und das.
 
Dann musst du, wie e-Laurin schon gesagt hat, dich etwas mit objektorientiertem Programmieren auseinandersetzen. Einem Objekt ein Attribut zu geben, hast du in gewisser Weise schon gemacht, dir ist das nur nicht klar. Vor allem musst du dir den Unterschied zwischen static und nicht-static Variablen/Methoden anschauen.
 
Alles klar, aber das Problem, dass ich immer erst das eine aufheben muss bevor ich das andere aufheben kann ist noch immer da. Das was zuletzt hinzugefügt wurde via "pickableItems.add(...);" Wird auch als erstes aufgehoben... Macht ja auch irgendwie Sinn. Aber das ist Mist für die Spieldynamik/Logik.
 
Woher weist du denn, dass der falsche Stein aufgehoben wird? Du kannst die ja nicht voneinander Unterscheiden, außer von deren Koordinaten.
 
Also erstmal Lob für das, was du bereits geschafft hast. So weit kommen die meisten gar nicht erst, weil sie vorher die Lust verlieren. Du darfst aber nicht den Punkt verpassen, an dem du auch ein bisschen auf die "Sauberkeit" (damit meine ich z.B. die Architektur) deines Codes achtest, denn zum einen schleifst du dir damit schlechten Stil ein und zusätzlich wird dein Spiel im Laufe der Zeit immer schwerer anzupassen.

Der direkte Zugriff auf public static fields, sollte zum Beispiel eher die Ausnahme als die Regel sein, weil es die einzelnen Teile deines Codes zu sehr verkoppelt, dir keine Möglichkeit gibt auf die Zugriffe noch Logik anzuwenden und die Schnittstellen der Objekte nach außen hin aufbläht. Die Innereien deiner Klassen sollten sie für sich behalten und nur das Nötigste nach außen lassen.

Dein zuletzt genanntes Problem basiert so wie ich das sehe darauf, dass du keine Kollisionserkennung zum Aufheben nutzt, wie das sonst so üblich ist. Aber gut, das geht natürlich auch anders. In deinem Fall könntest du zum Beispiel in dem Moment in dem der Pickup-Knopf gedrückt wird über sämtliche aufhebbaren Gegenstände drüber iterieren (dazu sollten sie in einer Liste sein, wie bereits vorgeschlagen) und dann könntest du anhand der Position deines Spielers und der Position des aktuellen Listenelements durch einen Vergleich feststellen, welcher Gegenstand am nächsten am Spieler dran ist. Den hebt er dann auf und der Gegenstand wird aus der Liste entfernt. Das ist natürlich erstmal eine Knobelaufgabe für dich, aber ich glaube anders kannst du das auf der gegebenen Grundlage nicht lösen.
 
@ Tumbleweed Hey das ist eine gute Idee, zumal das spiel dann nicht Nonstop guckt ob gerade was in der Nähe ist. Da freut sich die Performance. Denn wie du schon sagst ist das richtige Design wichtig bei solchen klomplexen Dingen. Naja das Ganze war ja erstmal ein Test. Um die Schönheit des Codes kümmer ich mich immer erst dann, wenn etwas soweit funktioniert. Dann versuche ich das Ganze noch zu optimieren. Sprich ich mach das immer in Etappen. Ich werde mich mal daran setzen, villt. schaffe ich das noch heute ;) Ich werde mich auf alle Fälle melden, ob es geklappt hat! Vielen Dank für diese super Hilfe! :)

Grüße, Daniel
Ergänzung ()

So jetzt bin ich mal wieder auf ein Problem gestoßen. Es geht um diese static Sache. Ich will jetzt aus einer anderen Klasse heraus die Methode "checkEnvironment()" aufrufen.

Code:
 public void checkEnvironment(InputHandler input) {
    if(Game.player.x == x && Game.player.y == y) {
    		pickUp = true;
    		} else {
    		pickUp = false;
    }
}

Jetzt meckert Eclipse und schlägt mir vor die Methode static zu machen. Warum? Ich meine das ist doch bloß nur ein simpler Methodenaufruf?!
 
Du kannst die Methode "checkEnvironment" nur über eine Instanz der Klasse, in der die Methode definiert ist, aufrufen.
Anscheinend benutzt du aber keine Instanz der Klasse (wird mit new erzeugt), sondern versuchst die Methode immer direkt über den Klassennamen aufzurufen, was nur mit statischen Methoden funktioniert.

Falls du Lust und Zeit hast dich etwas mehr in das Thema einzulesen:
http://openbook.galileocomputing.de/javainsel/index.html

Wenn du Kapitel 1-5 durch hast, sollten breites einige Fragen geklärt sein.
 
Zuletzt bearbeitet:
Was ist denn ein "simpler Methodenaufruf"?
Schau' dir doch mal die Methode an: Sie greift auf Instanzvariablen zu, also Variablen die zu einem Objekt gehören (hier: x und y). Wenn du die Methode static machst, woher soll dann dass Programm wissen, aus von welchen Objekt x und y geholt werden sollen? Du müsstest dann x und y ebenso als static deklarieren, wie pickUp. Dann kannst aber auch nicht einfach mit Objekten arbeiten, da jedes Objekt die selben Attribute haben wird und somit sinnlos ist.
 
@ Darlis eben das ist ja mein Problem. Ich hatte das nur für einen Moment vergessen, dass ich nicht mit einer Instanz von "PickableItem" arbeite. Jedoch muss die Methode von wo anders aus aufgerufen werden. Ich werde das schon irgendwie hinbekommen! ;)

@ JesusRulez Danke ich werde es mir mal ansehen :)
 
Zurück
Oben