A Cool Parallax Starfield Simulation Using FireMonkey

Posted by on in Blogs
There is nothing like writing demo projects. Whenever I see an interesting piece of code, does not really matter in which language, I'm thinking about moving it to Delphi. Delphi XE2 is all about cross-platform and mobile development, so "moving to Delphi" specifically means for me these days "moving to Delphi iOS mobile app".

Every framework and every language is different, so at one level it is about understanding syntactic difference between programming languages, and at the other level it is also about differences in framework architecture.

Check out this blog post with Python version of a simple starfield simulation: http://codentronix.com/2011/04/27/a-cool-parallax-starfield-simulation-using-python/

Why not to port this code to FireMonkey and Delphi? Here we go: "A Cool Parallax Starfield Simulation Using FireMonkey"!

Create a new "FireMonkey HD iOS Application" project in Delphi XE2 IDE and save all in a location that is accessible to your XCode installation.

Add a timer component to the form and sets its "Interval" property to 20 ms, which means 50 frames per second refresh rate. In the "OnTimer" event just call "Invalidate" to force the "OnPaint" event of the form.

Instead of implementing updating and painting code directly in the "OnPaint" event handler, I have decided to create a standalone "TStarsApp" class that encapsulates application state updates and drawing code.

Here is the code for my application form:
unit FormStarsUnit;

interface

uses
SysUtils, Types, UITypes, Classes, Variants, FMX_Types, FMX_Controls, FMX_Forms,
FMX_Dialogs, AllStars;

type
TForm1 = class(TForm)
Timer1: TTimer;
procedure Timer1Timer(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormPaint(Sender: TObject; Canvas: TCanvas; const ARect: TRectF);
procedure FormResize(Sender: TObject);
private
FApp: TStarsApp;
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.lfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
FApp := TStarsApp.Create(self.Width, self.Height);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
FApp.Free;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
Invalidate;
end;

procedure TForm1.FormPaint(Sender: TObject; Canvas: TCanvas;
const ARect: TRectF);
begin
FApp.Update;
FApp.Draw(Canvas);
end;

procedure TForm1.FormResize(Sender: TObject);
begin
FApp.ResizeView(self.Width, self.Height);
end;

end.

Add a new unit to your project for the starfield simulation application logic and rendering code. In my case I have saved it as "AllStars.pas", but you can go for something else.

Here is the complete source code of my "AllStars" unit where the "TStarsApp" class is defined.

unit AllStars;

interface

uses
Types, UITypes, FMX_Types, contnrs;

const
STARS_COUNT = 150;

type
TStarItem = class
Loc: TPointF;
Speed: double;
procedure Draw(const c: TCanvas);
end;

TStarsApp = class
private
FWidth,
FHeight: double;
FStars: TObjectList;
public
constructor Create(const AWidth, AHeight: double);
destructor Destroy; override;
procedure Update;
procedure Draw(const c: TCanvas);
procedure ResizeView(const AWidth, AHeight: double);
end;

implementation

uses
Classes;

{ TStarsApp }

constructor TStarsApp.Create(const AWidth, AHeight: double);
var i: integer; s: TStarItem;
begin
FWidth := AWidth;
FHeight := AHeight;
FStars := TObjectList.Create;
for i := 0 to STARS_COUNT-1 do
begin
s := TStarItem.Create;
s.Loc := PointF(random(round(FWidth)), random(round(FHeight)));
s.Speed := 1 + random(2);
FStars.Add(s);
end;
end;

destructor TStarsApp.Destroy;
begin
FStars.Free;
inherited;
end;

procedure TStarsApp.Draw(const c: TCanvas);
var i: integer; s: TStarItem;
begin
c.Clear(claBlack);

for i := 0 to FStars.Count-1 do
begin
s := TStarItem(FStars[i]);
s.Draw(c);
end;
end;

procedure TStarsApp.ResizeView(const AWidth, AHeight: double);
var i: integer; s: TStarItem; temp: double;
begin
FWidth := AWidth;
FHeight := AHeight;
for i := 0 to FStars.Count-1 do
begin
s := TStarItem(FStars[i]);
temp := s.Loc.X;
s.Loc.X := s.Loc.Y;
s.Loc.Y := temp;
end;
end;

procedure TStarsApp.Update;
var i: integer; s: TStarItem;
begin
for i := 0 to FStars.Count-1 do
begin
s := TStarItem(FStars[i]);
s.Loc.Y := s.Loc.Y + s.Speed;
if s.Loc.Y > FHeight then
s.Loc := PointF(random(round(FWidth)),0);
end;
end;

{ TStarItem }

procedure TStarItem.Draw(const c: TCanvas);
begin
if Speed > 2 then
c.Stroke.Color := MakeColor(255,255,255)
else if Speed > 1 then
c.Stroke.Color := MakeColor(190,190,190)
else
c.Stroke.Color := MakeColor(100,100,100);

c.StrokeThickness := 1;
c.Stroke.Kind := TBrushKind.bkSolid;
c.DrawLine(PointF(Loc.X-1, Loc.Y), PointF(Loc.X+1, Loc.Y), 1);
c.DrawLine(PointF(Loc.X, Loc.Y-1), PointF(Loc.X, Loc.Y+1), 1);
end;

initialization
Randomize;

end.

After exporting the Delphi project to XCode with the "dpr2xcode" tool, you can run the starfield simulation in the iOS Simulator or on the physical iOS device.



The source code for this demo is available for download in Embarcadero CodeCentral.


About
Gold User, Rank: 9, Points: 364
Crazy about Delphi Programming!

Comments

  • Guest
    Jeremy North Monday, 11 June 2012

    Cool. I wrote something similar for a little game I put together for the iPad.

    BTW for how you have written the code, you should be using random(3), not random(2).

  • Please login first in order for you to submit comments
  • Page :
  • 1

Check out more tips and tricks in this development video: