Java Bucla principală a jocului Java (Programare, Java, Motor De Joc)

Ieșiți de pe peluza mea a intrebat.

Scriu o buclă de joc, am găsit codul în exemplul de mai jos aici. M-am uitat și la alte moduri de a face o buclă de joc, cum ar fi de la acest articol. Nu am reușit să fac să funcționeze niciuna dintre acestea, totuși. Așa că am rămas cu cel din primul link.

Ce aș vrea să știu:

  • Este modul în care am scris bucla jocului meu o modalitate bună de a face acest lucru?
    • Aveți vreo sugestie?
  • Ar trebui să folosesc Thread.sleep(); în bucla mea de joc?

Aici este codul meu actual:

public void run(){
    long lastLoopTime = System.nanoTime();
    final int TARGET_FPS = 60;
    final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;
    long lastFpsTime = 0;
    while(true){
        long now = System.nanoTime();
        long updateLength = now - lastLoopTime;
        lastLoopTime = now;
        double delta = updateLength / ((double)OPTIMAL_TIME);

        lastFpsTime += updateLength;
        if(lastFpsTime >= 1000000000){
            lastFpsTime = 0;
        }

        this.updateGame(delta);

        this.repaint();

        try{
            Room.gameTime = (lastLoopTime - System.nanoTime() + OPTIMAL_TIME) / 1000000;
            System.out.println(Room.gameTime);
            Thread.sleep(Room.gameTime);
        }catch(Exception e){
        }
    }

Comentarii

  • Am o postare despre buclele de joc și despre cum să le păstrez constante aici. –  > Por Lee Fogg.
2 răspunsuri
William Morrison

În cele din urmă veți dori să treceți la ceva de genul LWJGL, , dar permiteți-mi să subliniez, continuați să faceți ceea ce faceți aici pentru moment. Vă va învăța fundamentele.

Bună treabă cu bucla ta. Arată bine, permiteți-mi să vă ofer câteva indicații:

  • Repaint nu va reda ecranul imediat. Îi spune RepaintManager să redea când este gata. Utilizați invalidate paintImmediately în schimb. paintImmediately va bloca execuția până când componenta a fost redesenată, astfel încât să puteți măsura timpul de redare.

  • Thread.sleep are de obicei o derivă de câteva milisecunde. Ar trebui să o folosiți pentru ca bucla dvs. să nu folosească prea mult CPU, dar asigurați-vă că înțelegeți că dacă dormiți 10 milisecunde, s-ar putea să dormiți 5 milisecunde sau s-ar putea să dormiți 20.

  • În cele din urmă:

    double delta = updateLength / ((double)OPTIMAL_TIME);
    

    Dacă updateLength este mai mică decât OPTIMAL_TIME, nu apelați update. Cu alte cuvinte, dacă delta este mai mică de unu, nu se face actualizarea. Acest tutorial explică de ce mai bine decât aș putea eu vreodată.

Comentarii

  • Vă mulțumim! Dacă folosesc doar invalidate, nu se întâmplă nimic. Ar trebui să se întâmple asta? –  > Por Nu te mai apropia de peluza mea.
  • Ups, nu, am greșit. Lucrez zilnic cu C# și Java. M-am încurcat. Editat. Încercați paintImmediately în loc –  > Por William Morrison.
  • A trebuit să mut paintImmediately după ce try-catch altfel face o redare ciudată… ar trebui să fac asta? –  > Por Dă-te jos de pe peluza mea.
  • paintImmediately trebuie să fie rulat pe firul principal. Puteți fie să rulați tot codul pe firul UI, update și rendering (ceea ce vă recomand), fie să folosiți repaint. –  > Por William Morrison.
  • Acesta este firul principal. Cred că… Aici este clasa completă: github.com/TheColorRed/JGame/blob/master/src/JGame/Room/… –  > Por Dă-te jos de pe peluza mea.
Robert E Fry

În general, este o buclă bună, dar lipsesc câteva aspecte din ceea ce am constatat din experiență că este cea mai bună buclă.

În cele din urmă veți dori să vă mutați la LWJGL sau un alt API de joc Java, dar pentru moment, învățați elementele de bază ale modului în care funcționează buclele de joc și ce se potrivește cel mai bine nevoilor dumneavoastră.

  • În primul rând, ca răspuns la unul dintre punctele dumneavoastră, nu. Vă veți descurca mai bine dacă stați departe de

    Thread.sleep()

    acesta se poate abate de la timpul real pe care l-ați setat să doarmă.
    De exemplu, dacă îl setați să doarmă timp de 10 milisecunde, programul ar putea dormi între 5 și 20 de milisecunde.

  • A doua problemă pe care o văd imediat este că nu aveți nicio modalitate de a opri bucla de joc pentru o metodă stop() personalizată. Încercați

    boolean running = true;
    while (running) {
    // Codul dvs. aici //
    }

  • În al treilea rând, este posibil să doriți să luați în considerare schimbarea modului în care utilizați variabila delta. Modul din codul de mai jos ar putea fi o utilizare și o construcție mai bună pentru dvs.

    Acesta este un exemplu de buclă de joc pe care o folosesc în programele mele:

    @Override
    public void run() {
    
    long initialTime = System.nanoTime();
    final double timeU = 1000000000 / UPS;
    final double timeF = 1000000000 / FPS;
    double deltaU = 0, deltaF = 0;
    int frames = 0, ticks = 0;
    long timer = System.currentTimeMillis();
    
        while (running) {
    
            long currentTime = System.nanoTime();
            deltaU += (currentTime - initialTime) / timeU;
            deltaF += (currentTime - initialTime) / timeF;
            initialTime = currentTime;
    
            if (deltaU >= 1) {
                getInput();
                update();
                ticks++;
                deltaU--;
            }
    
            if (deltaF >= 1) {
                render();
                frames++;
                deltaF--;
            }
    
            if (System.currentTimeMillis() - timer > 1000) {
                if (RENDER_TIME) {
                    System.out.println(String.format("UPS: %s, FPS: %s", ticks, frames));
                }
                frames = 0;
                ticks = 0;
                timer += 1000;
            }
        }
    }
    

Comentarii

  • Bună Știu că este o postare/întrebare veche , dar ce realizează metoda getInput()? Și de ce folosești un thread în loc de un Timer? este mai bine să folosești un Thread? –  > Por navy1978.
  • 1. Funcția „getInput()” doar separă procesarea intrărilor utilizatorului de logica principală a jocului (adică apăsați la dreapta, jocul se înregistrează la dreapta în „getInput()”, obiectul se mișcă la dreapta în „update()”). 2. Folosesc un Thread în loc de un Timer pentru că separă întreaga logică a jocului de procesele de fundal, astfel încât va rula pe un alt nucleu de cpu. Aruncați o privire la acest lucru pentru mai multe informații despre thread-uri: tomshardware.co.uk/forum/306079-28-what-core-thread –  > Por Robert E Fry.
  • Mulțumesc pentru răspuns Am făcut o mulțime de teste și folosind un Thread în loc de Timer (executând la 60FPS) face ca aplicația mea să folosească mult mai mult din CPU pe MacBook Pro (3,1 GHz Intel Core i7 cu 16GB de ram). Foarte ciudat, voi încerca să fac alte investigații, dar pentru moment voi folosi Timer-ul. Pe de altă parte, mi-ați dat o idee bună cu metoda getInput. 😉 –  > Por navy1978.
  • Ce înseamnă RENDER_TIME? –  > Por Jason210.
  • ‘RENDER_TIME’ este un indicator FPS – adică o variabilă booleană statică care activează dacă interfața de comandă (cmd) trebuie să tipărească cadrele redate pe secundă la fiecare secundă. Prin urmare, „frames” și „ticks” sunt contoare pentru cadrele desenate și, respectiv, pentru actualizările de joc efectuate, resetate la fiecare secundă după ce sunt eventual afișate pe interfața de comandă. –  > Por Robert E Fry.