Disparando tiros em 3D com Irrlicht em Turbo Delphi .NET

Esse programa é constituído de 3 módulos→ Tiro.dpr, Principal.pas e Maquina.pas.
Usa um billboard pra servir de mira.
A navegação é feita com SETAESQUERDA, SETADIREITA, SETAPRACIMA, SETAPRABAIXO e ESPAÇO.
Essa última tecla dispara o tiro.
Quando o tiro atinge os limites do mundo (parede, chão, teto), o triângulo da colisão é desenhado, bem como um billboard pra marcar o local exato da colisão.
Billboards são imagens 2D que sempre estão voltadas pra câmera.

ConfigurarDemais → cria um detector de colisão pra navegar no mundo.

CarregarBB → carrega um billboard pro nódulo corrente (mundo).





  Tiro.dpr  
program Tiro;

{%DelphiDotNetAssemblyCompiler 'C:\Arquivos de programas\Irrlicht-1.3.1\bin\Win32-VisualStudio\Irrlicht.NET.dll'}

uses Principal in 'Principal.pas', Maquina in 'Maquina.pas';

var
  IJogo: TJogo;

begin
  IJogo:= TJogo.Create;
  try
    IJogo.Rodar;
  finally
    IJogo.Free;
  end;
end.

  Principal.pas  
unit Principal;

interface

uses Borland.Vcl.SysUtils, Irrlicht, Irrlicht.Core, Irrlicht.Scene, Irrlicht.Video;

type
  TJogo_Base= class (TObject, IEventReceiver)
    protected
      function CarregarMundo: Boolean; overload; virtual;
      function Configurar   : Boolean; overload; virtual;
    protected
      procedure ConfigurarDemais; overload; virtual;
      procedure DispararQuadro;   overload; virtual;
    protected
      function CarregarBB (const AFigura: String): IBillboardSceneNode; overload; virtual;
    public
      function OnEvent (AE: Event): Boolean; overload; virtual;
    public
      procedure Rodar; overload; virtual;
    protected
      Mundo             : IAnimatedMesh;
      Particula         : IBillboardSceneNode;
      Camera            : ICameraSceneNode;
      Dispositivo_Device: IrrlichtDevice;
      Dispositivo_SMgr  : ISceneManager;
      NoduloMundo       : ISceneNode;
      Seletor           : ITriangleSelector;
      Dispositivo_Driver: IVideoDriver;
  end;

implementation
function TJogo_Base.CarregarMundo: Boolean;
const
  cPacote= 'map-20kdm2.pk3';
  cMapa  = '20kdm2.bsp';

begin
  Result:= False;
  Dispositivo_Device.FileSystem.AddZipFileArchive (cPacote);
  Mundo      := Dispositivo_SMgr.GetMesh (cMapa);
  NoduloMundo:= nil;
  if (Mundo <> nil) then begin
    NoduloMundo:= Dispositivo_SMgr.AddOctTreeSceneNode (Mundo.GetMesh (0), nil, 0);
    if (NoduloMundo <> nil) then
      with NoduloMundo do begin
        AutomaticCulling:= False;
        SetMaterialType (MaterialType.LightMap_M4);
        Position:= Vector3D.Create (-1350, -130, -1400);
        Result  := True;
      end;
  end;
end;
function TJogo_Base.Configurar: Boolean;

begin
  SetCurrentDir ('C:\Arquivos de programas\irrlicht-1.3.1\media');

  Dispositivo_Device:= IrrlichtDevice.Create (DriverType.Direct3D9);
  Result            := Assigned (Dispositivo_Device);
  if Result then begin
    Dispositivo_Device.ResizeAble           := True;
    Dispositivo_Device.WindowCaption        := 'Tiro [ '+ Dispositivo_Device.VideoDriver.Name + ' ]';
    Dispositivo_Driver                      := Dispositivo_Device.VideoDriver;
    Dispositivo_SMgr                        := Dispositivo_Device.SceneManager;
    Dispositivo_Device.CursorControl.Visible:= False;
    Dispositivo_Device.EventReceiver        := Self;
    Camera                                  := Dispositivo_SMgr.AddCameraSceneNodeFPS;
    Camera.Position                         := Vector3D.Create (0, 50, -100);
  end;
end;
procedure TJogo_Base.ConfigurarDemais;
var
  ISNDA: ISceneNodeAnimator;

begin
  Seletor:= Dispositivo_SMgr.CreateOctTreeTriangleSelector (Mundo.GetMesh (0), NoduloMundo, 128);

  Dispositivo_SMgr.AddLightSceneNode (nil, Vector3D.Create (-60, 100, 400), ColorF.Create (1.0, 1.0, 1.0, 1.0), 600, 100);

  ISNDA:= Dispositivo_SMgr.CreateCollisionResponseAnimator (Seletor, Camera, Vector3D.Create (30, 50, 30), Vector3D.Create (0, -3, 0), Vector3D.Create (0, 50, 0), 0);
  try
    Camera.AddAnimator (ISNDA);
  finally
    ISNDA.Free;
  end;
end;
procedure TJogo_Base.DispararQuadro;

begin
end;
function TJogo_Base.CarregarBB (const AFigura: String): IBillboardSceneNode;

begin
  Result:= Dispositivo_SMgr.AddBillboardSceneNode (nil, Dimension2DF.Create (0, 0), Vector3D.Create (0, 0, 0), -1);
  with Result do begin
    Position:= Vector3D.Create (0, 1000, 0);
    SetMaterialType (MaterialType.Transparent_Add_Color);
    SetMaterialTexture (0, Dispositivo_Driver.GetTexture (AFigura));
    SetMaterialFlag (MaterialFlag.Lighting, False);
    SetMaterialFlag (MaterialFlag.ZBuffer, False);
    Size:= Dimension2DF.Create (25, 25);
  end;
end;
function TJogo_Base.OnEvent (AE: Event): Boolean;

begin
  Result:= False;
end;
procedure TJogo_Base.Rodar;

begin
  if not (Configurar and CarregarMundo) then
    Exit;
  Particula:= CarregarBB ('particle.bmp');
  ConfigurarDemais;
  while Dispositivo_Device.Run and Dispositivo_Device.WindowActive do begin
    Dispositivo_Driver.BeginScene (True, True, Color.Create (0, 100, 100, 100));
    Dispositivo_SMgr.DrawAll;
    DispararQuadro;
    Dispositivo_Driver.EndScene;
  end;
  Dispositivo_Device.Free;
end;
end.

  Maquina.pas  
unit Maquina;

interface

uses Principal, Irrlicht, Irrlicht.Core, Irrlicht.Scene, Irrlicht.Video;

type
  TJTiro= record
    Explodiu                 : Boolean;
    Tempo                    : Integer;
    Raio                     : Line3D;
    ColisaoTriangulo         : Triangle3D;
    ColisaoPonto, Fim, Inicio: Vector3D;
  end;

  TJTiros= class
    public
      procedure Adc (AValor: TJTiro);   overload; virtual;
      procedure Rmv (AIndice: Integer); overload; virtual;
    public
      Itens: array of TJTiro;
  end;

  TJogo= class (TJogo_Base)
    public
      constructor Create;
    protected
      procedure DispararQuadro;    override;
      procedure DesenharParticula; overload; virtual;
      procedure Atirar;            overload; virtual;
      procedure Explodir;          overload; virtual;
    protected
      procedure MostrarExplosao (ATriangulo: Triangle3D); overload; virtual;
    public
      function OnEvent (AE: Event): Boolean; override;
    public
      Tiros: TJTiros;
  end;

implementation
procedure TJTiros.Adc (AValor: TJTiro);
var
  M: Integer;

begin
  M:= Length (Itens);
  SetLength (Itens, M+1);
  Itens[M]:= AValor;
end;
procedure TJTiros.Rmv (AIndice: Integer);
var
  I, M: Integer;

begin
  M:= High (Itens);
  if AIndice > M then
    Exit
  else if AIndice < M then
    for I:= AIndice to M-1 do
      Itens[I]:= Itens[I+1];
  SetLength (Itens, M);
end;




constructor TJogo.Create;

begin
  inherited Create;
  Tiros:= TJTiros.Create;
end;
procedure TJogo.DispararQuadro;

begin
  inherited DispararQuadro;
  DesenharParticula;
  Explodir;
end;
procedure TJogo.DesenharParticula;
var
  Raio            : Line3D;
  ColisaoTriangulo: Triangle3D;
  ColisaoPonto    : Vector3D;

