⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 hgeparticle.pas

📁 完整的Delphi游戏开发控件
💻 PAS
📖 第 1 页 / 共 2 页
字号:
unit HGEParticle;
(*
** Haaf's Game Engine 1.7
** Copyright (C) 2003-2007, Relish Games
** hge.relishgames.com
**
** Delphi conversion by Erik van Bilsen
**
** NOTE: The Delphi version uses public IHGEParticleSystem and
** IHGEParticleManager interfaces instead of a classes (more conform the main
** IHGE interface).
*)

interface

(****************************************************************************
 * HGEParticle.h
 ****************************************************************************)

uses
  HGE, HGERect, HGEVector, HGEColor, HGESprite;
  
const
  MAX_PARTICLES  = 500;
  MAX_PSYSTEMS  = 100;

type
  THGEParticle = record
    Location: THGEVector;
    Velocity: THGEVector;

    Gravity: Single;
    RadialAccel: Single;
    TangentialAccel: Single;

    Spin: Single;
    SpinDelta: Single;

    Size: Single;
    SizeDelta: Single;

    Color: THGEColor;
    ColorDelta: THGEColor;

    Age: Single;
    TerminalAge: Single;
  end;
  PHGEParticle = ^THGEParticle;

type
  { Maps directly onto PSI files, 128 bytes }
  THGEParticleSystemInfo = record
    Sprite: IHGESprite;
    Emission: Integer;
    Lifetime: Single;

    ParticleLifeMin: Single;
    ParticleLifeMax: Single;

    Direction: Single;
    Spread: Single;
    Relative: Boolean;

    SpeedMin: Single;
    SpeedMax: Single;

    GravityMin: Single;
    GravityMax: Single;

    RadialAccelMin: Single;
    RadialAccelMax: Single;

    TangentialAccelMin: Single;
    TangentialAccelMax: Single;

    SizeStart: Single;
    SizeEnd: Single;
    SizeVar: Single;

    SpinStart: Single;
    SpinEnd: Single;
    SpinVar: Single;

    ColorStart: THGEColor;
    ColorEnd: THGEColor;
    ColorVar: Single;
    AlphaVar: Single;
  end;
  PHGEParticleSystemInfo = ^THGEParticleSystemInfo;

type
  IHGEParticleSystem = interface
  ['{69A7DF86-8F28-419C-B265-742E7CE4F78B}']
    function GetInfo: PHGEParticleSystemInfo;

    { NOTE: This replaces the C++ '=' operator }
    procedure Assign(const PS: IHGEParticleSystem);

    procedure Render;
    procedure FireAt(const X, Y: Single);
    procedure Fire;
    procedure Stop(const KillParticles: Boolean = False);
    procedure Update(const DeltaTime: Single);
    procedure MoveTo(const X, Y: Single; const MoveParticles: Boolean = False);
    procedure Transpose(const X, Y: Single);
    procedure TrackBoundingBox(const Track: Boolean);

    function GetParticlesAlive: Integer;
    function GetAge: Single;
    procedure GetPosition(out X, Y: Single);
    procedure GetTransposition(out X, Y: Single);
    function GetBoundingBox(out Rect: THGERect): PHGERect;

    function Implementor: TObject;

    property Info: PHGEParticleSystemInfo read GetInfo;
  end;

type
  IHGEParticleManager = interface
  ['{C1A425FF-BB58-4539-870B-70C917149E4D}']
    procedure Update(const DT: Single);
    procedure Render;

    function SpawnPS(const PSI: THGEParticleSystemInfo;
      const X, Y: Single): IHGEParticleSystem;
    function IsPSAlive(const PS: IHGEParticleSystem): Boolean;
    procedure Transpose(const X, Y: Single);
    procedure GetTransposition(out DX, DY: Single);
    procedure KillPS(const PS: IHGEParticleSystem);
    procedure KillAll;
  end;

type
  THGEParticleSystem = class(TInterfacedObject,IHGEParticleSystem)
  protected
    { IHGEParticleSystem }
    function GetInfo: PHGEParticleSystemInfo;
    procedure Assign(const PS: IHGEParticleSystem);

    procedure Render;
    procedure FireAt(const X, Y: Single);
    procedure Fire;
    procedure Stop(const KillParticles: Boolean = False);
    procedure Update(const DeltaTime: Single);
    procedure MoveTo(const X, Y: Single; const MoveParticles: Boolean = False);
    procedure Transpose(const X, Y: Single);
    procedure TrackBoundingBox(const Track: Boolean);

    function GetParticlesAlive: Integer;
    function GetAge: Single;
    procedure GetPosition(out X, Y: Single);
    procedure GetTransposition(out X, Y: Single);
    function GetBoundingBox(out Rect: THGERect): PHGERect;

    function Implementor: TObject;
  private
    class var
      FHGE: IHGE;
  private
    FInfo: THGEParticleSystemInfo;
    FAge: Single;
    FEmissionResidue: Single;
    FPrevLocation: THGEVector;
    FLocation: THGEVector;
    FTX, FTY: Single;
    FParticlesAlive: Integer;
    FBoundingBox: THGERect;
    FUpdateBoundingBox: Boolean;
    FParticles: array [0..MAX_PARTICLES - 1] of THGEParticle;
  public
    constructor Create(const Filename: String; const Sprite: IHGESprite); overload;
    constructor Create(const PSI: THGEParticleSystemInfo); overload;
    constructor Create(const PS: IHGEParticleSystem); overload;
    procedure DoUpdate(const DeltaTime: Single);
  end;

type
  THGEParticleManager = class(TInterfacedObject,IHGEParticleManager)
  protected
    { IHGEParticleManager }
    procedure Update(const DT: Single);
    procedure Render;

    function SpawnPS(const PSI: THGEParticleSystemInfo;
      const X, Y: Single): IHGEParticleSystem;
    function IsPSAlive(const PS: IHGEParticleSystem): Boolean;
    procedure Transpose(const X, Y: Single);
    procedure GetTransposition(out DX, DY: Single);
    procedure KillPS(const PS: IHGEParticleSystem);
    procedure KillAll;
  private
    FNPS: Integer;
    FTX: Single;
    FTY: Single;
    FPSList: array [0..MAX_PSYSTEMS - 1] of IHGEParticleSystem;
  public
    destructor Destroy; override;
  end;

implementation

uses
  HGEUtils;

(****************************************************************************
 * HGEParticle.h, HGEParticle.cpp, HGEParticleManager.cpp
 ****************************************************************************)

{ THGEParticleSystem }

procedure THGEParticleSystem.Assign(const PS: IHGEParticleSystem);
begin
  CopyInstanceData(PS.Implementor,Self);
end;

constructor THGEParticleSystem.Create(const Filename: String;
  const Sprite: IHGESprite);
var
  PSI: IResource;
  P: PByte;
begin
  inherited Create;
  FHGE := HGECreate(HGE_VERSION);
  PSI := FHGE.Resource_Load(Filename);
  if (PSI = nil) then
    Exit;
  P := PSI.Handle;
  Inc(P,4);
  Move(P^,FInfo.Emission,SizeOf(THGEParticleSystemInfo) - 4);
  PSI := nil;
  FInfo.Sprite := Sprite;
  FAge := -2;
  FBoundingBox.Clear;
end;

constructor THGEParticleSystem.Create(const PS: IHGEParticleSystem);
begin
  inherited Create;
  CopyInstanceData(PS.Implementor,Self);
  FHGE := HGECreate(HGE_VERSION);
end;

constructor THGEParticleSystem.Create(const PSI: THGEParticleSystemInfo);
begin
  inherited Create;
  FHGE := HGECreate(HGE_VERSION);
  Move(PSI.Emission,FInfo.Emission,SizeOf(THGEParticleSystemInfo) - 4);
  FInfo.Sprite := PSI.Sprite;
  FAge := -2;
  FBoundingBox.Clear;
end;

procedure THGEParticleSystem.DoUpdate(const DeltaTime: Single);
var
  I, ParticlesCreated: Integer;
  Ang, ParticlesNeeded: Single;
  Par: PHGEParticle;
  Accel, Accel2, Temp: THGEVector;
