Tutorials

Andorra 2D Tutorials
Part 3 - The Spriteengine


Introduction
Introduction of the Introduction
Finally it's here: The (first) tutorial which will teach you how to use the Spriteengine. In this tutorial we will modify our project from the last tutorial.


What are sprites?
Sprites are objects which are (simply said) able to move independently from other objects (Check out the correspondingWikipedia Article for more information.).
In Andorra 2D, a sprite is an abstract object which is drawable, moveable and collideable. This actions are controlled by an surperior object (which actually is a sprite, too). Sprite do not have to be moveable - they can also be used to display the background.
And even more - you can write whole games using the Spriteengine.

Creating the SpriteEngine
We have used 4 units so far:

  • TAdDraw - The Andorra 2D core
  • TAdPerformanceCounter - Calculates the FPS and the "Timegap"
  • TAdImageList - Holds all the images
  • TAdImage - Manages and draws the images


In this tutorial you will learn how to use the object "TSpriteEngine". First of all, you should add the unit "AdSprites" to the uses-List of your program. Declare a new variable named "AdSpriteEngine" of the type "TSpriteEngine".
The following line of code - which should be inserted after the creation of your Imagelist - will create the SpriteEngine:

Delphi-Code:
AdSpriteEngine := TSpriteEngine.Create(AdDraw);

As a matter of course you should free this object at the end of your program.

The family of sprites
Warning: If you do not know much about objectorientated programming yet, you should altercate with that first. Otherwise you may not understand everything.

As already mentioned above a sprite is not only used to draw a simple image. It is able to manage something much more abstract (the Spriteengine itself is a good example for that). Thats why there are multiple spriteclasses available. They all have something in common: They are derived from TSprite, the base-sprite-class.

  • TSprite
    • TImageSprite
      • TImageSpriteEx

    • TBackgroundSprite
    • TLightSprite
    • TParticleSprite


The baseclass is a "naked" sprite, which literally is not able to do anything but integrating itself into the Spriteengine. TImageSprite already is able to do anything we would want from a sprite: Drawing and animating. Again, TImageSpriteEx is capable of being drawn transparently and can be turned. TBackgroundSprite is (surprise!) to draw a background. TLight and TParticleSprite will be addressed later.

Go, Sprite, Go
We will now get the guy from our last tutorial running across the screen, but this time we'll use a sprite. So get your result from the last tutorial and get started!
We add the creation of the SpriteEngine and the TImageSprite with the name "figure" the initialisation routine:

Delphi-Code:
.
.
AdImageList.Restore;

//create the SpriteEngine
AdSpriteEngine := TSpriteEngine.Create(nil);
AdSpriteEngine.Surface := AdDraw1;

//create TImageSprite
Figure := TImageSprite.Create(AdSpriteEngine);
with Figure do
begin
  //we'll go on here later on
end;

Do not free "Figure" at the end of your program - you would end in a mess of errors. The spriteengine frees the sprites automatically. If you want a Sprite to be free earlier you have to use it's dead routine.

We now have an "empty" sprite, without graphics or animation:

Delphi-Code:
//assign a graphic to the sprite
Image := AdImageList.Find('figur')
//activate the animation
AnimActive := true
//repeat the animation
AnimLoop := true
//set the fps the animation should be played at
AnimSpeed := 15;

From the variables we used before (Pattern,StartPt,EndPt,Y,X,XSpeed) is only one left: XSpeed, because all the other ones are part of TImageSprite.


We will now modify the render routine:

Delphi-Code:
if AdDraw.CanDraw then
begin
  AdPerCounter.Calculate
   
  Figure.X := Figure.X + XSpeed*AdPerCounter.TimeGap/1000;
  if ((Figure.X > ClientWidth) and (XSpeed > 0)) or
     ((Figure.X < -96) and (XSpeed < 0)) then
  begin
    SetLine;
  end;


  AdDraw.ClearSurface(clBlack);
  AdDraw.BeginScene;

  //call all move-procedure of the engine
  AdSpriteEngine.Move(AdPerCounter.TimeGap / 1000);
  //draw all sprite
  AdSpriteEngine.Draw;
  //delete all sprites wich are marked as dead
  AdSpriteEngine.Dead;

  AdDraw1.EndScene;
  AdDraw1.Flip;

  Done := false;
end;

The following things have changed:

  • The part, which was responsible for the animation, has completly gone
  • Instead of modifying the value of the variable "X" directly, we now modify the X-Position
  • The drawing-routine has gone and is replaced by a routine in the spriteengine

We now have to modify the SetLine procedure a bit:

Delphi-Code:
procedure TForm1.SetLine;
begin
  XSpeed := -XSpeed;
  if XSpeed > 0 then
  begin
    Figure.AnimStart := 0;
    Figure.AnimStop := 7;
    Figure.X := -96;
  end
  else
  begin
    Figure.AnimStart := 8;
    Figure.AnimStop := 15;
    Figure.X := ClientWidth+96;
  end;
  Figure.Y := Random(ClientHeight-96);
