Render-Methode nach jedem Schleifendurchlauf aufrufen in Slick-2D

Hallo zusammen,

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.

Wie geht das bei Slick-2D?

SG

Kannst du in der Render Methode nicht ein zweites mal iterieren?
Code wäre hilfreich und wer ruft die Render Methode auf?

Kenn’ mich zwar mit Slick nicht im Detail aus, aber … da du keinen Code gepostet hast, rate ich mal, dass das sowas ist wie (pseudocode)

void update() {
    for (int i=0;i<10; i++) { 
        rectangle**.setColor(GREEN);
    }
}

void paint() {
    for (int i=0;i<10; i++) { 
        paint(rectangle**);
    }
}

und das Problem ist, dass die die Zwischenzustände sehen willst, aber immer nur den “letzten” Zustand siehst, wo alle Rechtecke grün sind.

Falls das so ist, wäre die Lösung GROB sowas wie

int currentIndex = 0;
void update() {
    rectangle[currentIndex].setColor(GREEN);
    currentIndex++;
}

void paint() {
    for (int i=0;i<10; i++) { 
        paint(rectangle**);
    }
}

Also, der “Zwischenzustand” muss irgendwo liegen, wo er gezeichnet werden kann.

Aber nochmal: Das ist

Pseudocode

Wie man das in Slick “richtig” macht, müßte ich mir auch erst anlesen.

Ja, genau das ist mein Problem.

Wenn ich die zwei For-Schleifen nicht hätte, dann würde es funktionieren.
Nur für ein 2D-Array brauche ich zwei For-Schleifen.

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.

SG

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? :wink:
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 :wink:

Nochmal der disclaimer: Mit Slick2D hab’ ich nocht nichts gemacht.

Aber wenn man sich http://slick.ninjacave.com/wiki/index.php?title=Controlling_Game_Updates und http://slick.ninjacave.com/javadoc/org/newdawn/slick/Game.html#update(org.newdawn.slick.GameContainer,%20int) ansieht, sieht das erstmal recht klassisch und straightforward aus. Man kann z.B. limitieren, wie oft die update-Methode aufgerufen wird. Aber in jedem Fall kriegt man die Millisekunden seit dem letzten Aufruf mit. Und darauf sollte vermutlich das Aktualisieren des Spielzustandes aufsetzen.

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.

Hallo nochmals,

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.

Ich verstehe das ganze nicht?

Hängt das mit Slick zusammen?

SG

Was soll man mit so einem Schnipsel anfangen, im Ernst:

if(array[x][y] != null) {
    System.out.println("it is NOT null, see: "+array[x][y]);
} else {
    System.out.println("it is null, see: "+array[x][y]);
}

Was wird dort ausgegeben? Beantwortet das die Frage?

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…

Naja, sowohl als auch besonders “im Spiel”. Slick2D hat mit Multithreading aber nix am Hut.

Hallo,

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.

SG

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.

1 Like

Es heißt if-Bedingung.

Code umgangssprachlich/natürlichsprachlich zu be-/um-/schreiben ist etwas schwerer, als ihn gerade hier zu posten. Da stimme ich Marco zu.

Schreib einfach hier die for-Schleife mit der if-Bedingung und den array-Anweisungen, quasi wo etwas falsch 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.

Hallo,

hier mal der Code wie ich es gemacht habe:

@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.

SG

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.)

Sorry für die verspätete Antwort.

Habe es gelöst, indem ich einfach alles in die Render-Methode verlegt habe und die Grafik direkt in der
Schleife zeichne.

SG und Danke!