Delphi World - GLScene - Космосим на GLScene. Часть 2
Delphi World - это проект, являющийся сборником статей и малодокументированных возможностей  по программированию в среде Delphi. Здесь вы найдёте работы по следующим категориям: delphi, delfi, borland, bds, дельфи, делфи, дэльфи, дэлфи, programming, example, программирование, исходные коды, code, исходники, source, sources, сорцы, сорсы, soft, programs, программы, and, how, delphiworld, базы данных, графика, игры, интернет, сети, компоненты, классы, мультимедиа, ос, железо, программа, интерфейс, рабочий стол, синтаксис, технологии, файловая система...
GLScene - Космосим на GLScene. Часть 2

Автор: Daddy
WEB-сайт: http://daddy.mirgames.ru


Часть 2. Простейшее движение корабля, эффекты пламени, фон, создание планет, астероидов.

Зазвезденный фон

Есть различные методы создания звездного фона. В большинстве игр для этой цели используются или сферы(кубы) с соответствующей текстурой или спрайты. В общем-то спрайты они даже лучше, чем сферы, хоть и требуют некоторых знаний стереометрии и спрайтовая реализация куда сложнее. Лучше они тем, что меньше искажений, да и проще хранить спрайты, чем текстуру размером 6400x4800. Да и скорость вывода зачастую больше, но все же для начала проще реализовать сферу. Ну в GLScene реализовать это проще простого, хотя я уверен, что на картах с 8Мб или 16Мб видео это может сильно тормозить. Ну что ж, допустим, что у камеры DepthOfView установлен 400. Значит сферу надо создавать размером около 390.

Далее после создания сферы встает другая проблема - текстура, при наложении текстуры на сферу она сильно искажается. Для этого надо подредактировать текстуру под сферу. У кого есть Adobe Photoshop с помощью необходимых фильтров могут добиться этого, у меня же его нет, но вот прога, переведенная мною с С++ на Дельфи, она сделает все, что необходимо.

Воспользовавшись моей прогой и получил необходимую текстуру надо сделать банальное. Просто зайти в свойство Material у некоторой Sphere и добавить туда текстурку. Ну и придется каждый тик Cadencer-а приравнивать позицию сферы к позиции камеры, чтобы объект не мог долететь до границы. Правда в GLScene есть небольшая фича с рендерингом и прозрачные объекты на этом шаре рендериться не будут, но на этот случай вот этот файл. Просто замените такой же файл в GLScene этим. Что делает этот файл. Этот файл делает у всех объектов дополнительное свойство Sorted. То есть дает возможность указывать следует сортировать этот объект или нет. Для незнающих поясню: GLScene сортирует объекты по некоторому принципу, или "прозрачное последним", или "чем ближе тем раньше". Рекомендую второе. Кроме того этот файл добавляет в каденесер фичу Cadencer.deltaTime - и каждый тик каденсера туда этот deltatime и записывается.

Планетарный класс

В общем-то так называемый планетарный класс вот он:

unit planet;

interface

uses Windows, GLScene, GLVectorFileObjects, GLObjects, GLCadencer,
GLWin32Viewer, GLMisc, GLTexture, jpeg, Graphics, GLThorFX, GLFireFX, Geometry,
VectorTypes, inifiles, sysutils, option;

type
  TAsteroid = class
  private
    fmodel: TFreeForm;
    rolla, turna, pitcha, speed: Single;
    Sp: TVector4f;
  public
    procedure Life;
    constructor Create(path: string; cadencer: TGLCadencer);
  end;

  TMoon = class
  private
    fmodel: TSphere;
    rotationspeed: Single;
    length: Single;
    espeed: Single;
    angle: Single;
    kx, ky, kz: Single;
  public
    procedure Life;
    constructor Create(inif: TMemIniFile; num: Integer; planet: TSphere);
  end;

  TPlanet = class
  private
    fmodel: TSphere;
    rotationspeed: Single;
    Moons: array[1..12] of TMoon;
  public
    property Model: TSphere read fmodel write fmodel;
    constructor Create(path: string; cadencer: TGLCadencer);
    procedure Life;
  end;

implementation

constructor TPlanet.Create(path: string; cadencer: TGLCadencer);
var
  inif: TMemIniFile;
  i: Integer;
