Tutorial Quickstart

Aus DGL Wiki
Version vom 8. Dezember 2005, 22:27 Uhr von DGLBot (Diskussion | Beiträge) (Bot: repariere tote Links zu Bildern)

Wechseln zu: Navigation, Suche

Quickstart: Delphi & OpenGL

Einleitung

Willkommen beim Delphi & OpenGL Quickstart. Diese kurze Einleitung soll euch auf die Tutorials bei DelphiGL.com und allgemein auf die Grafikprogrammierung mit OpenGL und Delphi vorbereiten. Dieser Quickstart ist kein Tutorial für sich, sondern soll ein Grundgefühl vermitteln wie OpenGL und Delphi miteinander arbeiten.

Wozu OpenGL? Mit OpenGL kann man eine Vielzahl von Aufgaben bewältigen. Ob man Forschungsergebnisse aller Art visualisieren, 2D oder 3D Spiele schreiben oder einfach seiner Anwendung eine Oberfläche geben möchte, die nicht dem windowsgrauen Standardlook entspricht. All das ist möglich mit OpenGL.

Wie fange ich an?

Genau zwei Dinge braucht der OpenGL-Programmierer um effektiv arbeiten zu können:

  1. Einen OpenGL Header
  2. Eine Codebasis von der aus man neue Projekte starten kann (ein sog. Template)


Der OpenGL-Header

Das is ja easy! Denn Delphi bringt ja schon einen OpenGL-Header mit...

STOP!

Denn wir reden von einem guten Header. Leider ist der original von Delphi mitgelieferte Header alles andere als zu empfehlen. Er ist fehlerhaft, hält sich nicht an OpenGL-Normen und außerdem ist er absolut veraltet.

Was nun? Ganz einfach: Bei DelphiGL.com (kurz DGL) gibt es DEN OpenGL-Header für alle Pascalsprachen: Die DGLOpenGL.pas.

Diesen solltet Ihr euch jetzt besorgen, wenn Ihr ihn nicht schon habt. Der Header wird bei neuen OpenGL-Versionen vom DGL-Team aktualisiert.

Codebasis/Templates

So... das war schon alles was Ihr aus dem Netz benötigt. Den Rest machen wir jetzt per Hand.

Im nächsten Kapitel zeige ich euch wie man sich ein einfaches Template schreibt. Natürlich hat DelphiGL.com auch bereits fertige Lösungen, die durchaus zu empfehlen sind und auch extra Features wie Vollbildrendering besitzen. ABER aus Erfahrung kann ich sagen: Man findet sich im eigenen Code viel einfacher zurecht. (Und die Extras kann man nachher immer noch einbauen.)


Das Template - Delphi fit für OpenGL machen

Bevor man wirklich loslegen kann, muss noch die runtergeladene DGLOpenGL.pas an den richtigen Ort gebracht werden. Gut wäre z.B. sie in das Verzeichnis "\lib" in Eurem Delphiverzeichnis zu legen. (Wenn ihr den DGLSDK verwendet wurden die Suchpfade schon eingerichtet.)

Dann startet mal Delphi. Vor euch sollte jetzt ein leeres Projekt erscheinen. Das leere Formular kann gleich minimiert werden, denn jetzt wird erstmal hübsch gecodet.

(Am Ende von Kapitel 1.3 findet ihr den Kopf der Template-Klasse. Dort seht ihr auch welche Variablen, von welchem Typ deklariert werden müssen. )

Initialisieren von OpenGL

Dieser Teil ließ früher dem OpenGL Anfänger die Haare nicht nur zu Berge stehen, sondern gleich ausfallen. Dank der DGLOpenGL.pas wurde das aber um Längen einfacher.

Zuerst einmal solltet Ihr die DGLOpenGL.pas in die uses-Klausel des interface-Teils der Unit1 schreiben.

Die eigentliche Initialisierung soll direkt beim Erstellen des Formulars gemacht werden. Deshalb kommt der folgende Quelltext ins OnCreate-Ereignis des Formulars.

procedure TForm1.FormCreate(Sender: TObject);
begin
  DC:= GetDC(Handle);
  if not InitOpenGL then Application.Terminate;
  RC:= CreateRenderingContext( DC,
                               [opDoubleBuffered],
                               32,
                               24,
                               0,0,0,
                               0);
  ActivateRenderingContext(DC, RC);
end;

