<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[BCB - Spiel Beispiel kommentiert (wie mach ich das mit Canvas) 2D Game]]></title><description><![CDATA[<p>Hallo Leute,</p>
<p>da in letzter Zeit hier mehrfach die Spieleprogrammierung mit dem BCB<br />
erfragt und diskutiert wurde. Habe ich mir mal die (kleine) Mühe<br />
gemacht und ein &quot;Pong&quot; Klone erstellt.</p>
<p>Um mal zu Verhindern das hier noch mehr Fragen, ala wie scrolle ich ein Hintergrund<br />
Bild usw. auftauchen (gruß an Ag3nt <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f609.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--winking_face"
      title=";)"
      alt="😉"
    /> )<br />
Die Kommentare im Quellcode lesen sich zwar als hätte es ein &quot;Nicht Deutscher&quot; geschrieben,<br />
aber ich wollte mich nicht besonders lange damit aufhalten und hab es auf die Schnelle<br />
runtergesabert, aber ich denke man kann es schon verstehen.</p>
<p>Das Projekt ist mit dem BCB6pro erstellt worden, um es auch in älteren<br />
Versionen zu Verwenden braucht man nur ein neues Projekt zu erstellen<br />
und die Grafiken sowie die &quot;Resourcen.rc&quot; in eurem Projekt-Ordner kopieren.<br />
Auf der Form erstellt ihr 2 Buttons (Button1,Button2), Button1 erhält den<br />
Text &quot;Start in Window&quot; und Button2 &quot;Start in Fullscreen&quot;, doppelklickt dann<br />
bei Button1 auf das Ereigniss &quot;OnClick&quot; damit in der Unit1.cpp automatisch<br />
&quot;void __fastcall TForm1::Button1Click(TObject *Sender)&quot; erscheint.<br />
In dieser Funktion kopiert ihr dann den Code von meine Unit1.cpp aus der<br />
Gleichnamiegen Funktion. Ordnet diese Funktion ebenfalls dem Button2 zu.<br />
Nun benötigt ihr noch die Ereignissfunktionen &quot;OnKeyDown&quot; und OnMouseMove der<br />
Form, und kopiert den dafür vorgesehenden Code aus meiner Unit1.cpp dort hinein.<br />
Nun müßt ihr noch die Resourcendatei &quot;Resourcen.rc&quot; dem Projekt hinzu fügen und<br />
fertig ist das Ganze.</p>
<p>Hier könnt ihr euch das Projekt herunterladen <a href="http://bcb.24day.de/SoloPong.zip" rel="nofollow">http://bcb.24day.de/SoloPong.zip</a><br />
Und Hier nur die Ausführbare EXE, also das Spiel zum Zocken <a href="http://bcb.24day.de/SoloPong.exe" rel="nofollow">http://bcb.24day.de/SoloPong.exe</a></p>
<p>Mein Ziel ist es euch und gerade den Anfängern, die Canvas Methoden nähr zu bringen und<br />
eine ungefähre Vorstellung wie man ein Spiel mit dem BCB auch ohne DirectX oder OpenGL<br />
und Co erstellen kann.</p>
<p>Damit man auch nur mal ebend schauen kann wie man wo und was machen kann, habe ich dazu<br />
den Quellcode auch mal hier mit hinein gepostet.</p>
<p>Zum Spiel: Das Spiel läuft in einer Form mit 640x480 Pixel ab, diese kann als Fenster<br />
angezeigt werden oder auch Fullscreen, dazu wird die Bildschirmauflösung auf 640x480<br />
umgeschaltet. Pong ist eigentlich jedem Bekannt, nur bei dieser Variante müßt ihr<br />
alleine den Puk (Ball) im Spielfeld halten. Dazu habt ihr 3 Puk´s. Die Punkte werden<br />
als Millisekunden aufgezählt, somit ergiebt eine Sekunde 1000 Punkte hinzu.<br />
Der Puk wird nach und nach immer schneller. Die Pannels steuert ihr hoch und runter<br />
mit der Maus, dabei ist zu beachten das nur das rechte Pannel sich in gleicher Richtung<br />
wie die Maus bewegt und das linke exakt entgegen gesezt. Viel spass beim Spielen.</p>
<p>Nun der Quellcode:</p>
<pre><code>//---------------------------------------------------------------------------

#include &lt;vcl.h&gt;
#pragma hdrstop
#include &quot;jpeg.hpp&quot; //jpeg unterstützung mit einbinden, Hintergrund ist Jpeg-Bild
#include &quot;Unit1.h&quot;
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource &quot;*.dfm&quot;
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-&gt;Left = (Screen-&gt;Width - 640) / 2;
        Form1-&gt;Top = (Screen-&gt;Height - 480) / 2;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &amp;Key,
      TShiftState Shift)
{
	//sollte man die Escape Taste oder &quot;q&quot; 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 &quot;Y&quot; 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-&gt;DoubleBuffered = true; //Hier nicht unbedingt notwendig, schadet aber nicht
        Button1-&gt;Visible = false; //Button1 unsichtbar machen
        Button2-&gt;Visible = false; //Button2 unsichtbar machen
        Form1-&gt;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 &quot;.Pos() erfragen ob das Wort &quot;Fullscreen&quot;
	//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&lt;TButton*&gt;(Sender)-&gt;Caption.Pos(&quot;Fullscreen&quot;)&gt;0 &amp;&amp; Screen-&gt;Width&gt;640) {
                fullscreen = true; // damit wir wissen das wir Fullscreen haben

                NW = Screen-&gt;Width; // alte Breite der Bildschirmauflösung
                NH = Screen-&gt;Height; // und alte Höhe, das brauchen wir zum Zurückschalten
                NC = GetDeviceCaps(Canvas-&gt;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, &amp;Modus)) {
                        if (Modus.dmBitsPerPel == DWORD(NC) &amp;&amp; Modus.dmPelsHeight == DWORD(480) &amp;&amp; Modus.dmPelsWidth == DWORD(640)) {
                                Modok = true;
                                break;
                        }
                        loop++;
                }
                // wemm Gefunden, dann Umschalten
                bool AllOK = false;
                if (Modok) {
                        int Status = ChangeDisplaySettings(&amp;Modus, CDS_UPDATEREGISTRY);
                        if (Status == DISP_CHANGE_SUCCESSFUL) {
                                AllOK = true;
                                fullscreen = true;
                        }
                }
                if (!AllOK) {
                         ShowMessage(&quot;Konnte Bildschirmauflösung nicht ändern!&quot;);
                        Form1-&gt;Close();
                }
                // Nun Schalten wir die Form in den Vollbildmodus
                // Das ist ähnlich Form1-&gt;Top/Left/Width/Height nur das man damit den ganzen Screen bedeckt
                SetWindowPos(Form1-&gt;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 &quot;Resourcen.rc&quot;)
        TResourceStream *temp = new TResourceStream((int)HInstance,&quot;backpic&quot;,RT_RCDATA);
        //Nun laden wir den Stream in das JPGImage
        BGjpg-&gt;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-&gt;Width = 640;
        BGquelle-&gt;Height = 1440;
        //und der Bitmap das JPEG-Bild zu ordnen, so haben wir JPG nach Bitmap konvertiert
        BGquelle-&gt;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-&gt;LoadFromResourceName((int)HInstance,&quot;panel&quot;);
        Graphics::TBitmap *pcpanel = new Graphics::TBitmap();
        pcpanel-&gt;LoadFromResourceName((int)HInstance,&quot;pcpanel&quot;);
        Graphics::TBitmap *puk = new Graphics::TBitmap();
        puk-&gt;LoadFromResourceName((int)HInstance,&quot;puk&quot;);
        //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-&gt;TransparentColor = clBlack;
        // und aktivieren die transparents für den Puk
        puk-&gt;Transparent = true;
        // das gleiche mit dem Mittelpunkt Strich, bzw PingPong-netz
        Graphics::TBitmap *midline = new Graphics::TBitmap();
        midline-&gt;LoadFromResourceName((int)HInstance,&quot;midline&quot;);
        midline-&gt;TransparentColor = clBlack;
        midline-&gt;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-&gt;Width = 640;
        Puffer-&gt;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-&gt;Canvas-&gt;Font-&gt;Color = clYellow; //Schriftfarbe setzten TColor hier Gelb
        Puffer-&gt;Canvas-&gt;Font-&gt;Size = 16; // Schriftgröße
        Puffer-&gt;Canvas-&gt;Font-&gt;Name = &quot;Arial&quot;; //Schrifttyp
        Puffer-&gt;Canvas-&gt;Font-&gt;Style &lt;&lt; fsBold; // Fettschrift dem Style &quot;Zufügen&quot; (&lt;&lt;)
        //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 &quot;bsClear&quot; (Leer-durchsichtig)
        Puffer-&gt;Canvas-&gt;Brush-&gt;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 &gt; 0 &amp;&amp; dwVersion &gt; 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 &gt;= 8000)
                        speed = 6;

                if (Punkte &gt;= 15000)
                        speed = 8;

                if (Punkte &gt;= 20000)
                        speed = 10;

                if (Punkte &gt;= 25000)
                        speed = 12;

                if (Punkte &gt;= 28000)
                        speed = 14;

                if (Punkte &gt;= 31000)
                        speed = 16;

                if (Punkte &gt;= 33000)
                        speed = 18;

                if (Punkte &gt;= 40000)
                        speed = 20;

                if (Punkte &gt;= 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-&gt;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 &quot;screen&quot; fest, die zu Anfang mit 960 beginnt
		// daher lautet das Quell-TRect 0,screen,640,screen+480 (0,960,640,1440 zu beginn)
                Puffer-&gt;Canvas-&gt;CopyRect(Rect(0,0,640,480),BGquelle-&gt;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 &lt;= 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-&gt;Canvas-&gt;Draw(292,0,midline);
                Puffer-&gt;Canvas-&gt;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 &gt; 170) {
                        pcY = panelY-170;
                        pcY = 170-pcY;
                } else if(panelY &lt; 170) {
                        pcY = 170-panelY;
                        pcY = 170+pcY;
                } else {
                        pcY = 170;
                }
                // Nun wird auch das linke Pannel mit Draw() ins Spielfeld gezeichnet
                Puffer-&gt;Canvas-&gt;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 &lt;= 50 &amp;&amp; pukY &gt;= pcY &amp;&amp; pukY &lt;= pcY+140) {
                        pukGoRight = !pukGoRight;
                        PingSound = true;
                //Das gleiche für das rechte Pannel
                } else if (pukX &gt;= 590 &amp;&amp; pukY &gt;= panelY &amp;&amp; pukY &lt;= 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 &lt;= 0 || pukX &gt;= 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 &lt;= 0 || pukY &gt;= 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-&gt;Canvas-&gt;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-&gt;Canvas-&gt;TextOutA(350,10,&quot;Punkte: &quot;+IntToStr(Punkte));
                Puffer-&gt;Canvas-&gt;TextOutA(50,10,&quot;Puk´s: &quot;+IntToStr(baelle));
                //So wenn Game Over ist und GOshow auf 40 gesetzt wurde
                //wird das nun je Schleifendurchlauf um eins verringert und 
                //der Text &quot;Game over&quot; ausgegeben mit TextOut() wie gerade beschrieben
                if (GOshow&lt;=40 &amp;&amp; GOshow &gt;=1) {
                        GOshow--;
                        Puffer-&gt;Canvas-&gt;TextOutA(250,220,&quot;GAME OVER&quot;);
                //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-&gt;Canvas-&gt;Draw(0,0,Puffer);
		//oder man macht es auch mittels CopyRect()
		//Form1-&gt;Canvas-&gt;CopyRect(Rect(0,0,640,480),puffer-&gt;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() &lt; 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 &gt; 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-&gt;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-&gt;Canvas-&gt;Brush-&gt;Color = clBtnFace;
        //Und füllen mittels Canvas-&gt;FillRect die komplette Form mit dieser Farbe (640x480)
        Form1-&gt;Canvas-&gt;FillRect(Rect(0,0,640,480));
        //zu letzt noch die Punktzahl in die Caption (Titelleiste) der form schreiben
        Form1-&gt;Caption = &quot;Letzte Punktzahl: &quot;+IntToStr(Punkte);
        //und die Buttons wieder sichtbar machen
        Button1-&gt;Visible = true;
        Button2-&gt;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, &amp;Modus)) {
                        if (Modus.dmBitsPerPel == (DWORD)NC &amp;&amp; Modus.dmPelsHeight == (DWORD)NH &amp;&amp; Modus.dmPelsWidth == (DWORD)NW) {
                                break;
                        }
                        loop++;
                }
                int Status = ChangeDisplaySettings(&amp;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-&gt;Width = 640;
        Form1-&gt;Height = 480;
        Form1-&gt;Left = (Screen-&gt;Width - 640) / 2;
        Form1-&gt;Top = (Screen-&gt;Height - 480) / 2;
        Form1-&gt;BorderStyle = bsSingle;
        // UND FERTIG
}
//---------------------------------------------------------------------------
</code></pre>
<p>Ich hoffe ich kann damit noch mehr Fragen zur einfachen Spiele Programmierung<br />
mit dem BCB bremsen, damit das Forum nicht noch mehr damit Bombadiert wird.</p>
<p>Viele Grüße und viel Spass beim Nachmachen und Zocken</p>
<p>euer Micha (promicha)</p>
]]></description><link>https://www.c-plusplus.net/forum/topic/68006/bcb-spiel-beispiel-kommentiert-wie-mach-ich-das-mit-canvas-2d-game</link><generator>RSS for Node</generator><lastBuildDate>Sun, 12 Jul 2026 02:38:45 GMT</lastBuildDate><atom:link href="https://www.c-plusplus.net/forum/topic/68006.rss" rel="self" type="application/rss+xml"/><pubDate>Sat, 13 Mar 2004 22:27:40 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to BCB - Spiel Beispiel kommentiert (wie mach ich das mit Canvas) 2D Game on Sat, 13 Mar 2004 23:58:12 GMT]]></title><description><![CDATA[<p>Hallo Leute,</p>
<p>da in letzter Zeit hier mehrfach die Spieleprogrammierung mit dem BCB<br />
erfragt und diskutiert wurde. Habe ich mir mal die (kleine) Mühe<br />
gemacht und ein &quot;Pong&quot; Klone erstellt.</p>
<p>Um mal zu Verhindern das hier noch mehr Fragen, ala wie scrolle ich ein Hintergrund<br />
Bild usw. auftauchen (gruß an Ag3nt <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f609.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--winking_face"
      title=";)"
      alt="😉"
    /> )<br />