begin
  inif := TMemIniFile.Create(path);
  rotationspeed := inif.ReadFloat('General', 'rotationspeed', 0);
  fmodel := TSphere(cadencer.Scene.Objects.AddNewChild(TSphere));
  fmodel.Slices := MainOption.Slice;
  fmodel.Stacks := MainOption.Stack;
  fmodel.Material.Texture.Image.LoadFromFile('images\textures\planets\' +
    inif.ReadString('General', 'Texture', ''));
  fmodel.Material.Texture.TextureMode := tmModulate;
  fmodel.Material.Texture.Enabled := True;
  fmodel.Radius := inif.ReadFloat('General', 'radius', 10);
  fmodel.Pitch(inif.ReadFloat('General', 'pitch', 0));
  fmodel.Roll(inif.ReadFloat('General', 'roll', 0));
  fmodel.Turn(inif.ReadFloat('General', 'turn', 0));
  with fmodel.Position do
  begin
    x := inif.ReadFloat('General', 'x', 0);
    y := inif.ReadFloat('General', 'y', 0);
    z := inif.ReadFloat('General', 'z', 0);
  end;
  for i := 1 to inif.ReadInteger('General', 'mooncount', 0) do
    Moons[i] := TMoon.Create(inif, i, fmodel);
  inif.Destroy;
end;

procedure TPlanet.Life;
var
  i: Byte;
begin
  fmodel.Turn(rotationspeed);
  i := 1;
  while Assigned(Moons[i]) do
  begin
    Moons[i].Life;
    inc(i);
  end;
end;

//----------------------------------
//----------------------------------

constructor TMoon.Create(inif: TMemIniFile; num: Integer; planet: TSphere);
var
  Sect: string;
begin
  Sect := 'Moon' + IntToStr(num);
  fmodel := TSphere(planet.AddNewChild(TSphere));
  length := inif.ReadFloat(Sect, 'length', 10);
  fmodel.Slide(length);
  rotationspeed := inif.ReadFloat(Sect, 'rotationspeed', 0);
  fmodel.Slices := MainOption.Slice;
  fmodel.Stacks := MainOption.Stack;
  fmodel.Radius := inif.ReadFloat(Sect, 'radius', 3);
  fmodel.Material.Texture.Image.LoadFromFile('images\textures\planets\' +
    inif.ReadString(Sect, 'Texture', ''));
  fmodel.Material.Texture.TextureMode := tmModulate;
  fmodel.Material.Texture.Enabled := True;
  fmodel.Pitch(inif.ReadFloat(Sect, 'pitch', 0));
  fmodel.Turn(inif.ReadFloat(Sect, 'turn', 0));
  fmodel.Roll(inif.ReadFloat(Sect, 'roll', 0));
  kx := inif.ReadFloat(Sect, 'kx', 1);
  ky := inif.ReadFloat(Sect, 'ky', 1);
  kz := inif.ReadFloat(Sect, 'kz', 1);
  angle := inif.ReadFloat(Sect, 'startangle', 0);
  espeed := inif.ReadFloat(Sect, 'speed', 0);
end;

procedure TMoon.Life;
begin
  fmodel.Turn(rotationspeed);
  angle := angle + espeed;
  if angle > 360 then
    angle := angle - 360;
  with fmodel.Position do
  begin
    X := cos(DegToRad(angle)) * length * kx;
    Y := cos(DegToRad(angle)) * length * ky;
    Z := sin(DegToRad(angle)) * length * kz;
  end;
end;

constructor TAsteroid.Create(path: string; cadencer: TGLCadencer);
var
  inif: TMemIniFile;
begin
  inif := TMemIniFile.Create(path);
  fmodel := TFreeForm(cadencer.Scene.Objects.AddNewChild(TFreeForm));
  fmodel.LoadFromFile('models/asteroids/' + inif.ReadString('General', 'Model',
    ''));
  with fmodel.Material do
  begin
    Texture.Image.LoadFromFile('images/textures/asteroids/' +
      inif.ReadString('General', 'Texture', ''));
    Texture.TextureMode := tmModulate;
    Texture.Disabled := False;
  end;
  fmodel.Scale.Scale(0.01);
  fmodel.Position.X := inif.ReadFloat('General', 'X', 0);
  fmodel.Position.Y := inif.ReadFloat('General', 'Y', 0);
  fmodel.Position.Z := inif.ReadFloat('General', 'Z', 0);
  Randomize;
  fmodel.PitchAngle := inif.ReadFloat('General', 'StartPitch', random(360));
  fmodel.TurnAngle := inif.ReadFloat('General', 'StartTurn', random(360));
  fmodel.RollAngle := inif.ReadFloat('General', 'StartRoll', random(360));
  rolla := inif.ReadFloat('General', 'RollSpeed', 0);
  pitcha := inif.ReadFloat('General', 'PitchSpeed', 0);
  turna := inif.ReadFloat('General', 'TurnSpeed', 0);
  Sp := fmodel.AbsoluteDirection;
  speed := inif.ReadFloat('General', 'Speed', 0);
  ScaleVector(Sp, speed);
end;

procedure TAsteroid.Life;
begin
  fmodel.Roll(rolla);
  fmodel.Pitch(pitcha);
  fmodel.Turn(turna);
  fmodel.Translate(Sp[0], Sp[1], Sp[2]);
end;

end.

Все планеты хрянятся в отдельном ini-файле, вот его формат:

[General]
Name=%Имя планеты, в общем-то не нужно, но в будущем может пригодиться%
Texture=%Название файла с текстурой%
radius=%Радиус планеты%
rotationspeed=%Скорость вращения вокруг своей оси%
mooncount=%Количество лун%
x=%Координата X%
y=%Координата Y%
z=%Координата Z%
pitch=%Стартовый Pitch планеты, можно даже сказать её ось%
roll=%Аналогично Roll%
turn=%И Turn%
[MoonN] (N - номер луны)
Name=%Название первой луны%
Texture=%Название файла с текстурой%
radius=%Радиус%
rotationspeed=%Скорость вращения вокруг своей оси%
speed=%Скорость вращения вокруг планеты%
pitch=%Pitch оси луны%
roll=%Pitch оси луны%
turn=%Pitch оси луны%
length=%Расстояние до планеты%
startangle=%Начальная позиция по орбите планеты в градусах%
kx=%Коэффициент для растягивания орбиты%
ky=%Коэффициент для растягивания орбиты%
kz=%Коэффициент для растягивания орбиты%  

Орбита считается как косинус или синус умноженный на соответствующий коэффициент и на расстояние до планеты. Для движения планеты и лун и объекта TPlanet есть функция Life, вызов которой надо написать в каденсере. Все текстуры должны находятся в папке: images\textures\planets. Модели для астероидов в папке models\asteroids\. Для астероидов в папке images\textures\asteroids\. Формат представления ini-файлов астероидов:

[General]
Model=%Модель%
Texture=%Текстура%
#x=%Положение можно не указывать%
#y=%Положение можно не указывать%
#z=%Положение можно не указывать%
StartPitch=%Начальный Pitch%
StartRoll=%Начальный Roll%
StartTurn=%Начальный Turn%
Speed=%Скорость движения%
PitchSpeed=%Скорость вращения%
RollSpeed=%Скорость вращения%
TurnSpeed=%Скорость вращения%  

Класс корабля

unit engine;

interface

uses SysUtils, Windows, GLScene, GLVectorFileObjects, GLObjects, GLCadencer,
  GLWin32Viewer, GLMisc, Math, GLTexture, jpeg, Graphics, GLThorFX, GLFireFX,
  Geometry, VectorTypes, keyboard, inifiles, option;

type
  TCustomShip = class
  private
    fmodel: TFreeForm;
    dummy: TDummyCube;
    speed, maxspeed, accel: single;
    numengine: integer;
    ksrot: Single;
    maxsrot: Single;
    Rotated: Boolean;
    pits, turs, rols: single;
    kpitch, kturn, kroll: single;
    distance: single;
    fengine: array[1..10] of TDummyCube;
    flaser: array[1..10] of TCylinder;
    fenginesprite: array[1..10] of TSprite;
    flight: array[1..10] of TGLLightSource;
    ffire: array[1..10] of TGLFireFXManager;
    fcad: TGLCadencer;
    IntervalK: Word;
    Acceled: Boolean;
  public
    property target: TDummyCube read dummy write dummy;
    procedure Rotate(pitch, turn, roll: single);
    procedure Move;
    procedure Life;
    procedure Stop;
    procedure Fire(TargetObj: TGLBaseSceneObject);
    constructor Create(path: string; cadencer: TGLCadencer);
  end;

  TShip = class(TCustomShip)
  private
    fcamera: TGLCamera;
    Pricel: TSprite;
  public
    property camera: TGLCamera read fcamera write fcamera;
    procedure Rotate(pitch, turn, roll: single);
    procedure Life;
    constructor Create(path: string; cadencer: TGLCadencer);
  end;

  TAIShip = class(TCustomShip)
  private
    kAttack: single;
    kDrive: single;
    kMoral: single;
    kAvoid: single;
    kKam: single;
    IQ: single;
    kShab: single;
  public
    procedure Brain(Target: TCustomShip);
  end;

implementation

//TCustomShip-------------------------------------------------

constructor TCustomShip.Create(path: string; cadencer: TGLCadencer);
var
  f: text;
  s, s2: string;
  v, m: Single;
  i, n: Integer;
  FX: TGLBFireFX;
  inif, inim: TMemIniFile;
begin
  maxspeed := 0.05;
  speed := 0;
  pits := 0;
  turs := 0;
  rols := 0;
  ksrot := 0.07;
  maxsrot := 3;
  inim := TMemIniFile.Create(path);
  dummy := TDummyCube(cadencer.Scene.Objects.AddNewChild(TDummyCube));
  maxspeed := inim.ReadFloat('General', 'maxspeed', 0.05);
  kroll := inim.ReadFloat('General', 'rollspeed', 3);
  kturn := inim.ReadFloat('General', 'turnspeed', 3);
  kpitch := inim.ReadFloat('General', 'pitchspeed', 3);
  accel := inim.ReadFloat('General', 'acceleration', 0.01);
  fcad := cadencer;
  fmodel := TFreeForm(dummy.AddNewChild(TFreeForm));
  fmodel.LoadFromFile('models\' + inim.ReadString('General', 'type', '') + '\' +
    inim.ReadString('General', 'name', '') + '\' + inim.ReadString('General',
    'model', ''));
  fmodel.Material.Texture.Image.LoadFromFile('images\textures\ships\' +
    inim.ReadString('General', 'texture', ''));
  fmodel.Material.Texture.Disabled := False;
  fmodel.Material.Texture.TextureMode := tmModulate;
  s2 := inim.ReadString('General', 'fire', '');
  fmodel.Scale.X := inim.ReadFloat('General', 'scale', 0.01);
  fmodel.Scale.Y := inim.ReadFloat('General', 'scale', 0.01);
  fmodel.Scale.Z := inim.ReadFloat('General', 'scale', 0.01);
  fmodel.Position.Y := -0.4;
  fmodel.Pitch(90);
  AssignFile(f, 'data\engine\' + inim.ReadString('General', 'engine', '') +
    '.eng');
  Reset(f);
  readln(f, n);
  readln(f, m);
  readln(f, s);
  numengine := n;
  for i := 1 to n do
  begin
    //-----------Огонь
    if MainOption.Fire then
    begin
      inif := TMemIniFile.Create('data/pfx/fire/' + s2 + '.ini');
      IntervalK := inif.ReadInteger('General', 'IntervalK', 2000);
      fengine[i] := TDummyCube(dummy.AddNewChild(TDummyCube));
      ffire[i] := TGLFireFXManager.Create(fcad.Scene);
      ffire[i].Reference := fengine[i];
      with ffire[i] do
      begin
        cadencer := TGLCadencer(fcad);
        firedensity := inif.ReadFloat('General', 'density', 0.0);
        fireradius := inif.ReadFloat('General', 'radius', 0.0);
        firedir.X := 0;
        firedir.Y := 0;
        firedir.Z := 0;
        MaxParticles := inif.ReadInteger('General', 'maxparticles', 0);
        ParticleInterval := inif.ReadFloat('General', 'interval', 0);
        ParticleLife := inif.ReadInteger('General', 'life', 1);
        ParticleSize := inif.ReadFloat('General', 'size', 1);
        InnerColor.Red := inif.ReadFloat('InnerColor', 'Red', 0);
        InnerColor.Blue := inif.ReadFloat('InnerColor', 'Blue', 0);
        InnerColor.Green := inif.ReadFloat('InnerColor', 'Green', 0);
        OuterColor.Red := inif.ReadFloat('InnerColor', 'Red', 0);
        OuterColor.Blue := inif.ReadFloat('InnerColor', 'Blue', 0);
        OuterColor.Green := inif.ReadFloat('InnerColor', 'Green', 0);
        InitialDir.X := 0;
        InitialDir.Y := 0;
        InitialDir.Z := 0.2;
      end;
      inif.Free;
      FX := TGLBFireFX.Create(fengine[i].Effects);
      FX.Manager := ffire[i];
    end;
    //-----------Свет от огня
    if MainOption.FireLight then
    begin
      inif := TMemIniFile.Create('data/pfx/fire/' + s2 + '.ini');
      flight[i] := TGLLightSource(dummy.AddNewChild(TGLLightSource));
      flight[i].Diffuse.Blue := inif.ReadFloat('InnerColor', 'Blue', 0);
      flight[i].Diffuse.Green := inif.ReadFloat('InnerColor', 'Green', 0);
      flight[i].Diffuse.Red := inif.ReadFloat('InnerColor', 'Red', 0);
      flight[i].LightStyle := lsOmni;
      inif.Free;
    end;
    //-----------Спрайт
    if MainOption.Sprite then
    begin
      fenginesprite[i] := TSprite(dummy.AddNewChild(TSprite));
      Randomize;
      fenginesprite[i].AlphaChannel := Random(10000) / Random(4000);
      fenginesprite[i].Scale.X := m;
      fenginesprite[i].Scale.Y := m;
      fenginesprite[i].Scale.Z := m;
      fenginesprite[i].Material.Texture.Image.LoadFromFile(s);
      fenginesprite[i].Material.Texture.Disabled := false;
      fenginesprite[i].Material.BlendingMode := bmAdditive;
      fenginesprite[i].Material.Texture.TextureMode := tmReplace;
      fenginesprite[i].Material.Texture.TextureWrap := twNone;
    end;
    read(f, v);
    if MainOption.Fire then
      fengine[i].Translate(v, 0, 0);
    if MainOption.Sprite then
      fenginesprite[i].Translate(v, 0, 0);
    if MainOption.FireLight then
      flight[i].Translate(v, 0, 0);
    read(f, v);
    if MainOption.Fire then
      fengine[i].Translate(0, v, 0);
    if MainOption.Sprite then
      fenginesprite[i].Translate(0, v, 0);
    if MainOption.FireLight then
      flight[i].Translate(v, 0, 0);
    read(f, v);
    if MainOption.Fire then
      fengine[i].Translate(0, 0, v);
    if MainOption.Sprite then
      fenginesprite[i].Translate(0, 0, v);
    if MainOption.FireLight then
      flight[i].Translate(v, 0, 0);
  end;
  CloseFile(f);
