You are on page 1of 19

Delphi Sprite Engine Part 7

by Craig Chapman Published 2015-09-18 Updated 2015-12-11

The Delphi Sprite Engine DelphiGlass, which Ive been building for this blog series, has seen several changes and updates recently. The
animation is now smoother, the threading model is functioning, and its far more stable than it was. Id like to share these updates with you.
If youve come across a previous post in this series, youll have been directed here to part 7. This is because the earlier posts, while
interesting learning exercises, contain the mistakes that Ive made along the way. A comment from narcis75 on Part-6 gave me the nudge I
needed to go in and correct several of these mistakes.
The threading model is working, and rendering is locked to a preset FPS rate (frames per second).
Animation is now smooth (providing you have sufficient CPU to run it).
There is no longer a crash as the application shuts down.
Simulations are now accurately timed with the addition of a high resolution timer.

These corrections lead to several alterations to the engine code, so much so that it no longer resembles that which began back in the earlier
posts. In fact, even part 6 of this series is out-of-date against the current code. If you were redirected here from an earlier post, its for this
reason. From this post on, Im going to be re-explaining the code, how it functions and how to enhance it. Im going to squeeze this do-over
into as few posts as possible, so that we can get on with a few enhancements and perhaps even finish this series!
The next part.
If you read part-6 and have checked out the latest revision of DelphiGlass, you may not need to read the remainder of this post. Having said
that, there are several changes to the engine, which is why I decided to repeat the steps for obtaining and installing the enigne, and building
the pigeon demo. If you decide to skip this part, be aware of the following changes before moving on to Part-8: (if you stay with us, Ill point
out the bulk of the post which you can skip)
Changes include:
Fixed bug on shut-down of application, no longer wrecking the rendering context.
Sprites are positioned from their center, rather than the top-left.
Spites (and all scene objects) are positioned relative to their parent.
Sprites get their width and height from the animation data (size of the image / frame)
Sprites may be scaled using the ScaleX / ScaleY properties.
Sprites have a FramesPerSecond property to adjust their speed when animated.
Timers are no longer required to progress animations, this is handled internally.
The viewport is frame-rate locked using the FramesPerSecond property.
The scene runs a simulation thread, allowing for smoother movement and animation.
TdgMovementSprite class has been added, to demonstrate motion, along with a second pigeon demo.

There are also still two known bugs:


The resource package editor still doesnt inform the IDE of changes, youll need to change some other property of the form in order to store
resources within the form at design time.

For some reason the IDE fails to close a project which uses the engine, instead it hangs and goes unresponsive. My guess is that one of the
components is not releasing some resource. This needs further investigation.

For those of you that are new.


And as a reminder for those that arent.
These are the objectives of the sprite engine:
1.
2.
3.
4.
5.
6.
7.
8.
9.

Ability to display 2d images (sprites).


Support transparency (transparent zones).
Supported on Mac OSX, Windows, Android and iOS.
Hierarchical scene composition
Scrolling backdrop component.
Scrolling tile map component.
Reuse of image resources to reduce memory costs.
Support for collision detection.
Must perform sufficiently to run a simple game.

Those listed in green, the engine is already capable of. Those in Red are currently missing from the engine. The last, in blue, is achieved at
present, but must be maintained as we complete the other parts.
Getting DelphiGlass
DelphiGlass is available from my public subversion repository, and so to get your hands on it youll need a subversion client. There happens
to be a subversion client built into the RadStudio IDE, however, I have been using a third party subversion client for several years, named
TortoiseSVN. If you are unfamiliar with subversion, Id suggest you start with TortoiseSVN as it integrates well into the windows explorer,
go download and install it. If youve chosen another client, Ill presume you know well enough how to use it to obtain the engine from the
repository here: Edit: The subversion server has been decommissioned. Replaced svn link with downloadable zip file, download
here: DelphiGlass (and you can skip ahead to Installing the engine)
First, youll need to create a new directory somewhere. You can place it anywhere you like, but I keep my subversion projects in a dedicated
directory on the c:\ drive, c:\subversion\public, within which Ive created a directory DelphiGlass.

Right click on your DelphiGlass directory, and from the context menu, select SVN Checkout (if youve installed TortoiseSVN correctly,
the option will be there in the menu).

In the dialog which appears, enter the repository URL:


Edit: The subversion server has been decommissioned. Replaced svn link with downloadable zip file, download here: DelphiGlass
and click OK.
A copy of the DelphiGlass project will promptly be downloaded from the repository into your DelphiGlass directory.
From this point on, if I tell you that there are updates to the engine, you can get them by right clicking this directory and selecting the SVN
Update option.
(*note: Ill try to only ever commit working versions of DelphiGlass to the repository. If you happen to check out a copy which is not
working, please try updating it again a little later.*)
Installing DelphiGlass
When youve checked out the engine, you should have the following directory structure.

Within the root of the directory is a project group named grpDelphiGlass.groupproj, double click it to open this project in your IDE.

Right click on each of the packages pkgDelphiGlass.bpl and pkgDelphiGlassDesign.bpl and select Install from the menu.

