ich hätte eine Frage an euch.
Ich programmiere gerade mein erstes Game in Slick-2D (BasicGame).
Jetzt habe ich folgende Situation.
In der Update-Methode wird ein 2D-Array abgefragt mit einer verschachtelten For-Schleife.
Jetzt wird die Render()-Methode erst wieder aufgerufen, wenn die For-Schleifen fertig ausgeführt wurden.
Nun müsste ich aber die Render-Methode nach jedem einzelnen Schleifendurchlauf aufrufen.
Er hängt in den For-Schleifen fest und aktualisiert erst die render-methode, wenn er die Schleifen durchgearbeitet hat.
Das ist bei einem Spiel ein Problem, bei anderen Anwendungen ist das meistens nicht so schlimm.
Hängt natürlich immer vom Einsatzgebiet der Software ab.
nur ‚in jedem Schleifendurchlauf auch rendern‘ wird dir nichts bringen, falls du nicht auch eine Zeitverzögerung dazu einplanst,
wenn sonst alles innerhalb von 10ms abgearbeitet wäre ist ziemlich egal ob die Zwischenschritte gemalt oder nicht…
meiner Ansicht nach brauchst du eine eigene Steuerung des Ablaufs, auch zeitlich,
wieviel Framework Xy dir dabei helfen kann und soll ist deine Sache,
meiner Empfehlung nach besser sowieso so wenig Abhängigkeit wie möglich,
wäre es nicht günstig, wenn das Programm mit begrenzten Anpassungen auch mit Java Swing und anderem laufen könnte?
nun gut, verzichtbares Ziel
jedenfalls, von Swing-Sicht aus gesehen braucht es z.B. einen Thread, der periodisch repaint() und evtl. etwas zur Programmsteuerung aufruft,
wie es bei Slick aussieht, wie oft von alleine update() + render() drankommt oder wer sich darum kümmern kann/ soll, das wirst du hoffentlich wissen oder herausfinden können,
der zu zeichnende Zustand sollte wie schon gesagt wurde vom Zeichnen unabhängig als Datenstand hinterlegt sein,
verändert eben nach zeitlichen Verlauf:
Programm beginnt z.B. in Sekunde 0.0, alle 0.1 sec soll ein weiteres Array-Element grün werden
,
wann immer update drankommt, vielleicht öfter innerhalb 0.1 sec, vielleicht durch irgendeine Verzögerung oder Fenster ausgeblendet erst nach 0.5 sec mal wieder,
→ immer Zeit feststellen, Soll-Zustand bestimmen, Ist-Zustand entsprechend einrichten, vielleicht gar nichts zu tun im update(), vielleicht mehrere Elemente zu ändern,
oder ganz simples Konzept immer genau ein weiteres Array-Element auf grün, wie schon genannt wurde,
Hauptsache am Ende dieser Methode zufrieden mit Array-Stand,
render() muss es dann nur noch malen,
so ist es ein allgemeines überall funktionierendes Konzept
ich empfehle bei dieser Gelegenheit das Quaxli Spiele-Tutorial,
zwar für Swing, aber wie gesagt könnten manche Konzepte universal passen
Wenn man bei dem suggestiven Beispiel von oben bleibt, wo in der update-Methode irgendwelche Objekte in einer For-Schleife “grün” gemacht werden, sollte die Lösung, soweit ich das bisher sehe, tatsächlich recht nah an dem sein, was ich als Pseudocode geschrieben habe.
Nochmal etwas weniger Pseudo und etwas mehr Code:
Rectangle rectangles[] = ...; // Das wären ggf. "Entity"-Instanzen
int numberOfGreenOnes = 0;
int msUntilOneTurnsGreen = 500;
int msSinceLastOneTurnedGreen = 0;
public void update(GameContainer gc, int delta) {
msSinceLastOneTurnedGreen += delta;
if (msSinceLastOneTurnedGreen > msUntilOneTurnsGreen) {
if (numberOfGreenOnes < rectangles.length) {
rectangles[numberOfGreenOnes].setColor(GREEN);
numberOfGreenOnes++;
}
msSinceLastOneTurnedGreen = 0;
}
}
Wenn das dann regelmäßig aufgerufen wird (und das wird bei Slick ja wohl in einer Art “Game Loop” gemacht), dann sollte alle 500 millisekunden ein Rechteck grün werden.
habe es nun so gelöst, das ich statt der For-Schleife einfach alles mit If-Anweisungen mache.
Nun habe ich aber ein Problem, was ich einfach nicht verstehe!
Wenn ich meine For-Schleife verwende, dann wird alles ausgeführt was im Source steht, nur wenn ich die For-Schleife durch die If-Anweisung ersetze, wird plötzlich folgender Teil des Source codes nicht mehr ausgeführt:
if(array[x][y] != null){
//hier gibt es noch eine if & drei else if-Anweisungen die bei einer Kollision des Spielers mit der Map aufgerufen werden.
Kollisionserkennung:
if(){
}
else if(){
}
Usw.
}
Nur kommt er in: „if(array[x][y] != null)“ nie rein, somit werden die If-Anweisungen für die Kollisionserkennung ebenfalls nie aufgerufen.
Ich weiß nicht warum, es kommt keine Fehlermeldung in der Konsole??
Mit der For-Schleife funktioniert es mit der If-Anweisung nicht mehr.
Alles vor: if(array[x][y] != null) wird einwandfrei ausgeführt, danach wird nichts mehr ausgeführt.
Die If-Anweisungen funktionieren, da ich keinen IndexOutofBoundError bekomme.
Habe mir X & Y in der Konsole ausgeben lassen und man sieht das er Richtig zählt.
nein, weder Slick2D noch 99% anderer Frameworks machen komische Dinge mit Java…,
überall gibt es natürlich gewisse Besonderheiten, die korrekt zu bedienen sind,
siehe letzte Absätze unten
wann x und y und der Arrayinhalt geändert wird, warum zu einem bestimmen Zeitpunkt der Inhalt null oder nicht sein sollte, ist nicht zu erkennen,
die Java-Mechanismen dazu sind aber einfach,
gib direkt vor der Abfrage x und y aus, wie du sagst es schon zu tun, und auch array[x][y],
ist das null oder nicht? allein davon hängt ab ob das if betreten wird,
nicht mehr und nicht weniger an Mysterium hier…
manchmal kann es kompliziert werden wenn z.B. mehrere Objekte einer Klasse in Verwendung sind,
in Objekt A wird das Array von wem auch immer gefüllt, Objekt B z.B. für Slick-Update verwendet, dort das Array noch verwundert leer, auch in den Ausgaben,
dann kann man die Ausgaben dazu, bei Befüllen + Abfrage, komplexer machen, Hashcode des Objektes mitausgeben usw.,
oder in Konstruktor der Klasse loggen, wieviele Objekte erzeugt usw.
für Korrektur es eben besser machen, genau schauen wo Objekte der Klasse erstellt und übergeben werden usw.
aber nur eine mögliche Fehlerursache von vielen,
es ist grundsätzlich immer der ganze Programmablauf auf korrektes Vorgehen zu überblicken,
idealerweise mit vielen Ausgaben im Programm unterstützt (im Fehlerfall)
Joa, ganz allgemein: Wenn mehrere Threads im Spiel sind (no pun intended), dann können “komische” Dinge passieren, die unerklärlich erscheinen - im ersten Moment. Aber ein System.out.println könnte da schon helfen…
nun habe ich das Problem gefunden!
Das Problem ist folgendes, die If-Anweisungen funktioniert benötigen aber zu viel Zeit zum hochzählen von X & Y.
Das heißt bis if(array[x][y] != null) einmal „true“ ist, ist der Spieler bereits aus dem Bild des Laptops verschwunden. Ich dachte am Anfang, das er nicht in die „if(array[x][y] != null)“ Anweisung eintritt.
Das tut er nur dauert das viel zu lange.
Man sieht auch in der Konsole, das die If-Anweisungen sehr langsam zählen.
Bei der For-Schleife geht das super schnell!
Deshalb funktioniert es auch mit der For-Schleife.
Erst wenn das hier „ture“ ist: if(array[x][y] != null) wird die Kollision abgefragt.
Nun stellt sich mir die Frage, warum die If-Anweisung so viel langsamer ist als die For-Schleife??
Ich habe schon versucht die update-Methode von Slick schneller zu machen mit: game_container.setMinimumLogicUpdateInterval(1000000000);
Bringt nicht viel was.
Das heißt, ich muss da einen ganz anderen Lösungsansatz finden.
Das klingt alles etwas konfus. Etwas mehr Code könnte helfen. Compilierbar wäre gut, notfalls mit Dummy-Klassen, aber eben so, dass man erkennt, was genau die Frage ist.
Ich würde dir empfehlen dich nochmal damit zu beschäftigen wie Spieleschleifen funktionieren.
Du berechnest in jedem Durchgang
[Loop] --> update() --> render() --> [Loop]
Schrittweise den Zustand aller Objekte neu, und dann zeichnest du den Zustand dieser Objekte.
Kollisionsabfragen gehören natürlich in einen Berechnungsschritt (deine For-Schleife), dann ist Marcos Code in dem Fall natürlich murks mit den If-Anweisungen.
Du musst stattdessen den berechneten Status in der For-Schleife zwischenspeichern (am besten in den Objekten selbst) und in der render-Methode wieder abrufen und zeichen.
Aber es ist nicht ganz klar was du machen möchtest. An der Stelle sei auch gesagt, wenn du klar kommunizierst was du machen möchtest und entsprechende Code Ausschnitte von update und render zeigst, dann können wir dir auch helfen und du kannst davon nur lernen.
@Override
public void init(GameContainer game_container) throws SlickException {
// Hier werden die Sprite-Grafiken erstellt.
// Genauso das Array für die Blöcke der Map.
// Diese Methode wird von Slick nur ein einziges mal beim Programmaufruf
// ausgeführt (Initialisierung der Daten)!
gravity = true;
}
@Override
public void update(GameContainer game_container, int arg1) throws SlickException {
// Hier wird die Gravitation aktiviert wenn "gravity == true", diese
// Variable ist am Anfang ture.
// Diese Methode wird von Slick in bestimmten Zeitabschnitten aufgerufen,
//diese Zeitabstände habe ich bereits auf minimum verkleinert, ohne Erfolg!
if(gravity == true){
gravity();
}
collision_detection();
}
@Override
public void render(GameContainer game_container, Graphics g) throws SlickException {
// Hier werden die Blöcke der Map gezeichnet, einfach nur quadratische Blöcke.
// Sonst passiert hier nichts.
// Diese Methode ist aber nicht der Grund für das langsame ablaufen der If-Anweisung.
// Diese Methode wird von Slick so oft wie möglich aufgerufen!
draw_map(g);
}
public void collision_detection(){
// Mit der For-Schleife läuft alles schnell genug ab, nur wird die Map
// erst am ende des gesamten durchlaufs upgedatet.
// Mit der If-Anweisung sollte nach jedem durchlauf die Map neu gezeichnet
//werden, leider braucht die If viel zu lange für einen Durchlauf.
// Es wird hier ein 2-Dimensionales Array abgefragt:
//for(int x=0; x<=39; x++){
//for(int y=0; y<=99; y++){
if(x == 39){ // 40 Felder des Arrays.
x=0;
}
else{
if(y == 99){ // 100 Felder des Arrays.
y=0;
x++;
}
else{
System.out.println("Y =" + y);
System.out.println("X =" + x);
// Array auf leere Felder prüfen:
if(array[x][y] != null){
System.out.println("Prüfung erfolgreich!");
// Abfragen, ob eine Kollision statt:
if(.......){
gravity = false; -> Kollision erkannt!
}
else {
gravity = true; -> Keine Kollision erkannt!
}
}
y++;
}
}
}
Wie bereits erwähnt, passt die durchlaufszeit der If nicht.
Warum ist jetzt die Frage.
Mit dem vagen “zu lange brauchen” meinst du jetzt wohl, dass collision_detection (was collisionDetection oder vielleicht eher detectCollisions heißen sollte) ein paar hundert mal aufgerufen werden muss, damit x++ “oft genug” ausgeführt wurde.
Diese Kollisionserkennung sollte in einer for-Schleife passieren.
(Und bevor du duch beschwerst, dass das ja im Widerspruch zu dem steht, was ich weiter oben geschrieben habe: Lies dir den thread nochmal von Anfang bis Ende durch, und beantworte dann ehrlich die Frage: Ist DIR klar, worum es hier eigentlich geht? Sicher nicht darum, “die render-Methode in jedem Schleifendurchlauf aufzurufen”, sondern eher darum, “irgendein Problem zu lösen”, von dem du glaubtest, dass es durch das Aufrufen der render-Methode gelöst werden könnte. Vermutlich.)