Die Kommentare im Quellcode lesen sich zwar als hätte es ein &quot;Nicht Deutscher&quot; geschrieben,<br />
aber ich wollte mich nicht besonders lange damit aufhalten und hab es auf die Schnelle<br />
runtergesabert, aber ich denke man kann es schon verstehen.</p>
<p>Das Projekt ist mit dem BCB6pro erstellt worden, um es auch in älteren<br />
Versionen zu Verwenden braucht man nur ein neues Projekt zu erstellen<br />
und die Grafiken sowie die &quot;Resourcen.rc&quot; in eurem Projekt-Ordner kopieren.<br />
Auf der Form erstellt ihr 2 Buttons (Button1,Button2), Button1 erhält den<br />
Text &quot;Start in Window&quot; und Button2 &quot;Start in Fullscreen&quot;, doppelklickt dann<br />
bei Button1 auf das Ereigniss &quot;OnClick&quot; damit in der Unit1.cpp automatisch<br />
&quot;void __fastcall TForm1::Button1Click(TObject *Sender)&quot; erscheint.<br />
In dieser Funktion kopiert ihr dann den Code von meine Unit1.cpp aus der<br />
Gleichnamiegen Funktion. Ordnet diese Funktion ebenfalls dem Button2 zu.<br />
Nun benötigt ihr noch die Ereignissfunktionen &quot;OnKeyDown&quot; und OnMouseMove der<br />
Form, und kopiert den dafür vorgesehenden Code aus meiner Unit1.cpp dort hinein.<br />
Nun müßt ihr noch die Resourcendatei &quot;Resourcen.rc&quot; dem Projekt hinzu fügen und<br />
fertig ist das Ganze.</p>
<p>Hier könnt ihr euch das Projekt herunterladen <a href="http://bcb.24day.de/SoloPong.zip" rel="nofollow">http://bcb.24day.de/SoloPong.zip</a><br />
Und Hier nur die Ausführbare EXE, also das Spiel zum Zocken <a href="http://bcb.24day.de/SoloPong.exe" rel="nofollow">http://bcb.24day.de/SoloPong.exe</a></p>
<p>Mein Ziel ist es euch und gerade den Anfängern, die Canvas Methoden nähr zu bringen und<br />
eine ungefähre Vorstellung wie man ein Spiel mit dem BCB auch ohne DirectX oder OpenGL<br />
und Co erstellen kann.</p>
<p>Damit man auch nur mal ebend schauen kann wie man wo und was machen kann, habe ich dazu<br />
den Quellcode auch mal hier mit hinein gepostet.</p>
<p>Zum Spiel: Das Spiel läuft in einer Form mit 640x480 Pixel ab, diese kann als Fenster<br />
angezeigt werden oder auch Fullscreen, dazu wird die Bildschirmauflösung auf 640x480<br />
umgeschaltet. Pong ist eigentlich jedem Bekannt, nur bei dieser Variante müßt ihr<br />
alleine den Puk (Ball) im Spielfeld halten. Dazu habt ihr 3 Puk´s. Die Punkte werden<br />
als Millisekunden aufgezählt, somit ergiebt eine Sekunde 1000 Punkte hinzu.<br />
Der Puk wird nach und nach immer schneller. Die Pannels steuert ihr hoch und runter<br />
mit der Maus, dabei ist zu beachten das nur das rechte Pannel sich in gleicher Richtung<br />
wie die Maus bewegt und das linke exakt entgegen gesezt. Viel spass beim Spielen.</p>
<p>Nun der Quellcode:</p>
<pre><code>//---------------------------------------------------------------------------