Zeile 3: Hier wird der Gerätekontext (Device Context) von Formular Form1 abgefragt.

Zeile 4: Mit InitOpenGL wird OpenGL initialisiert. Wenn das nicht funktioniert wird die gesamte Anwendung sofort beendet.

Zeile 5: Hier wird der Renderkontext erzeugt. Den braucht OpenGL zum Zeichnen auf das Formular. Was die Parameter genau bewirken lernt ihr im Tutorial_lektion1.

Zeile 11: Abschließend wird der Renderkontext aktiviert. OpenGL ist jetzt prinzipiell startbereit.

Info DGL.png DC und RC sind Eigenschaften des Formulars. Siehe Definition des Templateformulars.

Nach dieser durchaus simplen Initialisierung (man kann auch alles per Hand machen was InitOpenGL macht!) steht OpenGL ziemlich nackt da. Soll heißen, alle OpenGL Eigenschaften/Zustände stehen auf den definierten Anfangswerten. Es kommt aber durchaus oft - eigentlich ständig - vor, dass bestimmte Einstellungen von OpenGL benutzt werden sollen. Deshalb schreiben wir uns noch eine kleine Zusatzprozedur: SetupOpenGL

procedure TForm1.SetupGL;
begin
  glClearColor(0.3, 0.4, 0.7, 0.0); //Hintergrundfarbe
  glEnable(GL_DEPTH_TEST);          //Tiefentest aktivieren
  glEnable(GL_CULL_FACE);           //Backface Culling aktivieren
end;

Was hier passiert wird durch die Kommentare bereits erklärt (Für mehr Infos siehe Tiefentest bzw. Backface Culling). Die Hintergrundfarbe könnt ihr nach Belieben einstellen. (Wenn ihr später einmal geschlossene Szenen rendern wollt, dann ist es günstig eine sehr schräge Farbe als Hintergrundfarbe einzustellen, so findet man leichter Fehler in der Szene.)

Außerdem hat man ja hin und wieder auch noch globale Variablen, die man initialisieren möchte. Da wir mit solchen Sachen unser schön aufgeräumtes FormCreate nicht zumüllen wollen bietet sich ein Unterprogramm namens InitGlobals oder kurz Init an. Beide Unterprogramme (SetupGL und Init) sollten am Ende von FormCreate gerufen werden:

  [...]
  ActivateRenderingContext(DC, RC);
  SetupGL;
  Init;
end;

Die Ereignisbehandlung

Für OpenGL sind vor allem die Ereignisse von Bedeutung, die an der Zeichenfläche von OpenGL herumwerkeln. Da OpenGL direkt auf das Formular (oder auch auf ein Panel) zeichnet, müssen Ereignisse, die diese Zeichenfläche ändern, behandelt werden. Dies wären das OnCreate- und das OnDestroy-Ereignis.

Zuerst FormResize:

procedure TForm1.FormResize(Sender: TObject);
var tmpBool : Boolean;
begin
  glViewport(0, 0, ClientWidth, ClientHeight);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity;
  gluPerspective(45.0, ClientWidth/ClientHeight, NearClipping, FarClipping);    

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity;
  IdleHandler(Sender, tmpBool);
end;

Außerdem müssen im const Teil die beiden Konstanten Near- bzw. FarClipping definiert werden. Diese geben die Entfernung für die Clippingebenen (Szenenbegrenzung) an.

  NearClipping = 1;
  FarClipping  = 1000;

Zu FormResize:

Zeile 2: Diese Boolean-Variable wird in Zeile 11 verwendet und ist nur ein Dummy.

Zeile 4: Mittels glViewport sagt Ihr OpenGL wie groß die OpenGL-Ausgabe werden soll. Genau diese Größe hatte sich ja durch das Resize verändert.

Zeile 5/9: Hier seht Ihr 2 der 3 möglichen Matrixmodi. GL_PROJECTION wird benutzt um nachfolgend die OpenGL-Ausgabe zu manipulieren, GL_MODELVIEW benutzt man um OpenGL mit Daten zu füttern.

Zeile 6: glLoadIdentity füllt die aktuelle Matrix mit der Identitätsmatrix.

Zeile 7: Hier wird eingestellt wie der Betrachter die Welt sehen soll.

Zeile 11: Was der IdleHandler macht kommt später im Abschnitt 1.3.3 (Zeichenroutine).


Nun noch schnell das FormDestroy:

