Delphi Sprite Engine - Part 7
Delphi Sprite Engine - Part 7
Delphi Sprite Engine - Part 7
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.
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.
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).
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..
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 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 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
165
165
10
165
11
165
12
165
13
165
14
165
15
165
16
165
17
165
18
165
19
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