begin
  with Raio do begin
    Start:= Camera.Position;
    &End := Start + (Camera.Target - Start).Normalize * 1000;
  end;
  if Dispositivo_SMgr.SceneCollisionManager.GetCollisionPoint (Raio, Seletor, ColisaoPonto, ColisaoTriangulo) then
    Particula.Position:= ColisaoPonto;
end;
// Dispara a animação do tiro
procedure TJogo.Atirar;
var
  Fim, Inicio: Vector3D;

var
  Nodulo   : ISceneNode;
  Animador : ISceneNodeAnimator;
  Distancia: Real;
  Tiro     : TJTiro;

begin
  Inicio:= Camera.Position;
  Fim   := Camera.Target - Inicio;
  Fim.Normalize;
  Inicio:= Inicio + Fim*15.0;
  Fim   := Inicio + (Fim * Camera.FarValue);

  with Tiro.Raio do begin
    Start:= Inicio;
    &End := Fim;
  end;

  if not Dispositivo_SMgr.SceneCollisionManager.GetCollisionPoint (Tiro.Raio, Seletor, Tiro.ColisaoPonto, Tiro.ColisaoTriangulo) then
    Tiro.ColisaoPonto:= Fim;

  Distancia := (Tiro.ColisaoPonto - Inicio).GetLength;
  Tiro.Tempo:= Round (Distancia/0.25);
  Nodulo    := Dispositivo_SMgr.AddBillboardSceneNode (nil, Dimension2DF.Create (5, 5), Inicio, -1);
  try
    with Nodulo do begin
      SetMaterialFlag (MaterialFlag.Lighting, False);
      SetMaterialTexture (0, Dispositivo_Driver.GetTexture ('fire.bmp'));
      SetMaterialType (MaterialType.Transparent_Add_Color);
    end;

    // Cria uma animação representando o tiro. A trajetória vai de 'Inicio' até 'ColisaoPonto' e para após 'Tiro.Duracao' (em milisegundos)
    Animador:= Dispositivo_SMgr.CreateFlyStraightAnimator (Inicio, Tiro.ColisaoPonto, Tiro.Tempo, False);
    try
      Nodulo.AddAnimator (Animador);
    finally
      Animador.Free;
    end;

    Animador:= Dispositivo_SMgr.CreateDeleteAnimator (Tiro.Tempo);
    try
      Nodulo.AddAnimator (Animador);
    finally
      Animador.Free;
    end;
  finally
    Nodulo.Free;
  end;

  Inc (Tiro.Tempo, Dispositivo_Device.Timer.Time);

  Tiro.Explodiu:= False;

  Tiros.Adc (Tiro);
end;
// Mostra um triângulo e um billboard quando o 'FlyStraightAnimator' do tiro expira
procedure TJogo.Explodir;
var
  Expirou: Boolean;
  M      : Integer;

begin
  for M:= 0 to High (Tiros.Itens) do
    with Tiros.Itens[M] do begin
      Expirou:= Dispositivo_Device.Timer.Time > Tempo;
      if Expirou then begin
        MostrarExplosao (ColisaoTriangulo);

        // Adiciona o "billboard" ao nódulo apenas uma vez 
        if not Explodiu then begin
          Explodiu:= True;
          with CarregarBB ('particlewhite.bmp') do begin
            Position:= ColisaoPonto;
            SetMaterialFlag (MaterialFlag.ZBuffer, True);
          end;
        end;
      end;
    end;
end;
procedure TJogo.MostrarExplosao (ATriangulo: Triangle3D);
var
  Material: Irrlicht.Video.Material;

begin
  with Material do begin
    Texture1:= nil;
    Lighting:= False;
    ZBuffer := True;
  end;
  with Dispositivo_Driver do begin
    SetTransform (TransformationState.World, Matrix4.Create);
    SetMaterial (Material);
    Draw3DTriangle (ATriangulo, Color.Create (0, 255, 0, 0));
  end;
end;
function TJogo.OnEvent (AE: Event): Boolean;

begin
  with AE do
    if (Key = KeyCode.Key_Space) and not KeyPressedDown then
      Atirar;
  Result:= False;
end;
end.



Código fonte (3,50 Ko)





http://transeberiano.brinkster.net