end;

The changes made in this part should be clearly understandable.

And now our figure runs across the screen.

Our own spriteclass
Looking at the solution we have so far we have to admit that it is more or less suboptimal. Wouldn't it be better to have a Spriteclass that does all the movement for us? It would be no problem to have several figures moving independently from each other.

Actually this is possible - thats what the spriteengine is for.
So lets declare a class which is derived from TImageSprite or TImageSpriteEx (just as you like). The declaration should take place above the one of TForm1.

Delphi-Code:
type
  TFigure = class(TImageSprite)
    private
    protected
      procedure DoMove(TimeGap:double);override;
    public
      XSpeed:integer;
      constructor Create(AParent:TSprite);override;
      procedure SetLine;
  end;

DoMove, DoDraw...
Lets have a look at what we have so far:
The "public"-part now has a variable named "XSpeed" - which is the same as our previously globally declared "XSpeed" (which should be deleted by the way...).
The procedure SetLine hat now become part of our sprite, too. Again theres an overwritten constructor.
Now we have nearly everthing which belongs to our figure stored in its' own class. But where is the code that cares about the movement? That's what we have "DoMove" for. Andorra 2D provides a set of those "Do"-Methods:

Delphi-Code:
//this is where the movement takes place
procedure DoMove(TimeGap:double);
//this is where we store our code for collisions (later)
procedure DoCollision(Sprite:TSprite; var Done:boolean);
//this is where we the drawing is done (even later ;) )
procedure DoDraw;

Now we have the base frame of a new class called "TFigure". Thats why the variable "Figure" can now be of the type "TFigure".
The only thing left is the implementaion:

Delphi-Code:
{ TFigure }

constructor TFigure.Create(AParent: TSprite);
begin
  inherited;
  //Start at (0;0)
  X := 0;
  Y := 0;
 
  //Set XSpeed to -150
  XSpeed := -150;
end;

procedure TFigure.DoMove(TimeGap: double);
begin
  inherited;

  //the part from the render-procedure
  X := X + XSpeed*TimeGap;
  if ((X > Engine.SurfaceRect.Right) and (XSpeed > 0)) or
     ((X < -96) and (XSpeed < 0)) then
  begin
    SetLine;
  end;
end;

procedure TFigure.SetLine;
begin
  //moves the figure to a new layer
  XSpeed := -XSpeed;
  if XSpeed > 0 then
  begin
    AnimStart := 0;
    AnimStop := 7;
    X := -96;
  end
  else
  begin
    AnimStart := 8;
    AnimStop := 15;
    X := Engine.SurfaceRect.Right+96;
  end;
  Y := Random(Engine.SurfaceRect.Right-96);
end;

We have to change the code in FormCreate to correspond to our new changes. The following Code has to be replaced:

Delphi-Code:
Figure := TImageSprite.Create(AdSpriteEngine);
.
.
Delphi-Code:
Figure := TFigure.Create(AdSpriteEngine);
with Figure do
begin
  Image := AdImageList1.Find('figure');
  AnimActive := true;
  AnimLoop := true;
  AnimSpeed := 15;
  XSpeed := -150;
  SetLine;
end;

Marathon
Lets have more than one figure running across the screen. Delete the declaration of Figure and replace it by the following code:

Delphi-Code:
for i := 0 to 5 do
begin
  with TFigure.Create(AdSpriteEngine) do
  begin
    Image := AdImageList1.Find('figure');
    AnimActive := true;
    AnimLoop := true;
    AnimSpeed := 15;
    XSpeed := -(random(100)+50);
    SetLine;
  end;
end;

Yet six manikin with different speeds run across the screen.

The whole sourcecode is available at:hier.

Homework
You did it until here. But now I'm having a few assignments for you:

  • It is possible that some Figures are displayed over another one although it should be behind it. Remove this error! Hint: The class TSprite has a variable "Z" which is to define its position according to the background.
  • All figures look the same. Include a few more graphics!

If you have any questions, feel free to contact me at:E-Mail.

Conclusion
You're now able to deal with the spriteengine. The next tutorial will deal with an old DelphiX-demo, in which you move as a ball through a world peopled with other balls. Certainly, we will "upgrade" it a bit.
The last thing I wanted to notice is that I didn't invent the spriteengine. It's a reproduction of the spriteengine of DelphiX - although it has been enhanced.


Copyright und Lizenz
(c) by Andreas Stöckel März 2007
Translated by Romi

Revision 2: Oktober 2007

This tutorial is licenced under the GNU Free Documentation License


This page was generated with the help of the following PHP-Scripts: GeSHi a free PHP Syntax highlighter and StringParser_BBCode a free BBCode Parser.