Autor Beitrag
-dna-
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 18



BeitragVerfasst: Fr 09.11.07 00:05 
Da das Thema eher hier hin als in die Projektecke www.delphi-forum.de/....php?p=471218#471218 passt, führe ich es mal hier weiter:

user profile iconMarc. hat folgendes geschrieben:
Aber etwas Anderes: Ich eigne mir gerade selbst OpenGL an, stehe allerdings noch ganz am Anfang und muss ehrlich sagen, dass mich Dein Programm bereits sehr beeindruckt hat. Könntest Du mir eventuell Einblicke in den SourceCode gewähren? Einfach um zu sehen, wie etwas aufwendigeres OpenGL-Spiel letzendlich von innen ausschaut.

Das kann ich gern machen. Ich legs mal gleich in diesen Thread falls es noch jemanden interessiert. Im Prinzip ist OGL gar nicht schwer. Aber wenn man sich die ganzen Tutorials anschaut und daraufhin ein eigenes Projekt bauen will, dann hat man ganz fix Spaghetti-Code. Ich will damit sagen, dass man sich eine Menge Hilfsklassen suchen oder selbst schreiben muss. Ich denke da in meinem Fall an:
Einen Menucontroller(GUI), Fontmanager (GUI, ingame), Soundmanager, Texturmanager, 3D-Modelloader, 3D-Szenenverwaltung, Partikelengine und natürlich etwas, was den Netzwerkkram handhabbar macht. Sowas kann man nich allein schreiben (also ich zumindest nicht (Arbeit, Haus, Kinder und Hobbies gehen nun mal vor :) ). Daher stammen z.B. die GUI von Kollegen aus dem DelphiGL-Forum. Den passenden Controller rundrum hab ich wiederum gebaut, da hat jeder seine Vorlieben. Weiterhin sind der 3D-Loader und die Partikelengine (Fireblade) im Kern nicht von mir. Wenn man sich den ganzen Kram erstmal zusammengeschustert hat, dann ist es recht angenehm. Man hat dann also eine Mainloop:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
  game.initgame;
  while ( Done <> -1 ) do
  begin
    glHandleEvents;   //  Tastatur, Maus, Video
    Render(FALSE);    //  alles hinschmieren
    ProcessGame(NIL); //  das eigentliche Spiel fortführen
    Inc(FPSCount);    //  der Rest ist FPS-Counter und Frameratenbegrenzer (Weil P4-CPU's sonst an die Decke gehen :) )
    game.FrameElapsedTime := GetTimeNeeded(game.TimeStruct);
    while GetTimeNeeded(game.TimeStruct) <0.01500 do sleep(1);
    game.FrameElapsedTime := GetTimeNeeded(game.TimeStruct);
    BeginCounting(game.TimeStruct);
    if Game.gameEnds then Done := -1;
  end;
  game.settings.SaveToFile;


Beim Rendern passiert folgendes (ich kürze mal stellenweise)

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
  
  glDisable(GL_LIGHTING);
  glPushMatrix;
  glEnable(GL_CULL_FACE);

  for stein:=0 To Pred(Steine.count) do
  begin
    if Picken then  //  Im Pickmodus (Mausklick) wird ohne Texturen gerendert. Kommt daher, dass die 
                    //  originale OGL-Selektion mit NVidia-Karten unperformant ist. Daher ein Rendern ohne Texturen
                    //  sondern nur mit Farben, welche die ObjektID enthalten. Wenn man nun die Farbe an der               
                    //  Klickposition ausliest, hat man wieder die ObjektID.
    begin
      glDisable(GL_TEXTURE_2D);
      glColor4b(KlickOffset+stein, 0, 0, 0);
      glTranslatef( Steine.steine[stein].Posi.x+Steine.steine[stein].Offset.x,
                    Steine.steine[stein].Posi.y+Steine.steine[stein].Offset.y,  
                    Steine.steine[stein].Posi.z+Steine.steine[stein].Offset.z);
      glRotatef( Steine.steine[stein].Posi.rx,
                 Steine.steine[stein].Posi.rx, 
                 Steine.steine[stein].Posi.ry, 
                 Steine.steine[stein].Posi.rz);
      if Steine.Steine[stein].aktiv
         then glCallList(Steine.GetDispListe(stein));  // Ich verwende Displaylisten. Damit läuft alles auf der Graka.
    end else
    begin  // Jetzt normal rendern. Mit Texturen und später noch mehr Schnickschnack (Licht/Partikel/...).
      glEnable(GL_TEXTURE_2D);
      glEnable(GL_BLEND);
      glColor4f(1,1,1,Steine.Steine[stein].transparenz);
      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);       
      glTranslatef( Steine.steine[stein].Posi.x+Steine.steine[stein].Offset.x,   //Wo ist das Objekt, Translation
                    Steine.steine[stein].Posi.y+Steine.steine[stein].Offset.y,      
                    Steine.steine[stein].Posi.z+Steine.steine[stein].Offset.z);
      glRotatef( Steine.steine[stein].Posi.rx,                                  // Ist es gedreht? Rotation
                 Steine.steine[stein].Posi.rx, 
                 Steine.steine[stein].Posi.ry, 
                 Steine.steine[stein].Posi.rz);
      if Steine.Steine[stein].aktiv
         then glCallList(Steine.GetDispListe(stein));                            // Rendern
      glDisable(GL_BLEND);
    end;
    glPopMatrix;
    glPushMatrix;
  end;
  if not Picken then Partikel.ParticleEffect(FrameElapsedTime);      // Partikeleffekte einfügen. Als Parameter noch 
  glPopMatrix;                                                       // die abgelaufene Zeit zum letzten Rendern. Damit
                                                                     // Läuft es auf allen Maschinen gleich schnell.

  ...

   // Jetzt noch das Spielerinterface (in den Ecken) reinrendern. Das passiert im Orthomodus. Man kann es also
   // pixelgenau setzen

   for PlayerNum := 0 to Pred(steine.Players.GetNumPlayer) do
   begin
     if PlayerNum = steine.Players.GetActivePlayer then
     begin
       ZeitOffset := MilliSecondsBetween(ZeitMesspunkt,now);
     end else
     begin
       ZeitOffset := 0;
     end;

     score := steine.Players.GetScoreOfPlayer(PlayerNum);
     zeit  := steine.Players.GetTimeOfPlayer(PlayerNum);

     if GameState = gsInGame then
     begin
       try
         ShowPlayerInterface(PlayerNum,score, zeit+ZeitOffset)
       except
         sleep(2);
       end;
     end else
     begin
       try
         ShowPlayerInterface(PlayerNum,score, zeit);
       except
         sleep(2);
       end;
     end;
   end;