end;

procedure TCustomShip.Rotate(pitch, turn, roll: single);
begin
  pits := pits + pitch;
  turs := turs + turn;
  rols := rols + roll;
  Rotated := True;
end;

procedure TCustomShip.Move;
begin
  if speed < maxspeed then
    speed := speed + accel;
  Acceled := True;
end;

procedure TCustomShip.Stop;
begin
  if speed > 0 then
  begin
    speed := speed - accel * 2;
    if speed < 0 then
      speed := 0;
  end;
  Acceled := True;
end;

procedure TCustomShip.Life;
var
  i: Byte;
  Vec: TVector4f;
  RightVector: TAffineVector;
  Up, Dir: TAffineVector;
begin
  if (pits > maxsrot) or (pits < -maxsrot) then
    pits := sign(pits) * maxsrot;
  if (turs > maxsrot) or (turs < -maxsrot) then
    turs := sign(turs) * maxsrot;
  if (rols > maxsrot) or (rols < -maxsrot) then
    rols := sign(rols) * maxsrot;
  dummy.Pitch(pits * fcad.Delta * 10);
  dummy.Turn(turs * fcad.Delta * 10);
  dummy.Roll(rols * fcad.Delta * 10);
  if pits > 0 then
  begin
    pits := pits - ksrot;
    if pits < 0 then
      pits := 0;
  end;
  if rols > 0 then
  begin
    rols := rols - ksrot;
    if rols < 0 then
      rols := 0;
  end;
  if turs > 0 then
  begin
    turs := turs - ksrot;
    if turs < 0 then
      turs := 0;
  end;
  if pits < 0 then
  begin
    pits := pits + ksrot;
    if pits > 0 then
      pits := 0;
  end;
  if rols < 0 then
  begin
    rols := rols + ksrot;
    if rols > 0 then
      rols := 0;
  end;
  if turs < 0 then
  begin
    turs := turs + ksrot;
    if turs > 0 then
      turs := 0;
  end;
  if MainOption.Fire then
  begin
    if Rotated then
    begin
      SetVector(Up, dummy.Up.AsVector);
      SetVector(Dir, dummy.Direction.AsVector);
      VectorCrossProduct(Dir, Up, RightVector);
      MakeVector(vec, rightvector);
      ScaleVector(vec, 0.2);
      NegateVector(vec);
    end;
    for i := 1 to numengine do
    begin
      ffire[i].Disabled := not Acceled;
      ffire[i].ParticleInterval := 1 / ((speed + maxspeed / 500) * IntervalK);
      if Rotated then
        ffire[i].InitialDir.AsVector := vec;
    end;
  end;
  Rotated := False;
  if speed > 0 then
  begin
    speed := speed - accel / 2;
    Acceled := True;
    if speed < 0 then
      speed := 0;
  end;
  dummy.Slide(speed * 100 * fcad.Delta);
  // fcad.Scene.Objects.FindChild('Star',False).Position:=dummy.Position;
  if Assigned(flaser[1]) then
    flaser[1].Slide(0.1);
