Home » Tutorials » Grafik und Spiele » Figuren in 3D-Welten einsetzen

Figuren in 3D-Welten einsetzen

Neue Unit, neue Klasse

Um eine Figur ins Spiel zu bringen, sind eine Menge Aktionen nötig. Deshalb ist es sinnvoll, alle Methoden in einer neuen Unit zusammenzufassen, die ich G4DFigur nennen möchte. Die Methoden, die eine solche Figur mindestens braucht, sind allesamt in der Klasse TFigur vereinbart.
(Statt von einer Figur könnte man auch von Akteur, Bot, Kreatur, Gegner oder wie auch immer sprechen. Daher steht es Euch natürlich frei die Unit bzw. Klasse beispielsweise in G4DBot bzw. TBot umzubenennen. Allerdings kann unsere Figur hier einem echten Bot noch lange nicht das Wasser reichen.)
Verschaffen wir uns einen Überblick über die Genesis-Units, die in die Hauptunit eingebunden werden müssen:

G4D_Genesis, G4D_BaseType, G4D_XForm3D, G4D_geTypes,
  G4D_Vec3D, G4D_Vfile, G4DDriver, G4DFigur;

Neu gegenüber dem letzten Projekt ist lediglich die Unit G4DFigur, die es aber in sich hat. Denn unsere Figur lernt damit nicht nur (scheinbar) selbständig zu laufen, sondern sie kann auch mit Kollisionen umgehen, Treppen und die Schwerkraft sind für sie kein Problem. Dazu bedarf es einer Reihe weiterer Methoden, von denen viele jedoch für Voreinstellungen zuständig sind.
Auch hier zuerst die Uses-Liste:

uses
  Windows, Classes, Dialogs,
  G4D_Genesis, G4D_BaseType, G4D_XForm3D, G4D_geTypes, G4D_Vec3D,
  G4D_Vfile, G4D_Motion, G4D_Actor;

Als nächstes folgt die Vereinbarung der Klasse TFigur. Dabei habe ich eine ganze Reihe Vektoren aus der Hauptunit übernommen. So trifft man von ViewVector bis MaxVector nur gute Bekannte. Lediglich mit ScaleVector gibt es einen weiteren Vektor für die Größenverhältnisse, in denen die Figur im Spiel erscheinen soll. (Es leuchtet sicher ein, dass ich mich später für Bewegung und Kollisionskontrolle der Figur ebenfalls kräftig bei den Methoden aus TForm1 bediene.)

type
  TFigur = class
  private
    Actor      : pgeActor;      // Figurtyp
    ActorFile  : pgeVFile;      // Figurdatei
    ActorDef   : pgeActor_Def;  // Definitionsdaten
    ViewVector : geVec3D;       // aktuelle Position
    LookVector : geVec3D;       // neue Position
    TurnVector : geVec3D;       // Drehwinkel
    ScaleVector: geVec3D;       // Skalierungswerte
    MinVector  : geVec3D;       // min. Kollisionsgrenze
    MaxVector  : geVec3D;       // max. Kollisionsgrenze
    Motion     : pgeMotion;     // Animationphasen
    MCount     : geFloat;       // Animationszähler
    MStart     : geFloat;       // Startzeitpunkt
    MTime      : geFloat;       // Animationsdauer
    MStop      : geFloat;       // Endzeitpunkt
    MSpeed     : geFloat;       // Laufgeschwindigkeit
    isMoving   : Boolean;       // in Bewegung?
  public
    World: pgeWorld;
    constructor Create (Welt: pgeWorld; DateiName: String);
    procedure SetPosition (x,y,z: geFloat);
    procedure SetRotation (x,y,z: geFloat);
    procedure SetScale (x,y,z: geFloat);
    procedure SetRange (x0,y0,z0, x1,y1,z1: geFloat);
    procedure SetMotion
      (Speed: geFloat; Bewegung: String; Modus: Boolean);
    function  Collision: Boolean; virtual;
    procedure CheckGravity; virtual;
    procedure Walk; virtual;
    procedure Render (Zeit: DWord); virtual;
    destructor Destroy; override;
  end;