Der nächste große Punkt ist das Spiel selbst. Dort läuft alles über Gamestates ab. Ich habe das anfangs nicht gemacht. Man programmiert sich tot. Mit der Statusmaschine wird das ganze zum Kinderspiel. (Der Knackpunkt ist eine Prozedure, welche die Gamestates wechselt. Dort kann man beim verlassen eines Gamestates notwendige Aktionen veranlassen
Beispiel: Intro --> Hauptmenu
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
ChangeGameState(gsmainmenu);

...

 // TGame hat eine Variable Gamestate, welche den aktuellen Status trägt  //

procedure TGame.ChangeGameState(NewState: TGameState);
begin
  if NewState <> GameState then
  begin
    case Gamestate of
    gsIntro: // Intro beenden. Klasse freigeben. ...
    end; //case

    case Newstate of
    gsMainmenu: // Laden, initialisieren, starten
    end; //case
    Gamestate := NewState;  // und den neuen State setzen.
  end;
end;


Im Moment laufen 4 oder 5 Statemachines im Spiel. Eine für's Spiel selbst, eine für den Spielerzug, eine für den Computergegner, eine für die 3D-Szenenverwaltung und eine entsteht grade noch für das Netzwerk.

Ich merke aber grade, das wird ein Mordsthema. Hast Du evtl. schon was mit OpenGL probiert? Was willst Du machen? Hast Du evtl. spezielle Fragen? Gehts Dir mehr um das Rendern, oder um das Design des Grundgerüsts allgemein?

Grüße, DNA

Moderiert von user profile iconNarses: Code- durch Delphi-Tags ersetzt
Marc.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 1876
Erhaltene Danke: 129

Win 8.1, Xubuntu 15.10

BeitragVerfasst: Fr 09.11.07 16:19 
user profile icon-dna- hat folgendes geschrieben:
Hast Du evtl. schon was mit OpenGL probiert? Was willst Du machen? Hast Du evtl. spezielle Fragen? Gehts Dir mehr um das Rendern, oder um das Design des Grundgerüsts allgemein?

Hey!
Danke für Deine Mühe. :P
Ich habe mir bis jetzt die ersten vier Kapitel auf DelphiGL.com angeschaut. Bis dahin imo keine Probleme, mit Matrizen kenne ich mich aus. :)
Doch habe ich allerdings gemerkt, dass der Code bei mehrern Objekten bereits sehr unübersichtlich wird, allerdings ist die Idee, alles auf verschiedene Units aufzuteilen, auf fremde GUI's zurückzugreifen und Statemachines zu verwenden mit Sicherheit nicht verkehrt.
Konkrete Fragen habe ich jetzt gerade nicht, aber ich werde mich demnächst melden.

Gamestates und Displaylist habe ich übrigens zuvor noch nicht gekannt. ;)

grüße,
Marc
-dna- Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 18



BeitragVerfasst: Fr 09.11.07 18:34 
Zitat:
user profile iconMarc.
Ich habe mir bis jetzt die ersten vier Kapitel auf DelphiGL.com angeschaut. Bis dahin imo keine Probleme, mit Matrizen kenne ich mich aus. :)
Das ist von Vorteil. :)

Zitat:
user profile iconMarc.
Doch habe ich allerdings gemerkt, dass der Code bei mehrern Objekten bereits sehr unübersichtlich wird, allerdings ist die Idee, alles auf verschiedene Units aufzuteilen, auf fremde GUI's zurückzugreifen und Statemachines zu verwenden mit Sicherheit nicht verkehrt.
Konkrete Fragen habe ich jetzt gerade nicht, aber ich werde mich demnächst melden.

Gern. Am besten lernt man aber, wenn man probiert.


Zitat:
user profile iconMarc.Gamestates und Displaylist habe ich übrigens zuvor noch nicht gekannt. ;)
Gamestates sind genial einfach für sowas.
Und die Displaylisten sind schon recht praktisch, haben aber auch Nachteile. Sie sind statisch, man kann sie nach dem Laden nicht mehr ändern. Daher sind VBO (Vertex Buffer Object) im Moment der Hit. Diese kann man nachträglich noch Modifizieren (Also die Vertices selbst). Aber das muß man sich mal in Ruhe anschauen und eine Klasse rundrum bauen, damit man die ordentlich handhaben kann.

Grüße, DNA