end;

procedure TCustomShip.Fire(TargetObj: TGLBaseSceneObject);
begin
  if Assigned(flaser[1]) then
    flaser[1].Destroy;
  flaser[1] := TCylinder(fcad.Scene.Objects.AddNewChild(TCylinder));
  with flaser[1] do
  begin
    BottomRadius := 0.05;
    TopRadius := 0.05;
    RollAngle := dummy.RollAngle;
    TurnAngle := dummy.TurnAngle;
    PitchAngle := dummy.PitchAngle;
    Position := dummy.Position;
    //Direction:=TargetObj.Position;
    Height := 2;
  end;
end;

//TShip-------------------------------------------------

constructor TShip.Create(path: string; cadencer: TGLCadencer);
begin
  inherited;
  fcamera := TGLCamera(dummy.AddNewChild(TGLCamera));
  Pricel := TSprite(dummy.AddNewChild(TSprite));
  with Pricel.Material do
  begin
    Texture.Image.LoadFromFile('images/sprites/ships/pricel.bmp');
    BlendingMode := bmAdditive;
    Texture.Enabled := True;
  end;
  Pricel.Scale.Scale(0.3);
  with fcamera do
  begin
    Position.X := 2;
    Position.Y := -0.3;
    DepthOfView := MainOption.DepthView;
    CameraStyle := csPerspective;
    FocalLength := 100;
    SceneScale := 0.5;
    Direction.Z := -1;
    Turn(-90);
    Pitch(10);
    //TGLLightSource(AddnewChild(TGLLightSource)).ConstAttenuation:=2;
  end;
end;

procedure TShip.Rotate(pitch, turn, roll: single);
begin
  inherited;
end;

procedure TShip.Life;
var
  deltatime: double;
begin
  inherited;
  //При Rotate вместо 30*deltatime поставь строчки внизу
  if isKeyDown(vk_up) then
    Rotate(0, 0, kroll);
  if isKeyDown(vk_down) then
    Rotate(0, 0, -kroll);
  if isKeyDown(vk_left) then
    Rotate(kpitch, 0, 0);
  if isKeyDown(vk_right) then
    Rotate(-kpitch, 0, 0);
  // if isKeyDown(33) then fcamera.MoveObjectAround(fmodel,kpitch/3,0);
  // if isKeyDown(34) then fcamera.MoveObjectAround(fmodel,-kpitch/3,0);
  if isKeyDown(vk_insert) then
    Move
  else
    Acceled := False;
  if isKeyDown(vk_delete) then
    Stop;
  if distance < speed * 40 then
  begin
    distance := distance + speed / 2;
    fcamera.Move(-speed / 2);
    fcamera.Lift(speed / 10);
  end
  else
  begin
    fcamera.Move(speed / 2);
    fcamera.Lift(-speed / 10);
    distance := distance - speed / 2;
  end;
end;

//TAIShip-------------------------------------------------

procedure TAIShip.Brain(Target: TCustomShip);
var
  Vect: TVector4f;