#include &lt;vcl.h&gt;
#pragma hdrstop
#include &quot;jpeg.hpp&quot; //jpeg unterstützung mit einbinden, Hintergrund ist Jpeg-Bild
#include &quot;Unit1.h&quot;
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource &quot;*.dfm&quot;
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-&gt;Left = (Screen-&gt;Width - 640) / 2;
        Form1-&gt;Top = (Screen-&gt;Height - 480) / 2;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &amp;Key,
      TShiftState Shift)
{
	//sollte man die Escape Taste oder &quot;q&quot; 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 &quot;Y&quot; 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-&gt;DoubleBuffered = true; //Hier nicht unbedingt notwendig, schadet aber nicht
        Button1-&gt;Visible = false; //Button1 unsichtbar machen
        Button2-&gt;Visible = false; //Button2 unsichtbar machen
        Form1-&gt;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 &quot;.Pos() erfragen ob das Wort &quot;Fullscreen&quot;
	//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&lt;TButton*&gt;(Sender)-&gt;Caption.Pos(&quot;Fullscreen&quot;)&gt;0 &amp;&amp; Screen-&gt;Width&gt;640) {
                fullscreen = true; // damit wir wissen das wir Fullscreen haben

                NW = Screen-&gt;Width; // alte Breite der Bildschirmauflösung
                NH = Screen-&gt;Height; // und alte Höhe, das brauchen wir zum Zurückschalten
                NC = GetDeviceCaps(Canvas-&gt;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, &amp;Modus)) {
                        if (Modus.dmBitsPerPel == DWORD(NC) &amp;&amp; Modus.dmPelsHeight == DWORD(480) &amp;&amp; Modus.dmPelsWidth == DWORD(640)) {
                                Modok = true;
                                break;
                        }
                        loop++;
                }
                // wemm Gefunden, dann Umschalten
                bool AllOK = false;
                if (Modok) {
                        int Status = ChangeDisplaySettings(&amp;Modus, CDS_UPDATEREGISTRY);
                        if (Status == DISP_CHANGE_SUCCESSFUL) {
                                AllOK = true;
                                fullscreen = true;
                        }
                }
                if (!AllOK) {
                         ShowMessage(&quot;Konnte Bildschirmauflösung nicht ändern!&quot;);
                        Form1-&gt;Close();
                }
                // Nun Schalten wir die Form in den Vollbildmodus
                // Das ist ähnlich Form1-&gt;Top/Left/Width/Height nur das man damit den ganzen Screen bedeckt
                SetWindowPos(Form1-&gt;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 &quot;Resourcen.rc&quot;)
        TResourceStream *temp = new TResourceStream((int)HInstance,&quot;backpic&quot;,RT_RCDATA);
        //Nun laden wir den Stream in das JPGImage
        BGjpg-&gt;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-&gt;Width = 640;
        BGquelle-&gt;Height = 1440;
        //und der Bitmap das JPEG-Bild zu ordnen, so haben wir JPG nach Bitmap konvertiert
        BGquelle-&gt;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-&gt;LoadFromResourceName((int)HInstance,&quot;panel&quot;);
        Graphics::TBitmap *pcpanel = new Graphics::TBitmap();
        pcpanel-&gt;LoadFromResourceName((int)HInstance,&quot;pcpanel&quot;);
        Graphics::TBitmap *puk = new Graphics::TBitmap();
        puk-&gt;LoadFromResourceName((int)HInstance,&quot;puk&quot;);
        //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-&gt;TransparentColor = clBlack;
        // und aktivieren die transparents für den Puk
        puk-&gt;Transparent = true;
        // das gleiche mit dem Mittelpunkt Strich, bzw PingPong-netz
        Graphics::TBitmap *midline = new Graphics::TBitmap();
        midline-&gt;LoadFromResourceName((int)HInstance,&quot;midline&quot;);
        midline-&gt;TransparentColor = clBlack;
        midline-&gt;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-&gt;Width = 640;
        Puffer-&gt;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-&gt;Canvas-&gt;Font-&gt;Color = clYellow; //Schriftfarbe setzten TColor hier Gelb
        Puffer-&gt;Canvas-&gt;Font-&gt;Size = 16; // Schriftgröße
        Puffer-&gt;Canvas-&gt;Font-&gt;Name = &quot;Arial&quot;; //Schrifttyp
        Puffer-&gt;Canvas-&gt;Font-&gt;Style &lt;&lt; fsBold; // Fettschrift dem Style &quot;Zufügen&quot; (&lt;&lt;)
        //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 &quot;bsClear&quot; (Leer-durchsichtig)
        Puffer-&gt;Canvas-&gt;Brush-&gt;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 &gt; 0 &amp;&amp; dwVersion &gt; 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 &gt;= 8000)
                        speed = 6;

                if (Punkte &gt;= 15000)
                        speed = 8;

                if (Punkte &gt;= 20000)
                        speed = 10;

                if (Punkte &gt;= 25000)
                        speed = 12;

                if (Punkte &gt;= 28000)
                        speed = 14;

                if (Punkte &gt;= 31000)
                        speed = 16;

                if (Punkte &gt;= 33000)
                        speed = 18;

                if (Punkte &gt;= 40000)
                        speed = 20;

                if (Punkte &gt;= 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-&gt;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 &quot;screen&quot; fest, die zu Anfang mit 960 beginnt
		// daher lautet das Quell-TRect 0,screen,640,screen+480 (0,960,640,1440 zu beginn)
                Puffer-&gt;Canvas-&gt;CopyRect(Rect(0,0,640,480),BGquelle-&gt;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 &lt;= 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-&gt;Canvas-&gt;Draw(292,0,midline);
                Puffer-&gt;Canvas-&gt;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 &gt; 170) {
                        pcY = panelY-170;
                        pcY = 170-pcY;
                } else if(panelY &lt; 170) {
                        pcY = 170-panelY;
                        pcY = 170+pcY;
                } else {
                        pcY = 170;
                }
                // Nun wird auch das linke Pannel mit Draw() ins Spielfeld gezeichnet
                Puffer-&gt;Canvas-&gt;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 &lt;= 50 &amp;&amp; pukY &gt;= pcY &amp;&amp; pukY &lt;= pcY+140) {
                        pukGoRight = !pukGoRight;
                        PingSound = true;
                //Das gleiche für das rechte Pannel
                } else if (pukX &gt;= 590 &amp;&amp; pukY &gt;= panelY &amp;&amp; pukY &lt;= 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 &lt;= 0 || pukX &gt;= 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 &lt;= 0 || pukY &gt;= 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-&gt;Canvas-&gt;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-&gt;Canvas-&gt;TextOutA(350,10,&quot;Punkte: &quot;+IntToStr(Punkte));
                Puffer-&gt;Canvas-&gt;TextOutA(50,10,&quot;Puk´s: &quot;+IntToStr(baelle));
                //So wenn Game Over ist und GOshow auf 40 gesetzt wurde
                //wird das nun je Schleifendurchlauf um eins verringert und 
                //der Text &quot;Game over&quot; ausgegeben mit TextOut() wie gerade beschrieben
                if (GOshow&lt;=40 &amp;&amp; GOshow &gt;=1) {
                        GOshow--;
                        Puffer-&gt;Canvas-&gt;TextOutA(250,220,&quot;GAME OVER&quot;);
                //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-&gt;Canvas-&gt;Draw(0,0,Puffer);
		//oder man macht es auch mittels CopyRect()
		//Form1-&gt;Canvas-&gt;CopyRect(Rect(0,0,640,480),puffer-&gt;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() &lt; 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 &gt; 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-&gt;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-&gt;Canvas-&gt;Brush-&gt;Color = clBtnFace;
        //Und füllen mittels Canvas-&gt;FillRect die komplette Form mit dieser Farbe (640x480)
        Form1-&gt;Canvas-&gt;FillRect(Rect(0,0,640,480));
        //zu letzt noch die Punktzahl in die Caption (Titelleiste) der form schreiben
        Form1-&gt;Caption = &quot;Letzte Punktzahl: &quot;+IntToStr(Punkte);
        //und die Buttons wieder sichtbar machen
        Button1-&gt;Visible = true;
        Button2-&gt;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, &amp;Modus)) {
                        if (Modus.dmBitsPerPel == (DWORD)NC &amp;&amp; Modus.dmPelsHeight == (DWORD)NH &amp;&amp; Modus.dmPelsWidth == (DWORD)NW) {
                                break;
                        }
                        loop++;
                }
                int Status = ChangeDisplaySettings(&amp;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-&gt;Width = 640;
        Form1-&gt;Height = 480;
        Form1-&gt;Left = (Screen-&gt;Width - 640) / 2;
        Form1-&gt;Top = (Screen-&gt;Height - 480) / 2;
        Form1-&gt;BorderStyle = bsSingle;
        // UND FERTIG
}
//---------------------------------------------------------------------------
</code></pre>
<p>Ich hoffe ich kann damit noch mehr Fragen zur einfachen Spiele Programmierung<br />
mit dem BCB bremsen, damit das Forum nicht noch mehr damit Bombadiert wird.</p>
<p>Viele Grüße und viel Spass beim Nachmachen und Zocken</p>
<p>euer Micha (promicha)</p>
]]></description><link>https://www.c-plusplus.net/forum/post/479841</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/479841</guid><dc:creator><![CDATA[promicha]]></dc:creator><pubDate>Sat, 13 Mar 2004 23:58:12 GMT</pubDate></item><item><title><![CDATA[Reply to BCB - Spiel Beispiel kommentiert (wie mach ich das mit Canvas) 2D Game on Sun, 14 Mar 2004 00:46:00 GMT]]></title><description><![CDATA[<p>1000000000000000000000000000 x Danke <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f642.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--slightly_smiling_face"
      title=":)"
      alt="🙂"
    /></p>