Nun zu den einzelnen Methoden. Zuerst geht es um die Erschaffung der Figur. Dabei beginnt die Create-Methode damit, allen Vektoren einen (vorläufigen) Startwert zuzuteilen:

constructor TFigur.Create (Welt: pgeWorld; DateiName: String);
begin
  inherited Create;
  // Startaufstellung/ausrichtung "nullen"
  geVec3d_Set (@Motion, 0.0, 0.0, 0.0);
  geVec3d_Set (@ViewVector, 0.0, 0.0, 0.0);
  geVec3d_Set (@LookVector, 0.0, 0.0, 0.0);
  geVec3d_Set (@TurnVector, 0.0, 0.0, 0.0);
  // Normale Größe
  geVec3d_Set (@ScaleVector, 1.0, 1.0, 1.0);
  // Begrenzungen
  geVec3d_Set (@MinVector,-20.0,-0.0,-20.0);
  geVec3d_Set (@MaxVector, 20.0, 0.0, 20.0);
  // ACT-Datei laden
  ActorFile := geVFile_OpenNewSystem (nil,
    GE_VFILE_TYPE_DOS, PChar(DateiName), nil, GE_VFILE_OPEN_READONLY);
  if ActorFile = nil then
    ExitError ('ACT-Datei lässt sich nicht öffnen!');
  // Definitionsdaten der Figur "auspacken"
  ActorDef := geActor_DefCreateFromFile (ActorFile);
  if ActorDef = nil then
    ExitError ('Figurdaten lassen sich nicht auslesen!');
  // Die Figur "zusammenbauen"
  Actor := geActor_Create (ActorDef);
  if Actor = nil then
    ExitError ('Figur lässt sich nicht erzeugen!');
  // Die Figur in die Welt einfügen
  InitOK := Boolean(geWorld_AddActor
    (Welt, Actor, GE_ACTOR_RENDER_NORMAL, $fffffff));
  if not InitOK then
    ExitError ('Figur lässt sich nicht in Welt einfügen!');
  // Welt "übernehmen"
  World := Welt;
  // Startdaten für Animation setzen
  Motion := nil;
  MCount := GetTickCount; // 0.0;
  MSpeed := 0.0; isMoving := false;
end;

Dann wird die Datei geladen, die sämtliche Daten für unsere Figur enthält:

ActorFile := geVFile_OpenNewSystem (nil, GE_VFILE_TYPE_DOS,
  PChar(DateiName), nil, GE_VFILE_OPEN_READONLY);

Während die Weltdaten als BSP-Datei gespeichert sind, sind die Daten einer Figur in einer Datei untergebracht, die für Genesis3D die Kennung ACT hat. Das ist ein Kürzel für „Actor“, zu Deutsch soviel wie „Schauspieler, Darsteller“. In Genesis3D sind damit nicht nur Akteure gemeint, also Figuren, die sich bewegen und auch etwas tun (können). Als Actor gilt aber ebenso z.B. ein Baum oder eine Figur, die unbeweglich einen Eingang bewacht. Viele Gegenstände in einem Spiel können also vom Typ Actor sein.
Um eine Figur zu erstellen, wird ein 3D-Grafikprogramm benötigt. Eine sehr preisgünstige Möglichkeit ist das Shareware-Programm MilkShape 3D (mit dem sich Figuren nicht nur für Genesis-Games, sondern auch für zahlreiche andere Spiele wie z.B. Quake, Half-Life, Unreal oder Max Payne erstellen lassen). Nun müssen wir zuerst die Daten auspacken, die die Figur beschreiben. Anschließend kann damit die Figur selbst zusammengebaut werden:

ActorDef := geActor_DefCreateFromFile (ActorFile);
Actor := geActor_Create (ActorDef);

Die fertige Figur wird in die Welt eingefügt und diese kann dann mit der Eigenschaft World von TFigur verknüpft werden:

geWorld_AddActor (Welt, Actor, GE_ACTOR_RENDER_NORMAL, $fffffff));
World := Welt;

Zuletzt setzen wir die Ausgangswerte für eine mögliche Animation der Figur. Dabei sorgen wir jetzt dafür, dass sie erst einmal ihre Ruhe hat und stellen alles auf „regungslos“:

Motion := nil; MCount   := GetTickCount;
MSpeed := 0.0; isMoving := false;