begin
  if dummy.DistanceTo(Target.dummy) > 10 then
  begin
    Stop;
    exit;
  end;
  dummy.Direction := Target.dummy.Position;
  dummy.Direction.Rotate(YVector, -90);
  Move;

  {
  Life;
  if (dummy.Position.X > Target.dummy.Position.X) then
    dummy.Position.X := dummy.Position.X - 0.01
  else
    dummy.Position.X := dummy.Position.X + 0.01;
  if (dummy.Position.Y > Target.dummy.Position.Y) then
    dummy.Position.Y := dummy.Position.Y - 0.01
  else
    dummy.Position.Y := dummy.Position.Y + 0.01;
  if (dummy.Position.Z > Target.dummy.Position.Z) then
    dummy.Position.Z := dummy.Position.Z - 0.01
  else
    dummy.Position.z := dummy.Position.Z + 0.01;
  MakeVector(Vect, Target.dummy.Position.X - dummy.Position.X,
    Target.dummy.Position.Y - dummy.Position.Y, Target.dummy.Position.Z -
    dummy.Position.Z);
  }
end;

end.

Небольшое описание:

  • TCustomShip - минимальный класс для любого корабля
  • TShip - класс для "человеческого" корабля
  • TAIShip - небольшой класс минимального AI, тупого до невозможности.

Как им пользоваться:

В FormCreate надо будет нечто вроде этого:

AIShip:=TAIShip.Create('data\ships\storm.ini',GLCadencer1);
Ship:=TShip.Create('data\ships\shark.ini',GLCadencer1);
GLSceneViewer1.Camera:=Ship.camera;
AIShip.Life;  

Естественно в var должны быть указаны соответствующие переменные. А в GLCadencer1Progress требуется указать:

if not Assigned(Ship) then exit;
AIShip.Brain(Ship);
Ship.Life;
Plan.Life;
Asteroid.Life;
AIShip.Life;  

О том, как там все работает напишу в следующей статье, так сказать во второй части к этой части, а пока можете попытаться разобраться самим, кроме того надо еще доделать процедуры Brain и Fire, а пока немного о формате для корабля:

[General]
type=%Тип корабля%
name=%Название корабля%
model=%Файо модели%
maxspeed=%Максимальная скорость%
acceleration=%Ускорение%
turnspeed=%Скорость Turn%
pitchspeed=%Скорость Pitch%
rollspeed=%Скорость Roll%
texture=%Файл текстуры%
fire=%Файл описывающий пламя%
engine=%Файл описывающий расположение двигателей%
lasercount=%Количество лазеров%
scale=%Scale корабля%
[LaserType]
glowsize=%Размер свечения для лазмерного луча%
maxpoints=%MaxPoints для ThorFX лазерного луча%
vibrate=%Vibrate для ThorFX лазерного луча%
wildness=%Wildness для ThorFX лазерного луча%
speed=%Скорость движения луча%
[LaserN] %N - номер лазера%
x=%Позиция создания относительно корабля%
y=%Позиция создания относительно корабля%
z=%Позиция создания относительно корабля%  

Формат файлов для огня:

[General]
Density=2,5
Radius=0,00
MaxParticles=1400
Interval=0,00001
Life=10
Size=0,08
InteravlK=1000
[InnerColor]
Red=1
Green=1
Blue=0,5
[OuterColor]
Red=1
Green=1
Blue=0,5  

Вышеприведенные свойства соответствуют аналогичным у любого FireFX. А приведенные, как пример - наиболее оптимальные. Формат для двигателей:

4
0.46
images\sprites\lensef\yrengine.bmp
0.33 -0.17 0.34
0.33 -0.65 0.34
0.33 -0.17 -0.34
0.33 -0.65 -0.34  

Первая строка - количество engine-ов (NE). Далее размер для спрайта свечения у engine-а. Далее текстурка для спрайта. В следующих NE строках позиции каждого спрайта (они используются и для пламени).

Еще небольшое примечание:

Файлы engine-ов хранятся в папках data\engine\ и имеют расширение .eng
Файлы FireFX хранятся в папках data\pfx\fire\ и имеют расширение ini.
Модели хранятся в папках models\%Тип корабля%\%Название корабля%\
Текстуры хранятся в папке images\textures\ships\

В общем вот работающие файлы этой статьи, разве что без текстуры для звезд, да и без нее весит 2Мб.

Проект Delphi World © Выпуск 2002 - 2017
Автор проекта: Эксклюзивные курсы программирования