You now have DelphiGlass installed. You should find a new tab of controls under the tool palette for FMX applications..

Your new components.


You now have three new components in the component palette. Heres a brief description of each of them
TdgResourcePackage This component stores the graphical assets used by your application. Assets are stored in a tree structure, allowing you
to provide some logical formatting to your assets for easier search and retrieval. This component has a component editor which well see in a
moment.
TdgViewport This is the component that actually performs the rendering of your application graphics. The TdgViewport hosts a list
of primitives, or rendering instructions, which are executed several times per second in order to construct each frame of animation. The
component actually contains two lists of instructions, one which is being rendered, and a second which is being constructed for the next frame.
Several times per second, these two lists are swapped. Note: There is a FramesPerSecond property which defaults to 60, you can increase or
reduce the frame-rate depending on the capabilities of your target CPU, however, you should never need to go higher than 60 FPS as this is more
than enough for smooth animation, and any additional frames rendered will only cost CPU cycles that could be used on physics simulations,
input handling, or other functionality. The simulation will run at a consistent speed regardless of frame-rates.
TdgScene This component is where your scene is constructed and your simulation is executed. Objects are created, moved, and modified by
the application logic within the scene. This is a tree of objects, enabling child objects to be rendered with relative positioning to their
parent. The TdgScene attempts to update the viewport at the same frame-rate at which the viewport is running, however, the scene its-self
may refresh far more frequently.

Pigeon!
The DelphiGlass repository contains a sample application (in the samples directory), named pigeon. Its the animated pigeon that you may
have seen in earlier blog posts. For the remainder of this post, well construct that same application from scratch, so that you get a feel for
working with DelphiGlass. If youve done this before (back in part-6), skip ahead to the section titled Wiring Up.
In the IDE start a new Multi-Device Delphi application, and select a 3D Application from the wizard. *Important, there must be a 3D form to
host the viewport component, this is why we select 3D Application*

Now drop the following components onto your new form..


1. TdgResourcePackage
2. TdgViewport
3. TdgScene

Now that you have the components on your form, select the scene component and set its viewport property to dgViewport1. Also, select
the viewport component and ensure that its Form3D property has been correctly set to your main form Form1. Finally, make sure that the
dgViewport1 and dgScene1 each have their enabled property set to True.
At this point, your form should have turned blue, because this is the default back-drop color for the dgViewport. If your form is not blue, go
back and check that you got each of the above steps.
The resource package editor.
Right click on the Resource Package component and select Show Editor

This will open up the resource package editor window

This window is a component editor plugin for the IDE which will enable you to create and edit resources for use in the DelphiGlass engine.
Lets use this editor to create the flapping pigeon sprite that well use in this application.
To the left of this window is the resource tree, which currently contains only a directory named ROOT. To the right is a preview / edit pane
which well use later. The two sides are separated by a divider control which will allow you to expand or retract the two panes.
Make sure the ROOT directory is selected and click the + button on the tool-bar, or use the keyboard short-cut [CTRL]+[A]. *note, Ive
tried to make the keyboard shortcuts useful for the repetitive tasks which must be performed in this editor, its worth learning them.

The dialog which is presented is used to create new resources. On the left is a drop-down list box which contains the resources which may be
created under the currently selected node of the resource tree. For example, we have the ROOT directory selected, which may parent
additional Directory, or Sprite Sheet resources.
Select Directory from the drop down, and then type Level 1 into the Resource Name field.

You may now click the Okay button to create the directory. (*note: The keyboard [RETURN] or [ENTER] keys may be used instead of the
Okay button, and the [ESCAPE] key used instead of pressing the Cancel button.)

You now have a new directory named Level 1 under the root node. Note that you can right-click on this node to see the + - [UP] and
[DOWN] options. This allows you to add or remove resources to this directory, or to re-order sub-directories. The reordering option is
more useful for arranging frames of animation, as well see later.
Make sure you have the Level 1 directory selected, and click + or [CTRL]+[A] to add a new resource. This time, add a sprite sheet
named Hero

The sprite sheet resource is the resource which we use to store image data for our sprites. Youll notice the two new buttons on the editing
pane to the right, these allow you to load a new image into the sprite sheet, or save out the image to a file. Were going to load in the sprite
sheet which we used previously for the pigeon demo. Download a copy here (or right click the image below, and save it to disk somewhere).

Now click on the open button in the sprite-sheet edit pane, and import this image

Now that we have the image loaded, we need to define the animation frames.
Make sure the Hero sprite sheet is selected and add a new resource, this time, an animation resource named flap

With the flap animation resource selected, youll see that the editor pane has now divided into two panes. The top one shows a preview of
the sprite sheet image. The bottom pane is where well edit the animation frames.
Add a new resource, this time a frame resource named 0 (zero)

Repeat this process in order to add frames 1 through 19. When you have 20 frames (0..19) you can begin setting the Top, Left,
Height, and Width properties of each frame. (*tip: The TAB key will progress you through the edit boxes in the correct order). Here are
the values for our sprite sheet
Frame Top Left Height Width
0

155

165

168 155

165

335 155

165

502 155

165

158 1

155

165

315 1

155

165

472 1

155

165