procedure TForm1.FormDestroy(Sender: TObject);
begin
  DeactivateRenderingContext;
  DestroyRenderingContext(RC);
  ReleaseDC(Handle, DC);
end;

Was es macht? Steht doch da: Den RenderingContext deaktivieren und freigeben.


Die Zeichenroutine

Das Herzstück unseres Templates fehlte bisher. Irgendwann muss der Grafikkarte ja auch gesagt werden, was sie denn überhaupt ausgeben soll. Das kommt jetzt: TForm1.Render

procedure TForm1.Render;
begin
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity;
  gluPerspective(45.0, ClientWidth/ClientHeight, NearClipping, FarClipping);

  glTranslatef(0, 0, -5);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity;

  glBegin(GL_QUADS);
    glColor3f(1, 0, 0); glVertex3f(0, 0, 0);
    glColor3f(0, 1, 0); glVertex3f(1, 0, 0);
    glColor3f(0, 0, 1); glVertex3f(1, 1, 0);
    glColor3f(1, 1, 0); glVertex3f(0, 1, 0);
  glEnd;

  SwapBuffers(DC);
end;

Zeile 3: Dieser Aufruf sorgt dafür, dass der Farbpuffer und Tiefenpuffer gelöscht werden. Wenn man das nicht macht, sieht man alles mögliche, nur nicht das was Ihr rendern wollt. Probiert es ruhig mal ohne aus! Man wird dadurch nicht dümmer.

Zeile 7: Hier wird wieder die Perspektive gesetzt. Dieser Aufruf und der bei FormResize müssen von den Parametern identisch sein. Sonst sieht die Ausgabe nach einem Resize kurz anders aus. Wenn sich die Perspektive zwischen den Renderdurchgängen nicht ändert kann das auch weg gelassen werden.

Zeile 9: Dieser Aufruf verschiebt die "Kamera" (so etwas gibt es eigentlich nicht, aber da wir uns gerade in der GL_PROJECTION Matrix befinden passt diese Beschreibung am besten) etwas nach hinten. Schließlich wollen wir das, was wir zeichnen auch sehen. Alles was zu nah ist wird durch die Near-Clipping Plane abgeschnitten.

GlShadeModel SMOOTH.jpg

Zeile 14: glBegin/glEnd kapseln die eigentlichen Zeichenbefehle. Diese sorgen hier für ein hübsches buntes Viereck. Das soll für den ersten Test ausreichen. Wichtig ist in dem Zusammenhang noch folgendes: OpenGL ist es egal woher ein Befehl kommt. Alles wird ausgewertet und landet unter Umständen im Framebuffer. Ihr könnt also ein Unterprogramm, welches ein Unterprogramm, welches ... ..., welches die OpenGL Befehle enthält schreiben. Das interessiert OpenGL bzw. die Grafikkarte überhaupt nicht.

Zeile 21: SwapBuffers sorgt Ihr dafür, dass der Inhalt des Framebuffers auf dem Bildschirm erscheint. Ohne diesen Befehl seht Ihr gar nichts von OpenGL. (Interessanter Artikel dazu: Doppelpufferung)


So...ganz toll. Jetzt habt Ihr Eure Zeichenfunktion ... und nun? Irgendwie müsst Ihr diese auch aufrufen. Das wäre aber zu einfach. Die Ausgabe ändert sich ja normalerweise (z.B. in Spielen). Deshalb muss die Zeichenfunktion immer wieder ausgegeben werden. Dazu gibt es zwei Möglichkeiten, die beide Ihre Vor- und Nachteile haben.

Argument Timer OnIdle
Maximale Framezahl erreichbar
"Benchmark"
nein ja
Framezahl steuerbar ja bedingt (/umständlich)
Für flüssige Animationen nutzbar
(Egoshooter)
bedingt/schlecht ja
Für Menüs nutzbar ja ja
Für einfache Animationen nutzbar
(Strategiespiele)
ja ja
Laptopfreundlich (Anti-Akku-Killer) ja NEIN!

Wer einfache Anwendungen schreiben möchte, die auch mit 25 FpS (Bilder Pro Sekunde) auskommen und den Akku von Laptopusern schonen will, sollte die Timervariante nutzen. Wer die Potenziale der Grafikkarten voll ausnutzen möchte sollte OnIdle verwenden.


Methode 1: Timer

