BCB - Spiel Beispiel kommentiert (wie mach ich das mit Canvas) 2D Game



  • Hallo Leute,

    da in letzter Zeit hier mehrfach die Spieleprogrammierung mit dem BCB
    erfragt und diskutiert wurde. Habe ich mir mal die (kleine) Mühe
    gemacht und ein "Pong" Klone erstellt.

    Um mal zu Verhindern das hier noch mehr Fragen, ala wie scrolle ich ein Hintergrund
    Bild usw. auftauchen (gruß an Ag3nt 😉 )
    Die Kommentare im Quellcode lesen sich zwar als hätte es ein "Nicht Deutscher" geschrieben,
    aber ich wollte mich nicht besonders lange damit aufhalten und hab es auf die Schnelle
    runtergesabert, aber ich denke man kann es schon verstehen.

    Das Projekt ist mit dem BCB6pro erstellt worden, um es auch in älteren
    Versionen zu Verwenden braucht man nur ein neues Projekt zu erstellen
    und die Grafiken sowie die "Resourcen.rc" in eurem Projekt-Ordner kopieren.
    Auf der Form erstellt ihr 2 Buttons (Button1,Button2), Button1 erhält den
    Text "Start in Window" und Button2 "Start in Fullscreen", doppelklickt dann
    bei Button1 auf das Ereigniss "OnClick" damit in der Unit1.cpp automatisch
    "void __fastcall TForm1::Button1Click(TObject *Sender)" erscheint.
    In dieser Funktion kopiert ihr dann den Code von meine Unit1.cpp aus der
    Gleichnamiegen Funktion. Ordnet diese Funktion ebenfalls dem Button2 zu.
    Nun benötigt ihr noch die Ereignissfunktionen "OnKeyDown" und OnMouseMove der
    Form, und kopiert den dafür vorgesehenden Code aus meiner Unit1.cpp dort hinein.
    Nun müßt ihr noch die Resourcendatei "Resourcen.rc" dem Projekt hinzu fügen und
    fertig ist das Ganze.

    Hier könnt ihr euch das Projekt herunterladen http://bcb.24day.de/SoloPong.zip
    Und Hier nur die Ausführbare EXE, also das Spiel zum Zocken http://bcb.24day.de/SoloPong.exe

    Mein Ziel ist es euch und gerade den Anfängern, die Canvas Methoden nähr zu bringen und
    eine ungefähre Vorstellung wie man ein Spiel mit dem BCB auch ohne DirectX oder OpenGL
    und Co erstellen kann.

    Damit man auch nur mal ebend schauen kann wie man wo und was machen kann, habe ich dazu
    den Quellcode auch mal hier mit hinein gepostet.

    Zum Spiel: Das Spiel läuft in einer Form mit 640x480 Pixel ab, diese kann als Fenster
    angezeigt werden oder auch Fullscreen, dazu wird die Bildschirmauflösung auf 640x480
    umgeschaltet. Pong ist eigentlich jedem Bekannt, nur bei dieser Variante müßt ihr
    alleine den Puk (Ball) im Spielfeld halten. Dazu habt ihr 3 Puk´s. Die Punkte werden
    als Millisekunden aufgezählt, somit ergiebt eine Sekunde 1000 Punkte hinzu.
    Der Puk wird nach und nach immer schneller. Die Pannels steuert ihr hoch und runter
    mit der Maus, dabei ist zu beachten das nur das rechte Pannel sich in gleicher Richtung
    wie die Maus bewegt und das linke exakt entgegen gesezt. Viel spass beim Spielen.

    Nun der Quellcode:

    //---------------------------------------------------------------------------
    
    #include <vcl.h>
    #pragma hdrstop
    #include "jpeg.hpp" //jpeg unterstützung mit einbinden, Hintergrund ist Jpeg-Bild
    #include "Unit1.h"
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    TForm1 *Form1;
    //Einige Globale Variablen die wir brauchen werden
    bool ende = false;  //ist Spiel ende? ja/nein (true/false)
    bool fullscreen = false; //ist Spiel in Fullscreen?
    int panelY=170; //Y-koordinate der rechten Pannels
    int NW,NH,NC; //Variablen um die Standart-Breite/-Höhe/-Farbtiefe zu speichern
    
    //---------------------------------------------------------------------------
    __fastcall TForm1::TForm1(TComponent* Owner)
            : TForm(Owner)
    {
    	//Hier setzten wir unsere Form erstmal in die Mitte
    	//des Bildschirms
            Form1->Left = (Screen->Width - 640) / 2;
            Form1->Top = (Screen->Height - 480) / 2;
    }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key,
          TShiftState Shift)
    {
    	//sollte man die Escape Taste oder "q" drücken, wir ende auf true
    	//gesetzt damit die Spielschleife beendet wird
            if (Key == VK_ESCAPE || char(Key) == 'Q')
                    ende = true;
    }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::ListBox1MouseMove(TObject *Sender,
          TShiftState Shift, int X, int Y)
    {
            //Hier setzten wir die neue Koordinate "Y" für das rechte Pannel
            //wir ziehen aber 70 Pixel ab, da unser Pannel 140 groß ist und
            //wir die Mitte erhalten möchten
            panelY = Y-70;
    }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
    	// So nun gehts zum Spiel, der Zocker hat auf ein der Buttons geklickt
            Form1->DoubleBuffered = true; //Hier nicht unbedingt notwendig, schadet aber nicht
            Button1->Visible = false; //Button1 unsichtbar machen
            Button2->Visible = false; //Button2 unsichtbar machen
            Form1->BorderStyle = bsNone; //Die Titelleiste der Form ausblenden (kein Border)
    	//Nun erfragen wir mit dynmic_cast die Caption des Buttons, welcher gedrückt wurde,
    	//da es ein AnsiString ist können wir mit ".Pos() erfragen ob das Wort "Fullscreen"
    	//drinsteht, wenn ja (größer 0) und Bildschirmbreite größer 640, dann schalten wir
    	//den Bildschirm in die Auflösung 640x480, die Farbtiefe behalten wir bei
            if (dynamic_cast<TButton*>(Sender)->Caption.Pos("Fullscreen")>0 && Screen->Width>640) {
                    fullscreen = true; // damit wir wissen das wir Fullscreen haben
    
                    NW = Screen->Width; // alte Breite der Bildschirmauflösung
                    NH = Screen->Height; // und alte Höhe, das brauchen wir zum Zurückschalten
                    NC = GetDeviceCaps(Canvas->Handle,BITSPIXEL); //Farbtiefe erfragen
                    //-- BildschirmModus für 640x480xjetzigeFarbtiefe suchen
                    // Darauf möchte ich jetzt nicht nähr eingehen, das kann man dann mal Seperat machen
                    DEVMODE Modus;
                    bool Modok = false;
                    int loop = 0;
                    while (EnumDisplaySettings(NULL, loop, &Modus)) {
                            if (Modus.dmBitsPerPel == DWORD(NC) && Modus.dmPelsHeight == DWORD(480) && Modus.dmPelsWidth == DWORD(640)) {
                                    Modok = true;
                                    break;
                            }
                            loop++;
                    }
                    // wemm Gefunden, dann Umschalten
                    bool AllOK = false;
                    if (Modok) {
                            int Status = ChangeDisplaySettings(&Modus, CDS_UPDATEREGISTRY);
                            if (Status == DISP_CHANGE_SUCCESSFUL) {
                                    AllOK = true;
                                    fullscreen = true;
                            }
                    }
                    if (!AllOK) {
                             ShowMessage("Konnte Bildschirmauflösung nicht ändern!");
                            Form1->Close();
                    }
                    // Nun Schalten wir die Form in den Vollbildmodus
                    // Das ist ähnlich Form1->Top/Left/Width/Height nur das man damit den ganzen Screen bedeckt
                    SetWindowPos(Form1->Handle, HWND_TOPMOST, 0, 0, 640, 480, NULL);
            }
            //Jetzt gehts los
            //Eine neue Instance von TJPEGImage erstellen (jpeg.hpp) also ein neues JPG Bild
            TJPEGImage *BGjpg = new TJPEGImage();
            //mit TResourceStream, einer ableitung von TStream erstellen wir ein Stream von
            //dem JPG-Bild (backpic(Hintergundbild)) aus der Resource (siehe "Resourcen.rc")
            TResourceStream *temp = new TResourceStream((int)HInstance,"backpic",RT_RCDATA);
            //Nun laden wir den Stream in das JPGImage
            BGjpg->LoadFromStream(temp);
            //jetzt erstellen wir ein Bitmap für unser Hintergrund-JPEG, da wir mit Bitmaps arbeiten
            Graphics::TBitmap *BGquelle = new Graphics::TBitmap;
            //Größen setzten, der eigentliche Hintergrund ist 640x960 gemalt worden mit einem Grafikprog.
            //dann habe ich im Grafikp. ein neues Blatt erstellt mit 640x1440(3x480) und das Bild auf
            //die unteren beiden Drittel kopiert, aus dem unteren Drittel (640x480) habe raus kopiert
            //und auf das obere Drittel gesetzt, wir scrollen dann immer um 960 Punkte und fangen von
            //vorne an somit hat man einen sauberen Übergang
            //Nochmal Kurz und Bündig, vom Hintergrund mit 640x960, die untere Hälfte (640x480) kopieren
            //und oben drauf setzten, gesamt dann 640x1440 
            BGquelle->Width = 640;
            BGquelle->Height = 1440;
            //und der Bitmap das JPEG-Bild zu ordnen, so haben wir JPG nach Bitmap konvertiert
            BGquelle->Assign(BGjpg);
            //das JPGbild und der Stream werden nicht mehr benötigt daher, freigegeben.
            delete temp;
            delete BGjpg;
            //nun erstellen wir weitere Bitmaps und laden diese aus der Resource, TBitmap unterstützt das laden aus der resource
            Graphics::TBitmap *panel = new Graphics::TBitmap();
            panel->LoadFromResourceName((int)HInstance,"panel");
            Graphics::TBitmap *pcpanel = new Graphics::TBitmap();
            pcpanel->LoadFromResourceName((int)HInstance,"pcpanel");
            Graphics::TBitmap *puk = new Graphics::TBitmap();
            puk->LoadFromResourceName((int)HInstance,"puk");
            //Da das Bild des Puk´s Rechteckig ist, aber der Puk rund, müßen wir den Rest durchsichtig machen
            // Zum Glück unterstützt TBitmap Transparents, der Puk wurde auf ein schwarzen Hintergrund
            // gemalt, so stellen wir die Transparentfarbe des TBitmap auf schwarz (clBlack - TColor)
            puk->TransparentColor = clBlack;
            // und aktivieren die transparents für den Puk
            puk->Transparent = true;
            // das gleiche mit dem Mittelpunkt Strich, bzw PingPong-netz
            Graphics::TBitmap *midline = new Graphics::TBitmap();
            midline->LoadFromResourceName((int)HInstance,"midline");
            midline->TransparentColor = clBlack;
            midline->Transparent = true;
            //Nun erstellen wir eine Bitmap in der wir die Spielscene rein malen und später in die form kopieren
            // wir nennen es puffer (zwischenspeicher)
            Graphics::TBitmap *Puffer = new Graphics::TBitmap;
            //unsere Form ist ja 640x480 groß, also muß auch der puffer diese Werte bekommen
            Puffer->Width = 640;
            Puffer->Height = 480;
            //NUN, kommen die ersten Canvas Methoden
            //Mit Canvas kann man auch Texte schreiben, dazu stehen uns alle Möglichkeiten von TFont
            //zur Verfügung:
            Puffer->Canvas->Font->Color = clYellow; //Schriftfarbe setzten TColor hier Gelb
            Puffer->Canvas->Font->Size = 16; // Schriftgröße
            Puffer->Canvas->Font->Name = "Arial"; //Schrifttyp
            Puffer->Canvas->Font->Style << fsBold; // Fettschrift dem Style "Zufügen" (<<)
            //Einem Canvas ist auch TBrush (Pinsel) untergeordnet, mit dem man malen kann,
            //wenn man Text schreibt wir der brush als Hintergrund des Textes verwendet
            //das wollen wir aber nicht, wir wollen den Hintergrund des Textes transparent
            //daher schalten wir den Style des TBrush auf "bsClear" (Leer-durchsichtig)
            Puffer->Canvas->Brush->Style = bsClear;
            //Nun setzten wir eine Integer Variable, die uns die Y-Koordinate des Hintergrund
            //Bildes angiebt, welches gescrollt wird, wir fangen im unteren Drittel an und
            //wenn wir oben sind, springen wir wieder nach unten, also start ab 960 (1440-480=960)
            int screen = 960;
            //Spielende setzten wir auf Unwahr (false) da wir ja gleich mit dem Spiel beginnen
            ende = false;
    	//wir benötigen eine Variable in der wir Millisekundenzeit zwischenspeichern
    	//auf der in einer Warteschleif verglichen wird, damit unser spiel mit ca. 25 Bilder 
    	//pro Sekunde läuft (wie im Film)
            DWORD idleTime;
            //einen Integer für die Y-Koordinate des linken Pannels
            int pcY=170;
            //einen zum Runterzählen wie lange GameOver angezeigt wird, bevor man aus dem Spiel, zur normalen Form kommt
            int GOshow = 100;
            //Damit unser Puk auch richtig fliegt, benötigen wir noch angaben über seine Richtung
            //wenn der Puk sich nach Rechts bewegt, PukGoRight=true, sonst nach Links =false
            bool pukGoRight = true;
            //das Gleiche auch mit oben und unten
            bool pukGoDown = false;
            //wenn der Puk am Panel oder am Rand die Richtung wechselt, soll ein Ping Ton
            //über den PC-Lautsprecher erfolgen und wenn er Fehrloren geht (Tor) ein Pong Ton !?!
            bool PongSound = false;
            bool PingSound = false;
            //Für den jeweiligen Ton brauchen wir eine Frequenz, daher Integer frq
            int frq;
            //Der Puk brauch ja auch noch seine Koordinaten, daher folgende 2 Variablen mit dem
            //Startpunkt in der Mitte
            int pukX=320,pukY=240;
            //Die Geschwindigkeit des Puk´s, diese wird im Spielverlauf erhöht
            int speed = 4;
            //Unsere Punkte, gemesen anhand von Millisekunden brauchen wir auch
            DWORD Punkte = 0;
            //Anzahl Bälle, also Puk´s die wir zur verfügung haben bis Spielende, das kann man auch erhöhen
            int baelle = 3;
            //Nun setzten wir noch die Startzeit anhand von Millisekunden, dies benötigen wir um
            //die Punkte zu Berechnen mittels GetTickCount()
            DWORD StartTime = GetTickCount();
    	//Für den Sound prüfen wir noch welches System hier ist (NT,2000,XP oder 98,98se,95)
    	// das brauchen wir um Festzustellen ob wir per Beep() nen Ton machen müßen oder per
    	//Assembler code
            unsigned long dwVersion = GetVersion();
            //...................................................................
            //...................................................................
            // NUN GEHTS LOS MIT DER SPIELSCHLEIFE
            //. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 
            //Die Spielschleife ist eine WHILE-Schleife die Ausgeführt wird, solange wie ende=false ist
            while (!ende) {
            	//Wenn PongSound auf true gesetzt wurde, also beim Tor, Beep mit Frequenz 500 abspielen
                    if (PongSound) {
                            frq = 500;
                            PongSound = false;
                    }
                    //Wenn PingSpund true, da Puk am Panel oder Rand die Richtung wechselt, Beep mit 800
                    if (PingSound) {
                            frq = 800;
                            PingSound = false;
                    }
                    //Wenn frq einen wert erhielt, also größer 0, PC-Lautsprecher mit der frequenz aus frq einschalten
                    // hier aber nur wenn dwVersion kleiner 0x80000000 also 98,98se,95
                    if (frq > 0 && dwVersion > 0x80000000) {
                    	//Auf dem Assemblercode gehe ich nicht nähr ein, man kann auch ein WAVE-Sample verwenden
                    	//und mit PlaySound abspielen
                            unsigned short pitch = (unsigned short) (frq);
                            frq = 0;
                            asm mov bx, pitch
                            asm mov ax, 34DDh
                            asm mov dx, 0012h
                            asm cmp dx, bx
                            asm jnb Stop
                            asm div bx
                            asm mov bx,ax
                            asm in al, 61h
                            asm test al, 3
                            asm jne J1
                            asm or al, 3
                            asm out 61h, al
                            asm mov al, 0B6h
                            asm out 43h, al
                            J1:
                            asm mov al, bl
                            asm out 42h, al
                            asm mov al, bh
                            asm out 42h, al
                            Stop:
                    //ansonsten, wenn NT basiert (NT,2000,XP) verwenden wir Beep()
                    } else {
                            Beep(frq,40);
                            frq = 0;
                    }
    		//Nun setzten wir die Wartezeit bevor die Schleife ihren Code erneut ausführt
    		//dazu schreiben wir die Aktuelle Millisekundenzeit in idleTime + 38 Millisekunden
    		//wenn das ereicht wird am Ende der Schleife wird darauf geprüft, so kann er
    		//weitermachen, normal sinds 40ms (25Bilder x 40ms = 1000ms = 1 Sekunde) aber wir
    		//schenken noch 2ms für Programm-Events (mehr dazu unten)
                    idleTime = GetTickCount()+38;
                    //Nun berechen wir die Punkte indem wir von der aktuellen Zeit(+38) idleTime die
                    //Startzeit (StartTime) abziehen und als Punkte zu ordnen
                    Punkte = idleTime - StartTime;
                    //Hier erhöhen wir die Geschwindigkeit des Puks, wenn eine Punktzahl ereicht oder
                    //überschritten wurde
                    if (Punkte >= 8000)
                            speed = 6;
    
                    if (Punkte >= 15000)
                            speed = 8;
    
                    if (Punkte >= 20000)
                            speed = 10;
    
                    if (Punkte >= 25000)
                            speed = 12;
    
                    if (Punkte >= 28000)
                            speed = 14;
    
                    if (Punkte >= 31000)
                            speed = 16;
    
                    if (Punkte >= 33000)
                            speed = 18;
    
                    if (Punkte >= 40000)
                            speed = 20;
    
                    if (Punkte >= 60000)
                            speed = 26;
    
    		// NUN kommt eine Wichtige Canvas Funktion, die nähmlich uns als erstes den
    		// Hintergrund erstellt (der scrollt), dazu benutzen wir die Methode
    		// CopyRect(TRect ziel,Quelle->Canvas,TRect quelle)
    		// ein TRect beinhaltet die Koordinaten von links-oben und rechts-unten (x1,y1,x2,y2)
    		// das kann man auch mittels der Funktion Rect(int x1, int y1, int x2, int y2) schreiben
    		// so braucht man kein TRect erstellen
    		// Bei uns ist das Ziel das gesamte Bild also 0,0,640,480
    		// aber wir gehen bei der Quelle immer einen Punkt je Durchlauf nach oben
    		// das halten wir in der Variable "screen" fest, die zu Anfang mit 960 beginnt
    		// daher lautet das Quell-TRect 0,screen,640,screen+480 (0,960,640,1440 zu beginn)
                    Puffer->Canvas->CopyRect(Rect(0,0,640,480),BGquelle->Canvas,Rect(0,screen,640,screen+480));
                    // nun setzten wir den wert in screen um 1 nach unten (auf 959) beim nächsten
                    // durchlauf wäre es dann 0,959,640,1439 und so weiter
                    screen--;
                    // und wenn unser Bildausschnitt oben angelangt ist (screen=0) setzten wir es auf 960
                    // zurück und beginnen von vorne
                    if (screen <= 0) {
                            screen = 960;
                    }
                    // Nun kommt eine Canvas methode namens Draw(int x, int y, TBitmap) **bzw TGraphic
                    // mit ihr kann man kleinere grafiken ab einer x,y koordinate in ein Canvas kopieren
                    // hier mit der Mittellinie und dem rechten Panel, desen koordinate-Y Global verändert
                    // wir bei OnMousMove
                    Puffer->Canvas->Draw(292,0,midline);
                    Puffer->Canvas->Draw(590,panelY,panel);
                    // Jetzt erechnen wir die Entgegengesetzte Y-Koordinate für das linke Pannel, anhand
                    // der Y-Koordinate des rechten Pannels, so das sich das linke Pannel immer entgegengesetzt
                    // des rechten Pannels bewegt
                    if (panelY > 170) {
                            pcY = panelY-170;
                            pcY = 170-pcY;
                    } else if(panelY < 170) {
                            pcY = 170-panelY;
                            pcY = 170+pcY;
                    } else {
                            pcY = 170;
                    }
                    // Nun wird auch das linke Pannel mit Draw() ins Spielfeld gezeichnet
                    Puffer->Canvas->Draw(25,pcY,pcpanel);
                    //Nun kommt die Kollisionsabfrage für den Puk
                    // zunächst prüfen wir ob der Puk auf das linke Pannel getroffen ist
                    // wenn ja wird die X richtung geändert und ein PingSound gestartet
                    if (pukX <= 50 && pukY >= pcY && pukY <= pcY+140) {
                            pukGoRight = !pukGoRight;
                            PingSound = true;
                    //Das gleiche für das rechte Pannel
                    } else if (pukX >= 590 && pukY >= panelY && pukY <= panelY+140) {
                            pukGoRight = !pukGoRight;
                            PingSound = true;
                    //ansonsten wenn der Puk aus dem rechten oder linken Rand (Tor) ist
                    //wird auch die X richtung geändert aber ab der Mitte des Feldes aus wieder (320)
                    //und es wird ein Ball (Puk) abgezählt, sowie ein Pongsound gestartet
                    //sollte der Ballzähler 0 ereicht haben wird GOshow auf 40 gesetzt
                    // so wird Game over eingeblendet 40 Schleifen durchgänge lang
                    } else if (pukX <= 0 || pukX >= 640) {
                            pukGoRight = !pukGoRight;
                            pukX = 320;
                            baelle--;
                            PongSound = true;
                            if (baelle == 0) {
                                    GOshow = 40;
                            }
                    }
                    //Wenn der Puk am oberen oder unteren Rand trifft, wird die Y-richtung geändert
                    //und auch ein Pingsound gestartet
                    if (pukY <= 0 || pukY >= 480) {
                            pukGoDown = !pukGoDown;
                            PingSound = true;
                    }
                    //Hier werden jetzt die neuen Positionen des Puk gesetzt mit dem Wert in speed
                    if (pukGoDown) {
                            pukY = pukY+speed;
                    } else {
                            pukY = pukY-speed;
                    }
                    if (pukGoRight) {
                            pukX = pukX+speed;
                    } else {
                            pukX = pukX-speed;
                    }
                    //Zum Schluß wird der Puk mittels Draw() gezeichnet
                    Puffer->Canvas->Draw(pukX-13,pukY-13,puk);
                    //Nun kommt das mit dem Text schreiben mittels Canvas, ihr erinnert euch an
                    //den Angaben von oben mit der Schrifteinstellung
                    //mit textOut kan man ab einer X,Y-Koordinate einen AnsiString schreiben
                    Puffer->Canvas->TextOutA(350,10,"Punkte: "+IntToStr(Punkte));
                    Puffer->Canvas->TextOutA(50,10,"Puk´s: "+IntToStr(baelle));
                    //So wenn Game Over ist und GOshow auf 40 gesetzt wurde
                    //wird das nun je Schleifendurchlauf um eins verringert und 
                    //der Text "Game over" ausgegeben mit TextOut() wie gerade beschrieben
                    if (GOshow<=40 && GOshow >=1) {
                            GOshow--;
                            Puffer->Canvas->TextOutA(250,220,"GAME OVER");
                    //und wenn GOshow die 0 ereicht hat
                    //werden wir die Spielschleife entgültig beenden in dem wir ende auf true setzen
                    } else if (GOshow == 0) {
                            ende = true;
                    }
    
    		//Soooo unser aktuelles Bild unseres Spielfeld wurde im puffer neu gezeichnet
    		//und kann nun zur Anzeige auf die Canvas der Form übertragen werden
    		//das können wir auch mit Draw() machen, da der puffer 640x480 ist wie die Form auch
                    Form1->Canvas->Draw(0,0,Puffer);
    		//oder man macht es auch mittels CopyRect()
    		//Form1->Canvas->CopyRect(Rect(0,0,640,480),puffer->Canvas,Rect(0,0,640,480));
    
    		//Nun warten wir noch bis die knapp 40ms abgelaufen sind
    		//als Ausführung in dieser while-Schleiffe benutzen wir Sleep(), was das Programm
    		//für Anzahl Millisekunden anhält, wir verwenden aber 0 damit das nicht bremst
                    while (GetTickCount() < idleTime)
                            ::Sleep(0);
    
                    //nun Schalten wir den PC-Lautsprecher wieder ab, wenn ein Pong oder Ping sound
                    //gestartet wurde wir er jetzt wieder angehalten, sofern es ungleich NT Basiert ist
                    if (dwVersion > 0x80000000) {
                            asm in al, 61h
                            asm and al, 0fch
                            asm out 61h, al
    		}
    		//Nun kommt was ganz Wichtiges, damit der Rest des Programms (Mausabfrage, Tastatur, etc)
    		//überhaupt Arbeiten kann, und das Programm nicht einfriert, muß der Windows Nachrichtendienst
    		//aufgerufen werden, also die Messages werden behandelt
                    Application->ProcessMessages();
    
            } //Und ab nach oben das gleiche von vorne mit neuen Koordinaten
            //------------------------------------
            //Da das Spiel nun zu Ende ist können wir alle Bitmaps wieder freigeben
            delete BGquelle;
            delete Puffer;
            delete panel;
            delete midline;
            delete puk;
            //nun setzen wir die Pinselfarbe der Form-Canvas auf die Ursprungsfarbe
            Form1->Canvas->Brush->Color = clBtnFace;
            //Und füllen mittels Canvas->FillRect die komplette Form mit dieser Farbe (640x480)
            Form1->Canvas->FillRect(Rect(0,0,640,480));
            //zu letzt noch die Punktzahl in die Caption (Titelleiste) der form schreiben
            Form1->Caption = "Letzte Punktzahl: "+IntToStr(Punkte);
            //und die Buttons wieder sichtbar machen
            Button1->Visible = true;
            Button2->Visible = true;
            //wenn das Spiel in Fullscreen lief (fullscreen = true) dann setzten wir
            //die alte Bildschirmauflösung wieder zurück, die Werte dazu stehen ja in
            // NW,NH,NC die wir beim Start gesetzt haben
            if (fullscreen) {
                    DEVMODE Modus;
                    int loop = 0;
                    while (EnumDisplaySettings(NULL, loop, &Modus)) {
                            if (Modus.dmBitsPerPel == (DWORD)NC && Modus.dmPelsHeight == (DWORD)NH && Modus.dmPelsWidth == (DWORD)NW) {
                                    break;
                            }
                            loop++;
                    }
                    int Status = ChangeDisplaySettings(&Modus, CDS_UPDATEREGISTRY);
                    fullscreen = false;
            }
            //Nun noch die Standart größen der Form, wieder in die Mitte setzten und den Border 
            //auf bsSingle zurück setzten, damit wir die Titelleiste sehen
            Form1->Width = 640;
            Form1->Height = 480;
            Form1->Left = (Screen->Width - 640) / 2;
            Form1->Top = (Screen->Height - 480) / 2;
            Form1->BorderStyle = bsSingle;
            // UND FERTIG
    }
    //---------------------------------------------------------------------------
    

    Ich hoffe ich kann damit noch mehr Fragen zur einfachen Spiele Programmierung
    mit dem BCB bremsen, damit das Forum nicht noch mehr damit Bombadiert wird.

    Viele Grüße und viel Spass beim Nachmachen und Zocken

    euer Micha (promicha)



  • 1000000000000000000000000000 x Danke 🙂

    Genial nun versteh auch ich den kompletten Code 🙂

    Gleich mal ausdrucken und min 100x durchlesen 🙂



  • Aber nimm dir bitte keine Beispiel am Stil. 😉
    Das "alles in den Buttonclick" ist nämlich ein erschütterndes Beispiel für die dem BCB gern nachgesagten Nachteile.



  • Jansen schrieb:

    Aber nimm dir bitte keine Beispiel am Stil. 😉
    Das "alles in den Buttonclick" ist nämlich ein erschütterndes Beispiel für die dem BCB gern nachgesagten Nachteile.

    Dem kann ich nur Zustimmen, eine gute Möglichkeit einer Game-schleife kann
    man z.B. mit Application->OnIdle erstellen, vor allem wenn das Spiel länger dauert

    Ich möchte daher nochmals drauf hinweisen, wie in den andere Threads zu diesem
    Thema, das ich es auf die Schnelle runterprogge und kein Spiele-Entwickler bin,
    ich möchte mit dem Beispiel nur die Möglichkeiten des BCB vorzeigen.

    Ihr sollt auch nicht den Code einfach Kopieren und abändern, sondern die
    Methoden verstehen und mit diesem Wissen euer Spiel erstellen. Schaut euch
    auch das Beispiel im Example-Ordner des BCB an unter Games "Earth Pong"
    dort wird es auch in dem Application->OnIdle Event gesetzt.

    Und Alles Andere zum Thema Spieleentwicklung bitte in das entsprechende Board.

    In diesem Sinne, Micha.



  • Bitte einmal jede Zeile kommentieren und sagen, was sie macht. Ich stell es bewusst nicht ins Assembler-Forum, weil ich es von ihm erklärt haben möchte, nicht von irgendjemanden. Sorry.

    Und warum?

    //Auf dem Assemblercode gehe ich nicht nähr ein, man kann auch ein WAVE-Sample verwenden
    //und mit PlaySound abspielen
    unsigned short pitch = (unsigned short) (frq);
    frq = 0;
    asm mov bx, pitch
    asm mov ax, 34DDh
    ....

    Hast du nicht gelesen das ich das nicht näher erkläre?

    Ansonsten frag doch "W. Posur", den er hat diesen FAQ-Beitrag gepostet!

    gruß Micha



  • Zur Assemblerfrage siehe hier.



  • promicha schrieb:

    Hier könnt ihr euch das Projekt herunterladen http://bcb.24day.de/SoloPong.zip
    Und Hier nur die Ausführbare EXE, also das Spiel zum Zocken http://bcb.24day.de/SoloPong.exe

    Hi die links gehen leider nicht, ich würde aber sehr gerne bischen rum probieren, da ich ein Schul projekt habe was in diese richtung geht.

    könntest du die files vieleicht an meine e-mail schicken ?

    MfG djcccp


  • Mod

    Hallo

    bei mir gehen die Links auch nicht ?

    besser waere zu klaeren warum nicht anstatt sowas jedem per Mail zu senden

    MfG
    Klaus



  • Ja stimmt. Kennt jemand promicha's e-mail....oder wie man sonst mit ihm in kontak tretten kann ?

    MfG djcccp



  • hi,

    hier hab ich mal ein paar interessante Links zusammengesucht, vieleicht hilft dir das weiter 🤡

    http://cc.borland.com/cc/ccweb.exe/listing?id=17289

    http://www.monroeccc.edu/ckelly/game_programming.htm

    MFG Fregger



  • djcccp schrieb:

    Kennt jemand promicha's e-mail....oder wie man sonst mit ihm in kontak tretten kann ?

    Warum wirst du nicht mal einen Blick in sein Benutzerprofil?

    PS.
    Katastrophenalarm! 😉


Anmelden zum Antworten