629 1

155

165

158 168 155

165

158 335 155

165

10

158 502 155

165

11

315 168 155

165

12

472 168 155

165

13

629 168 155

165

14

315 335 155

165

15

315 502 155

165

16

472 335 155

165

17

629 335 155

165

18

472 502 155

165

19

629 502 155

165

Having loaded the coordinates of your animation frames into the editor, you should save a copy of this resource package (to prevent having
to enter this information again should anything go wrong.)
In the main tool-bar, click the save icon and save this resource package as pigeon.rpk

You now have the resource package that you need for this application.
Resource Loading
Id like to take a moment now to tell you about some of the features which have been introduced with the resource package component. The
most important of which are the loading options.
If you wish to deploy your application as a single binary file, you can do so by populating the resource package component, and then saving
your form. The resource package data is converted into a binary stream and inserted into your form, which will be compiled into your binary
at compile time. This is handy for simple deployments, however, it is not a preference for everyone, as itll significantly increase the size of
the executable file if you package resources inside it. (*note: As a side note, there is a minor known bug in the component editor, changes
may not be saved if you do not alter any other property of the form. Always save a copy of your package file, regardless of your chosen
deployment method.)
As an alternative to packaging the resources up inside the executable, you could right click the Resource Package component and select
Clear Package to empty the component. Assuming you have a copy of the resource package saved, you can call the .LoadFromFile()
method at run-time to load the resource from disk.
Finally, if you wanted to deploy resources over the internet, you could download a resource file and load it at run-time, or even, stream your
download into memory and use the .LoadFromStream() method to load it into the Resource Package component.
For simplicity of deployment, well leave the Resource Package component populated with the sprite sheet, and have the resource bundled
into our binary file. This may have a slowing effect on the IDE as it loads your form, but is a lot easier for us to use for demonstration
purposes.
Lets ensure our resources are saved with the form. You can do this by altering the color property of your form. This change has no effect on
the appearance of your form because the viewport component is overriding the rendering of the form to set it blue. What this does is to
register a change to the form, which means the IDE will save our changes. (this is the known bug I mentioned earlier, editing the resource
package does not indicate to the IDE that a change was made, Ill resolve this soon.)

Now go ahead and save your project and associated form files.
Wiring up.
Were now at the point where we should wire the demo together and make it function. Well start with some initialization code. Double click
on the OnCreate event for your form and add this code
with TdgSprite.Create(dgScene1.Root) do begin
Resource := dgResourcePackage1.Resource['ROOT/Level 1/Hero/Fly'];
Position := TPointF.Create(100,100);
FramesPerSecond := 40;
end;

The first thing this code does is to create a sprite component which is parented to the root node of dgScene1. We then set the resource
property of the sprite using the dgResourcePackage1.Resource[] accessor. Note that we specify the animation for the sprite class using the
path of the animation resource as constructed inside the resource package editor!
This is an important thing to notice. It means that specifying a resource can be done in a human readable way, but this comes at a cost.
Anything human readable, is less machine readable, and as such will have a performance cost. You should therefore locate any resource that
you need during the initialization of your application (or level), and keep references to them. This will prevent you having to do this look-up
again during the rendering cycle, where every CPU tick counts.
An additional thing to notice is that the dgResourcePackage1.Resource[] accessor does not have a specific resource type. We selected a sprite
sheet by its path, but had we selected some other resource the code would have functioned just as well. In fact, the TdgSprite class is able to
render an animation, a single frame, or an image resource and so we could have selected any of these resource types. Had we specified an
invalid type, such as a directory for example, the sprite class would generate an exception at run-time.
Having specified the resource, we set the initial position of the sprite that were rendering (in pixels). The position of the sprite is oriented
from its center, so Ive selected coordinates 100100 to make room for the sprite against the edge of the screen. Its width and height are
determined from the animation resource, however, you could adjust the ScaleX and ScaleY properties to alter its size as required.
Finally, we set the FramesPerSecond property to 40. This is because our animation has 20 frames, and Id like the pigeon to flap its wings
twice per second, so 20*2=40. We can set any frame rate we like here, itll operate at this rate regardless of the rendering frame-rate of the
viewport.
Note, at present it would be pointless to set the frame-rate higher than that of the viewport, because the sprite class will not function this way,
but in future versions of the engine, you might actually want to set the frame-rate of an animation higher than that of the viewport. When?
Well its a rare case, but suppose you had a very detailed animation with hundreds of frames, this would run very slowly at 60-FPS (a typical
viewport framerate), so in order to make this animation run more quickly youd need to set a high frame-rate and have the DelphiGlass
engine select the appropriate frames. Watch this space, Ill be adding this functionality soon.
At this point, go ahead and run the application, our pigeon demo is complete.

Conclusion
The DelphiGlass sprite engine now has a far smoother rendering loop. Its rendering is frame-rate locked, allowing us to take advantage of
remaining CPU cycles for our applications. Some known bugs still remain, but they are fewer than the previous revision. Most importantly,
the DelphiGlass engine is now at a good point for enhancements, such that we might finally achieve the goals set out in part 1 of this series!
Thanks for Reading

You might also like