Bei dieser Methode muss ein Timer (zu finden bei den Systemkomponenten) auf das Formular gezogen werden. Der Timer besitzt eine Eigenschaft names "Interval". Mit dieser Eigenschaft kann man einstellen nach wie vielen Millisekunden das Ereignis OnTimer ausgelöst wird. Man kann "Interval" nicht beliebig verkleinern. Werte unter 25 können vom Standardtimer den Windows verwendet nicht mehr korrekt erzeugt werden.

Der Inhalt von OnTimer könnte dieser sein:

procedure TForm1.Timer1Timer(Sender: TObject);
begin
   inc(FrameCount);
   Render;
   If FrameCount = 20 then
      begin
           ErrorHandler;
           FrameCount := 0;
      end;
end;

Tiefgreifende Erklärungen sind hier nicht notwendig. Was der ErrorHandler ist wird nach der Methode 2 erklärt.


Methode 2 : OnIdle

OnIdle ist ein besonderes Ereignis, welches das gesamte Programm betrifft. Wenn die Anwendung nichts zu tun hat, also faul ist (engl. idle), tritt das Ereignis ein.

Die Methode mit OnIdle kann gleich mit zum Auswerten der Framezahlen (Anzahl Bildwiederholungen pro Sekunde) benutzt werden (Framecounter). Der nachfolgende Code enthält selbigen bereits.

procedure TForm1.IdleHandler(Sender: TObject; var Done: Boolean);
begin
  StartTime:= GetTickCount;
  Render;
  DrawTime:= GetTickCount - StartTime;
  Inc(TimeCount, DrawTime);
  Inc(FrameCount);

  if TimeCount >= 1000 then begin
    Frames:= FrameCount;
    TimeCount:= TimeCount - 1000;
    FrameCount:= 0;
    Caption:= InttoStr(Frames) + 'FPS';
    ErrorHandler;
  end;

  Done:= false;
end;

Zeile 3: Mittels GetTickCount wird die Systemzeit gemessen. Dies ist nicht nötig um erfolgreich zu zeichnen, sondern dient ausschließlich der Berechnung der Framerate. Diese wiederum ist ein guter Performancemesser.

Zeile 4: Hier erfolgt der Aufruf unserer Zeichenroutine.

Zeile 9: Der hier angeordnete Block wird nur pro Sekunde einmal ausgeführt und sorgt dafür, dass die Framerate angezeigt wird. Außerdem wird der Errorhandler aufgerufen.

Zeile 14: Der Errorhandler wird im Anschluß beschrieben.

Zeile 17: Wenn Done nach der Ausführung false ist und das Programm wieder nichts zu tun hat, wird OnIdle erneut ausgeführt. Wenn Done = true ist wird OnIdle nur einmal ausgeführt.


Um auf das "Idle-Event" reagieren zu können, müsst ihr jetzt nur noch diese Funktion an das Event koppeln. Das macht ihr, indem ihr den nachfolgenden Code in die letzte Zeile eurer FormCreate-Methode schreibt.

Application.OnIdle := IdleHandler;

Wie ihr bald selbst feststellen werdet, sorgt diese Methode für eine hundertprozentige Prozessorauslastung. Umgehen könnt ihr dies mit einem kleinen Trick:

Fügt vor dem "Done := false" (Zeile 17 des IdleHandlers) noch ein sleep(1) oder sleep(5) ein. Dadurch sinkt die Prozessorlast auf ca. 80%, was schon ein Fortschritt ist.


Der ErrorHandler - Fehler erkannt, Fehler gebannt

Der Errorhandler ist wieder eine total einfache Funktion, denn OpenGL bietet von Haus aus eine Möglichkeit OpenGL-Fehler zu erkennen. Deshalb ist der ErrorHandler auch so klein:

procedure TForm1.ErrorHandler;
begin
  Form1.Caption := gluErrorString(glGetError);
end;

Enttäuscht? Ihr könnt den ErrorHandler nach belieben auch komplexer machen. Zum Beispiel könnt Ihr anstatt die Fehler im Fenstertitel anzuzeigen lieber den Fehler in ein Logfile schreiben. Ganz nebenbei: Falls kein Fehler auftritt liefert glGetError GL_NO_ERROR.

Das fertige Templateformular

Eure Klasse TForm1 sollte also jetzt so, oder so ähnlich aussehen:

TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure IdleHandler(Sender: TObject; var Done: Boolean);
    procedure FormResize(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private    { Private-Deklarationen }
    StartTime, TimeCount, FrameCount  : Cardinal; //FrameCounter
    Frames, DrawTime                  : Cardinal; //& Timebased Movement
    procedure SetupGL;
    procedure Init;
    procedure Render;
    procedure ErrorHandler;
  public    { Public-Deklarationen }
    DC                                : HDC;  //Handle auf Zeichenfläche
    RC                                : HGLRC;//Rendering Context
  end;

Zeile 3: Wie man sieht benutzt mein Template die zweite Methode (OnIdle).

Zeile 8: Was Timebased Movement ist könnt ihr ja mal nachlesen.

Zeile 14/15: HDC und HGLRC sind Typen die von Windows zur verfügung gestellt werden. Ihr findet sie in der Unit "Windows". (Diese Unit sollte bei einem neuen Projekt bereits durch Delphi eingebunden worden sein.)


So....fertig. Eigentlich seid Ihr jetzt soweit von den Tutorialschreibern so richtig mit OpenGL-Wissen voll gepumpt zu werden. Das nachfolgende Kapitel könnt Ihr euch trotzdem ruhigen Gewissens durchlesen. Wer es liest tappt vielleicht nicht gleich bei der ersten Frage im DGL-Forum ins berüchtigte Fettnäpfchen.



Tipps für den OpenGL Anfänger

Wenn man mit OpenGL anfängt ist man meist total perplex und ein "Das war ja einfach!" huscht einem nicht nur einmal über die Lippen. Vor allem zu Beginn Eurer OpenGL-Karriere werdet Ihr viel lernen und dabei nur auf verhältnismäßig geringen Widerstand stoßen. Aber glaubt mir es gibt ihn...

Häufig tauchen hoch motivierte OpenGL Anfänger im Forum auf und verkünden stolz, sie würden gerade an einer Engine arbeiten die "nur auf Doom 1 Niveau arbeiten soll".

Dazu gibt es häufig eine durchaus beachtliche Anzahl von Forenmitgliedern die dann ungefähr folgendes sagen: Fang gar nicht erst damit an. Vergiss es, und komm in einem Jahr noch mal darauf zurück!“ (Die Forensuche sollte euch einige dieser Threads zeigen)

Sind das böse Pessimisten, die Probleme nutzen um aufzugeben? Nein! Sie haben zumeist die Erfahrungen gemacht, die Ihr noch machen werdet. Deshalb will ich euch an dieser Stelle einweihen:


1. Je älter der Code ist desto besser wird er.

Leider nein. Code und Wein unterscheiden sich hier leider grundlegend. Viele ... die meisten ... eigentlich alle von uns bekommen Ausschlag, wenn sie sich ihren Code von vor 1 Jahr angucken. Erst wenn man so schätzungsweise 3 bis 5 Jahre mit OpenGL gearbeitet hat, hat man ein echtes Gefühl für den Code. Am Anfang hat man nämlich, ob man will oder nicht, die Tendenz schon nach den ersten 3 Wochen Programmierarbeit sich den Code so zu zerschießen, dass die weitere Arbeit keinen Spaß mehr macht.

Ich selbst wollte jetzt ein Projekt weiterbearbeiten, welches ungefähr ein 3/4 Jahr alt ist... Ich hab den Code weggeworfen und das fast fertige Spiel neu angefangen. (Glaubt mir: Aus solchen Fehlern lernt man!)


2. Große Projekte = Großer Ruhm

Stimmt! ABER Ruhm gibt es in der Szene nur für beendete Projekte. Und dreimal dürft Ihr raten was durch die im Punkt 1 angesprochenen Probleme meist nicht mit euren Projekten passiert ... .

Wenn Ihr OpenGL nur zur Visualisierung von z.B. wissenschaftlichen Ergebnissen benutzt, ist die Arbeit im Bezug auf OpenGL sehr übersichtlich. Wenn Ihr allerdings Spiele programmieren wollt, werdet Ihr schnell merken, dass Probleme auf euch zukommen werden, die euch beim Projektstart völlig unbekannt waren. Dies ist ein sicheres Zeichen dafür, dass doch noch etwas mehr Erfahrung nötig sein wird.

Und woher soll ich bitte Erfahrung nehmen?

Darin liegt der Trick:

Alle OpenGLer haben einmal "klein" angefangen. Berühmt-berüchtigt sind die 3DPong-Clone, die zahlreich im Internet anzufinden sind. Auch Tetris-, Memory- oder "Vier Gewinnt"-Clone sind solche stillen Zeugen eines großen Lernvorgangs. Das Besondere an solchen Spielen ist, dass die Spiellogik relativ einfach ist und euch damit nur wenig von der Visualisierung ablenkt.


Ihr denkt jetzt bestimmt "Was kann man denn schon in so nem Clon unterbringen?" VERDAMMT VIEL! Hier mal ein kleiner Auszug:

  • 3D-Spieldarstellung
  • 2D Menüführung (glOrtho)
  • Blending
  • Texturen
  • Licht und Materialien (Ihr werdet Augen machen!)
  • Selektion (Hin und wieder zum Haare raufen.)
  • Kamerasteuerung /Bewegung durch die Szene (Da gibt es ganze Tutorials dazu)

Als Ansporn solltet Ihr euch am Anfang ein kleines Ziel setzen und sagen "Ich möchte den besten und schönsten XYZ-Clon im ganzen Internet schaffen!". So etwas trifft in der Szene auf wesentlich mehr Anerkennung als wenn mal wieder ein Anfänger etwas von Engine und Doom X faselt.

Wenn Ihr ein oder zwei solcher Projekte abgeschlossen habt, und regelmäßig im Forum bzw. Wiki gelesen habt werdet Ihr schon merken, wenn Euer Traumprojekt endlich in Angriff genommen werden kann. (Ganz vergessen müsst ihr es nämlich doch nicht ;) )