<p>Genial nun versteh auch ich den kompletten Code <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f642.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--slightly_smiling_face"
      title=":)"
      alt="🙂"
    /></p>
<p>Gleich mal ausdrucken und min 100x durchlesen <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f642.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--slightly_smiling_face"
      title=":)"
      alt="🙂"
    /></p>
]]></description><link>https://www.c-plusplus.net/forum/post/479880</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/479880</guid><dc:creator><![CDATA[Ag3nt]]></dc:creator><pubDate>Sun, 14 Mar 2004 00:46:00 GMT</pubDate></item><item><title><![CDATA[Reply to BCB - Spiel Beispiel kommentiert (wie mach ich das mit Canvas) 2D Game on Sun, 14 Mar 2004 01:22:39 GMT]]></title><description><![CDATA[<p>Aber nimm dir bitte keine Beispiel am Stil. <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f609.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--winking_face"
      title=";)"
      alt="😉"
    /><br />
Das &quot;alles in den Buttonclick&quot; ist nämlich ein erschütterndes Beispiel für die dem BCB gern nachgesagten Nachteile.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/479889</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/479889</guid><dc:creator><![CDATA[Jansen]]></dc:creator><pubDate>Sun, 14 Mar 2004 01:22:39 GMT</pubDate></item><item><title><![CDATA[Reply to BCB - Spiel Beispiel kommentiert (wie mach ich das mit Canvas) 2D Game on Sun, 14 Mar 2004 01:38:24 GMT]]></title><description><![CDATA[<p>Jansen schrieb:</p>
<blockquote>
<p>Aber nimm dir bitte keine Beispiel am Stil. <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f609.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--winking_face"
      title=";)"
      alt="😉"
    /><br />
Das &quot;alles in den Buttonclick&quot; ist nämlich ein erschütterndes Beispiel für die dem BCB gern nachgesagten Nachteile.</p>
</blockquote>
<p>Dem kann ich nur Zustimmen, eine gute Möglichkeit einer Game-schleife kann<br />
man z.B. mit Application-&gt;OnIdle erstellen, vor allem wenn das Spiel länger dauert</p>
<p>Ich möchte daher nochmals drauf hinweisen, wie in den andere Threads zu diesem<br />
Thema, das ich es auf die Schnelle runterprogge und kein Spiele-Entwickler bin,<br />
ich möchte mit dem Beispiel nur die Möglichkeiten des BCB vorzeigen.</p>
<p>Ihr sollt auch nicht den Code einfach Kopieren und abändern, sondern die<br />
Methoden verstehen und mit diesem Wissen euer Spiel erstellen. Schaut euch<br />
auch das Beispiel im Example-Ordner des BCB an unter Games &quot;Earth Pong&quot;<br />
dort wird es auch in dem Application-&gt;OnIdle Event gesetzt.</p>
<p>Und <strong>Alles Andere</strong> zum Thema Spieleentwicklung bitte in das entsprechende Board.</p>
<p>In diesem Sinne, Micha.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/479892</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/479892</guid><dc:creator><![CDATA[promicha]]></dc:creator><pubDate>Sun, 14 Mar 2004 01:38:24 GMT</pubDate></item><item><title><![CDATA[Reply to BCB - Spiel Beispiel kommentiert (wie mach ich das mit Canvas) 2D Game on Mon, 15 Mar 2004 09:53:31 GMT]]></title><description><![CDATA[<blockquote>
<p>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.</p>
</blockquote>
<p>Und warum?</p>
<blockquote>
<p>//Auf dem Assemblercode gehe ich nicht nähr ein, man kann auch ein WAVE-Sample verwenden<br />
//und mit PlaySound abspielen<br />
unsigned short pitch = (unsigned short) (frq);<br />
frq = 0;<br />
asm mov bx, pitch<br />
asm mov ax, 34DDh<br />
....</p>
</blockquote>
<p>Hast du nicht gelesen das ich das nicht näher erkläre?</p>
<p>Ansonsten frag doch &quot;W. Posur&quot;, den er hat diesen <a href="http://www.c-plusplus.net/forum/viewtopic.php?t=39150" rel="nofollow">FAQ-Beitrag</a> gepostet!</p>
<p>gruß Micha</p>
]]></description><link>https://www.c-plusplus.net/forum/post/480617</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/480617</guid><dc:creator><![CDATA[promicha]]></dc:creator><pubDate>Mon, 15 Mar 2004 09:53:31 GMT</pubDate></item><item><title><![CDATA[Reply to BCB - Spiel Beispiel kommentiert (wie mach ich das mit Canvas) 2D Game on Mon, 15 Mar 2004 09:58:23 GMT]]></title><description><![CDATA[<p>Zur Assemblerfrage siehe <a href="http://www.c-plusplus.net/forum/viewtopic.php?p=480618" rel="nofollow">hier</a>.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/480620</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/480620</guid><dc:creator><![CDATA[Jansen]]></dc:creator><pubDate>Mon, 15 Mar 2004 09:58:23 GMT</pubDate></item><item><title><![CDATA[Reply to BCB - Spiel Beispiel kommentiert (wie mach ich das mit Canvas) 2D Game on Fri, 14 Jan 2005 21:55:24 GMT]]></title><description><![CDATA[<p>promicha schrieb:</p>
<blockquote>
<p>Hier könnt ihr euch das Projekt herunterladen <a href="http://bcb.24day.de/SoloPong.zip" rel="nofollow">http://bcb.24day.de/SoloPong.zip</a><br />
Und Hier nur die Ausführbare EXE, also das Spiel zum Zocken <a href="http://bcb.24day.de/SoloPong.exe" rel="nofollow">http://bcb.24day.de/SoloPong.exe</a></p>
</blockquote>
<p>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.</p>
<p>könntest du die files vieleicht an meine e-mail schicken ?</p>
<p>MfG djcccp</p>
]]></description><link>https://www.c-plusplus.net/forum/post/696726</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/696726</guid><dc:creator><![CDATA[djcccp]]></dc:creator><pubDate>Fri, 14 Jan 2005 21:55:24 GMT</pubDate></item><item><title><![CDATA[Reply to BCB - Spiel Beispiel kommentiert (wie mach ich das mit Canvas) 2D Game on Sat, 15 Jan 2005 09:53:00 GMT]]></title><description><![CDATA[<p>Hallo</p>
<p>bei mir gehen die Links auch nicht ?</p>
<p>besser waere zu klaeren warum nicht anstatt sowas jedem per Mail zu senden</p>
<p>MfG<br />
Klaus</p>
]]></description><link>https://www.c-plusplus.net/forum/post/696913</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/696913</guid><dc:creator><![CDATA[KlausB]]></dc:creator><pubDate>Sat, 15 Jan 2005 09:53:00 GMT</pubDate></item><item><title><![CDATA[Reply to BCB - Spiel Beispiel kommentiert (wie mach ich das mit Canvas) 2D Game on Sat, 15 Jan 2005 15:09:27 GMT]]></title><description><![CDATA[<p>Ja stimmt. Kennt jemand <strong>promicha's</strong> e-mail....oder wie man sonst mit ihm in kontak tretten kann ?</p>
<p>MfG djcccp</p>
]]></description><link>https://www.c-plusplus.net/forum/post/697209</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/697209</guid><dc:creator><![CDATA[djcccp]]></dc:creator><pubDate>Sat, 15 Jan 2005 15:09:27 GMT</pubDate></item><item><title><![CDATA[Reply to BCB - Spiel Beispiel kommentiert (wie mach ich das mit Canvas) 2D Game on Sat, 15 Jan 2005 15:30:15 GMT]]></title><description><![CDATA[<p>hi,</p>
<p>hier hab ich mal ein paar interessante Links zusammengesucht, vieleicht hilft dir das weiter <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f921.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--clown_face"
      title=":clown:"
      alt="🤡"
    /></p>
