Eine kurze Idee zum 2D-Scrolling



  • Das ist nur mal eine kurze Idee, wie man 2D Scrolling realisieren kann. (Wollte das mal hier rein posten, da es immer mal wieder Fragen dazu gibt.) Zahlenwerte sind nur Beispiele, dienen dem besseren Verständnis und können beliebig ersetzt werden.

    Voraussetzungen/ Vorkenntnisse:
    - 2D Bitmpas darstellen, wie die Darstellung erfolgt (DX, OpenGl...) ist letzendlich egal
    - Clipping, wie Bitmaps am Rand abgeschnitten und die ausserhalb erst garnicht gezeichnet werden, sollte klar sein
    - eine Funktion "zeichneBitmap(Bild*, X, Y)" die obengenanntes macht, sollte schonmal da sein

    Grundidee:
    Eigentlich funktioniert alles wie bei Darstellung ohne Scrolling, allerdings ist die Fläche viel grösser. Anstatt Koordinaten von (0,0) bis (1024,768) könnte man sich z.b. (0,0) bis (16384,16384) vorstellen. Das Scrolling besteht nun einfach darin, das eine obere Ecke für den sichtbaren Bereich festgelegt wird. Man sieht quasi durch den Monitor wie durch ein Fenster auf die Welt, durch die obere Ecke legt man fest welchen Teil der Welt man im Fenster sieht. Wird diese Ecke verändert so ist dies das Scrolling. Die obere Ecke kann im Bereich (0,0) bis (16384-1024,16384-768), ansonsten würde das Fenster (teilweise) ausserhalb der Welt liegen.

    Implementation in OOP:
    Wir haben also nun eine Menge Grafiken (Spielerfigur, Gegner, Umgebung), die alle Koordinaten haben, welche in dem Bereich (0,0) bis (16384,16384) liegen (eventl. auch ausserhalb, wenn sich ein Objekt am Rand befindet). Wir können nicht einfach zeichnen, da diese Koordinaten nicht die vom Bildschirm sind. Am besten wäre es eine Klasse zu erstellen, an die wir die Objekte übergeben und die dann diese so zeichnet, wie man sie durch das Fenster sieht. Diese Klasse brauch natürlich die Koordinaten der oberen Ecke, bevor sie das kann. Mal etwas (Pseudo)-Code:

    class Scroller2D
    {
    public:
        void setzeEcke(int iEckeX, int iEckeY);
        void scrolleBitmap(Bild* bild,int iX, int iY);
    
    private:
        int m_iEckeX;
        int m_iEckeY;
    }
    

    Nun ist es nur noch ein winziger Schritt, wir müssen aus den Koordinaten des Bildes berechnen, wo auf dem Bildschirm es erscheinen muss. Dies ist abhängig davon, wo das Bild ist und an welcher Stelle die obere Ecke ist. Genau genommen interessiert der _Abstand_ des Bildes zur oberen Ecke. Eine kleine "Zeichnung" dazu:

    (0,0)
    +---------------------------------------------+
    I                                             I
    I     (3800,2000)                             I
    I         +--------+                          I
    I         I        I                          I
    I         I  B     I                          I
    I         I        I                          I
    I         +--------+                          I
    I                                             I
    I                                             I
    I                                             I
    I                                             I
    I                                             I
    +---------------------------------------------+
                                      (16384,16384)
    

    Angenommen B ist das darzustellende Bild mit den Koordinaten (4000,2400). Die obere Ecke hat die Koordinaten (3800,2000). Auf unserem Bildschirm hat nun diese obere Ecke die Koordinaten (0,0). Mathematisch gesehen müssen wir die Koordinaten in ein anderes Koordinatensystem umrechnen, was aber eigentlich ganz einfach funktioniert. Wir müssen nur die (3800,2000) von den Koordinaten der Bilder abziehen. Die Koordinaten des Bildes auf dem Bildschirm sind als (4000-3800,2400-2000) = (200,400). Wenn man sich vor Augen hält, das B 200 Pixel neben und 400 Pixel unter der oberen Ecke ist, ist das Ergebnis auch logisch. Als Code ungefähr so:

    void Scroller2D::scrolleBitmap(Bild* bild,int iX, int iY)
    {
        int iBildschirmX=iX-m_iEckeX;
        int iBildschirmY=iY-m_iEckeY;
    
        zeichneBild(bild, iBildschirmX, iBildschirmY);
    }
    

    Man ruft diese Funktion nun also für alle zu scrollende Bilder auf, wodurch diese gezeichnet werden. Der Rest (Buttons, Mauszeiger) wird weiterin direkt mit "zeichneBild" dargestellt.

    Anwendung:
    Ein (Pseudo-)Code, wie die ganze Sache nun insgesamt benutzt werden könnte:

    ladeBilder();
    Scroller2D s2D;
    int iEckeX=0;
    int iEckeY=0;
    
    while (spiel_läuft)
    {
        s2d.setzeEcke(iEckeX,iEckeY);
    
        for (für_jedes_gescrollte_Bild)
        {
            s2d.scrolleBitmap(bild, bildX, bildY);
        }
    
        for (für_jedes_nicht_gescrollte Bild)
        {
            zeichneBild(bild, bildX, bildY);
        }
    
        if (Scrolling)
        {
            iEckeX+=WertX; // nach links/rechts
            iEckeY+=WertY; // nach oben/unten
    
            überprüfeBereich(iEckeX,iEckeY);
        }
    
        // der Rest des Spiels
    }
    

    Bemerkung Evtl. kann es auch sinnvoll sein, die Ecke in floats abzuspeichern.

    Optimierungen:
    Es gibt noch eine Idee, was man optimieren könnte (obwohl dies meiner Erfahrung nach sehr geringe Auswirkungen hat). Der Nachteil der hier gezeigten Methode ist, das alle Bilder an "zeichneBild" geschickt werden. Dort wird dann für jedes einzelne Bild entschieden, ob es im sichtbaren Bereich (Fenster) liegt, oder nicht. Üblicherweise können viele dieser Tests gespart werden, indem ein Grossteil der Bilder als unsichtbar ausgeschlossen wird. Man könnte sich z.b. vorstellen, das unsere Welt, die aus 16384x16384 Pixeln besteht, mit 256x256 quadratischen Bitmaps bedeckt ist (die jeweils 64x64 Pixelgross sind). Man würde dies ungefähr so zeichnen:

    for(iX=0;iX<16384;iX+=64)
        for(iY=0;iY<16384;iY+=64)
        {
            s2d.scrolleBitmap(bild, iX, iY);
        }
    

    Nun hat man jedoch viele Bilder umsonst "losgeschickt", sie waren sowieso unsichtbar. Besser wäre es das Quadrat zu finden, das auf der oberen Ecke liegt, und von dort an so weit zu zeichnen, bis der Bildschirm voll ist. Nun hat man zwangsweise alle sichtbaren Quadrate gezeichnet. Um nun dieses Quadrat zu bestimmt, teilt man einfach die Koordinaten der Ecke durch die Breite/Höhe der Quadrate. Man beginnt bei den Koordinaten dieses Quadrates zu zeichnen und zeichnet von der oberen Ecke soweit, bis der Bildschirmrand erreicht ist:

    int iBildSpalte=iEckeX/64;
    int iBildZeile=iEckeY/64;
    
    for(iX=iBildSpalte*64;iX<iEckeX+1024;iX+=64)
        for(iY=iBildZeile*64;iY<iEckeX+768;iY+=64)
        {
            s2d.scrolleBitmap(bild, iX, iY);
        }
    

    Ich hoffe dies hilft einigen weiter! Weitere Ideen, Vorschläge, Bemerkungen und (ganz wichtig!) Fehler passen hier auch noch drunter.

    Bye, TGGC



  • Super Beitrag!
    Das riecht nach FAQ



  • Tut es auch !

    Cooler Beitrag, danke !


Anmelden zum Antworten