Als kurze Zusammenfassung solltet Ihr euch merken:

  • Ein fertiges Projekt bringt euch Ruhm (Seelenbalsam).
  • Ein hübsches, fertiges Projekt bringt euch mehr Ruhm.
  • Ein großes, fertiges, hübsches Projekt bringt euch noch mehr Ruhm.
  • Ein abgebrochenes oder eingefrorenes Projekt bringt euch Frust.
  • Ein großes, abgebrochenes Projekt bringt euch noch mehr Frust, denn der verschwendeten Zeit werdet Ihr nachtrauern.


3.Das es lang dauert ist egal. Ich interessiere mich halt dafür.

Diese Aussage gilt nur dann, wenn Ihr schon über ein Jahr an ein und demselben Projekt gearbeitet habt. Anfänglich ist das kein Problem. Aber wenn man lange an etwas arbeitet und die gemachten Änderungen sind nicht sichtbar (weil sie z.B. den eigentlichen Motor der Anwendung betreffen und nicht die Ausgabe) dann verliert man schon mal die Lust. Folge sind die berüchtigten "Erfolgsmeldungen" wie: "Das Projekt wurde von mir bis auf weiteres aufs Eis gelegt. Ich werde sicherlich später daran weiterarbeiten." In Verbindung mit Punkt 1 und den letzen Satz aus Punkt 2 sollte euch das Endergebnis klar sein.

Nachwort

Soviel zur Euphoriebremse. OpenGL ist toll. OpenGL ist die Lösung für Eure Traumanwendung. Aber die wird auch mit OpenGL nicht von heute auf morgen programmiert. Deshalb heißt es Tutorials lesen kleine Testanwendungen schreiben um Effekte zu testen und Projekte bearbeiten. Dann wird es auch etwas mit den Traumprojekten. Zudem kann man anfangs auch versuchen in sehr kleinen Anwendungen einen Effekt auszuprobieren und diesen in Form eines kleinen Beispielprogramms oder eines Tutorials zu veröffenlichen. Somit lernt man nicht nur selbst etwas dazu...


Nach diesen unglaublich Weise klingenden Worten die ich mit einem hoch ernsten Gesicht geschrieben habe, könnt ihr euch jetzt hochmotiviert an die restlichen Tutorials machen.


Bis bald im Forum

Euer

Flash (Kevin Fleischer)


PS: Feedback wird nicht nur gewünscht, sondern ausdrücklich gefordert. Deshalb: Ab mit deinen Meinungen und Ratschläge ins Feedback-Forum!


Vorhergehendes Tutorial:
-
Nächstes Tutorial:
Tutorial_lektion1

Schreibt was ihr zu diesem Tutorial denkt ins Feedbackforum von DelphiGL.com.
Lob, Verbesserungsvorschläge, Hinweise und Tutorialwünsche sind stets willkommen.