<p><a href="http://cc.borland.com/cc/ccweb.exe/listing?id=17289" rel="nofollow">http://cc.borland.com/cc/ccweb.exe/listing?id=17289</a></p>
<p><a href="http://www.monroeccc.edu/ckelly/game_programming.htm" rel="nofollow">http://www.monroeccc.edu/ckelly/game_programming.htm</a></p>
<p>MFG Fregger</p>
]]></description><link>https://www.c-plusplus.net/forum/post/697229</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/697229</guid><dc:creator><![CDATA[Fregger]]></dc:creator><pubDate>Sat, 15 Jan 2005 15:30:15 GMT</pubDate></item><item><title><![CDATA[Reply to BCB - Spiel Beispiel kommentiert (wie mach ich das mit Canvas) 2D Game on Sat, 15 Jan 2005 15:34:44 GMT]]></title><description><![CDATA[<p>djcccp schrieb:</p>
<blockquote>
<p>Kennt jemand <strong>promicha's</strong> e-mail....oder wie man sonst mit ihm in kontak tretten kann ?</p>
</blockquote>
<p>Warum wirst du nicht mal einen Blick in sein Benutzerprofil?</p>
<p>PS.<br />
<a href="http://members.aol.com/apostrophs/" rel="nofollow">Katastrophenalarm</a>! <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f609.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--winking_face"
      title=";)"
      alt="😉"
    /></p>
]]></description><link>https://www.c-plusplus.net/forum/post/697231</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/697231</guid><dc:creator><![CDATA[Jansen]]></dc:creator><pubDate>Sat, 15 Jan 2005 15:34:44 GMT</pubDate></item></channel></rss>