begin
  if (FAge >= 0) then begin
    FAge := FAge + DeltaTime;
    if (FAge >= FInfo.Lifetime) then
      FAge := -2;
  end;

  // update all alive particles

  if (FUpdateBoundingBox) then
    FBoundingBox.Clear;
  Par := @FParticles[0];

  I := 0;
  while (I < FParticlesAlive) do begin
    Par.Age := Par.Age + DeltaTime;
    if (Par.Age >= Par.TerminalAge) then begin
      Dec(FParticlesAlive);
      Par^:= FParticles[FParticlesAlive];
      Continue;
    end;

    Accel := Par.Location - FLocation;
    Accel.Normalize;
    Accel2 := Accel;
    Accel := Accel * Par.RadialAccel;

    // vecAccel2.Rotate(M_PI_2);
    // the following is faster
    Ang := Accel2.X;
    Accel2.X := -Accel2.Y;
    Accel2.Y := Ang;

    Accel2 := Accel2 * Par.TangentialAccel;
    Par.Velocity.Increment((Accel + Accel2) * DeltaTime);
    Par.Velocity.Y := Par.Velocity.Y + Par.Gravity * DeltaTime;

    Par.Location.Increment(Par.Velocity);

    Par.Spin := Par.Spin + (Par.SpinDelta * DeltaTime);
    Par.Size := Par.Size + (Par.SizeDelta * DeltaTime);
    Par.Color.Increment(Par.ColorDelta * DeltaTime);

    if (FUpdateBoundingBox) then
      FBoundingBox.Encapsulate(Par.Location.X,Par.Location.Y);

    Inc(Par);
    Inc(I);
  end;

  // generate new particles

  if (FAge <> -2) then begin
    ParticlesNeeded := FInfo.Emission * DeltaTime + FEmissionResidue;
    ParticlesCreated := Trunc(ParticlesNeeded);
    FEmissionResidue := ParticlesNeeded - ParticlesCreated;

    Par := @FParticles[FParticlesAlive];
    for I := 0 to ParticlesCreated - 1 do begin
      if (FParticlesAlive >= MAX_PARTICLES) then
        Break;

      Par.Age := 0;
      Par.TerminalAge := FHGE.Random_Float(FInfo.ParticleLifeMin,FInfo.ParticleLifeMax);

      Par.Location := (FLocation - FPrevLocation) * FHGE.Random_Float(0,1);
      Par.Location.Increment(FPrevLocation);
      Par.Location.X := Par.Location.X + FHGE.Random_Float(-2,2);
      Par.Location.Y := Par.Location.Y + FHGE.Random_Float(-2,2);

      Ang := FInfo.Direction - M_PI_2 + FHGE.Random_Float(0,FInfo.Spread) - FInfo.Spread / 2;
      if (FInfo.Relative) then begin
        Temp := FPrevLocation - FLocation;
        Ang := Ang + Temp.Angle + M_PI_2;
      end;
      Par.Velocity.X := Cos(Ang);
      Par.Velocity.Y := Sin(Ang);
      Par.Velocity.Scale(FHGE.Random_Float(FInfo.SpeedMin,FInfo.SpeedMax));

      Par.Gravity := FHGE.Random_Float(FInfo.GravityMin,FInfo.GravityMax);
      Par.RadialAccel := FHGE.Random_Float(FInfo.RadialAccelMin,FInfo.RadialAccelMax);
      Par.TangentialAccel := FHGE.Random_Float(FInfo.TangentialAccelMin,FInfo.TangentialAccelMax);

      Par.Size := FHGE.Random_Float(FInfo.SizeStart,
        FInfo.SizeStart + (FInfo.SizeEnd - FInfo.SizeStart) * FInfo.SizeVar);
      Par.SpinDelta := (FInfo.SizeEnd - Par.Size) / Par.TerminalAge;

      Par.Spin := FHGE.Random_Float(FInfo.SpinStart,
        FInfo.SpinStart + (FInfo.SpinEnd - FInfo.SpinStart) * FInfo.SpinVar);
      Par.SpinDelta := (FInfo.SpinEnd - Par.Spin) / Par.TerminalAge;

      Par.Color.R := FHGE.Random_Float(FInfo.ColorStart.R,
        FInfo.ColorStart.R + (FInfo.ColorEnd.R - FInfo.ColorStart.R) * FInfo.ColorVar);
      Par.Color.G := FHGE.Random_Float(FInfo.ColorStart.G,
        FInfo.ColorStart.G + (FInfo.ColorEnd.G - FInfo.ColorStart.G) * FInfo.ColorVar);
      Par.Color.B := FHGE.Random_Float(FInfo.ColorStart.B,
        FInfo.ColorStart.B + (FInfo.ColorEnd.B - FInfo.ColorStart.B) * FInfo.ColorVar);
      Par.Color.A := FHGE.Random_Float(FInfo.ColorStart.A,
        FInfo.ColorStart.A + (FInfo.ColorEnd.A - FInfo.ColorStart.A) * FInfo.ColorVar);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -