Delphi Sprite Engine – Part 6 – DelphiGlass

pigeon

[If you’re new to this series, you may wish to skip ahead to part-7, where I’ve done a partial ‘start-over’]

It’s been a long time since I posted an update to our Delphi Sprite Engine. Regretfully, work, and other commitments, took precedence over my time, and I’d like to do something to make up for that, and so I’d like to introduce DelphiGlass.

Introducing DelphiGlass

[Edit: Library renamed to DelphiGlass ]

A short while ago, I decided that the sprite engine could use a bit of a redesign. What we had, worked. It was only ever intended to be an example of how to render 2D graphics with FireMonkey, but the sprite sheet was difficult to construct, because it had to be constructed in-code first. There was also no means of separation between the rendering loop and the scene logic.

In order to remedy this, I started over and wrote ‘DelphiGlass’ my new sprite engine for Delphi. DelphiGlass is still in the early stages, possibly too early to share, but having just returned from a long vacation on top of the other delays, I’ve decided to share this with you. This is the project status…

  • Implemented resource management.
    Sprites, images and animations so far, but it will also include backdrops, tile-maps, and audio resources.
  • Implemented component editor.
    The resource management has component editors which install into the IDE, making editing of resources far easier. This also allows you to include resources within your project file, embedded into the form if you chose to do so, and this makes deployment far easier.
  • Separate Logic and Render threads.
    Although I’ve only permitted a single thread for logic at this point, the game and animation logic is running in it’s own task, and therefore it’s own thread if the task pool sees fit. This means that rendering can progress at a set frame rate, while the logic which controls physics for example will run independently. More work is needed in this area, but the initial code is in place.
  • Lacking source code documentation!
    Lacking documentation for the source code is about as close as something gets to being a sin in my view. The code needs documentation and will get it, but I omitted this step in order to get the engine started before I left for my vacation. Watch this space.
  • Comparable to the previous engine.
    In terms of capabilities, this engine is comparable to what we had at the end of part 5.5, in that it currently supports little more than animated sprites. There is still no scrolling backdrop and there is still no tile map system. I can’t apologize enough that they aren’t ready yet, but like I said in my introduction, I think some of the features of this new rework will compensate for the delay. Keep reading 🙂
  • Un-tested
    What I have made certain of this time, is that the engine can be deployed out to mobile devices. Well, actually I’ve tested only on Android so far as I’m lacking an iOS device right now, but it does deploy and function on Windows and Android as expected. That’s about as far as I’ve gone with testing the engine. Many more tests will be required, but as I said, time has been short.
  • XE7 / XE8 or higher
    The code currently requires XE7 or higher, and has been written using XE8. There is nothing in the code thus far which prevents you using it in older FMX enabled versions. I expect it’ll work just fine with XE6. Minor code changes may be required for XE5 (some classes moved around within the FMX units). At a later time, I’ll get this code working on older versions using conditional defines, but until then you’ll require at least XE7 or XE8 to make it run.

Getting DelphiGlass

Download source code here: delphiglass

I recently began a subversion repository to accompany my blog. DelphiGlass is inside that repository, and so to get your hands on it you’ll need a subversion client. There happens to be a subversion client built into the RadStudio IDE, and there is also a great third party option for windows named TortoiseSVN. If you’re unfamiliar with using SVN, please go read up on how to check-out a project with your chosen client.

You’ll find the repository here svn://public.chapmanworld.com/public and DelphiGlass is in the sub-directory svn://public.chapmanworld.com/public/DelphiGlass

Go ahead and check out a copy of DelphiGlass, and for the remainder of this post I’ll show you how to install and make use of the engine. In future posts, I plan to enhance the engine and share the enhancements with you.

(*note: I’ll 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 a little later.*)

Installing DelphiGlass

Once checked out, you should have the following directory structure.

DelphiGlass

Within the root of the directory is a project group named grpDelphiGlass.groupproj open this project in your IDE.

DelphiGlassGroup

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

InstallDelphiGlass

DelphiGlassInstalled

InstallDelphiGlassDesign

DelphiGlassDesignInstalled

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

DelphiglassComponents

Having installed these new components, you might like to restart your IDE at this point. It seems that sometimes the IDE becomes unstable after installing these components. I don’t know if this is a fault in the components or the IDE and I’ll investigate further, however, the simple solution is to restart the IDE before making use of the components.

Your new components.

You now have three new components in the component palette. Here’s a brief description of each of them…

  • TdgResourcePackage – This component is similar to the sprite sheet class from our previous engine, in that it 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 we’ll see in a moment.
  • TdgViewport – This is the component that actually performs the rendering of your application graphics. The TdgViewport hosts a tree of primitives (TdgPrimitive) which are the objects that actually get rendered. At present, only a single primitive is available, but it’s a very useful one, the TdgTextureRect, which enables you to paste a texture onto your screen.
  • TdgScene – This component is where your scene is constructed, objects are created, moved, and modified by the application logic. This is another tree of objects, which connect to the viewport to create and manage viewport primitives. Every time a frame of animation is to be rendered by the viewport, your TdgScene will be called upon to update the primitives which it has placed in the viewport, and those primitives will then be rendered.

Pigeon!

The DelphiGlass repository contains a sample application (in the samples directory) which you’ll recognize from the previous sprite engine. It’s the animated pigeon. For the remainder of this post, we’ll construct that same application from scratch, so that you get a feel for working with DelphiGlass.

In the IDE start a new Multi-Device Delphi application, and select a 3D Application from the wizard.

NewMDDelphi

3DApp

Now drop the following components onto your new form..

  1. TdgResourcePackage
  2. TdgViewport
  3. TdgScene
  4. TTimer

Be sure to drop the TdgViewport onto your form BEFORE you drop on the TdgScene. The threads were the most recently added feature to DelphiGlass, and thus-far, a graceful application shutdown will only occur if the viewport is disposed before the scene. For that reason, you must add the viewport before the scene. This is a known bug which will be resolved soon. Note also, as you drop the viewport component, your form should turn blue (this is the default backdrop color).

Now that you have the components on your form, select the scene component and set it’s ‘viewport’ property to ‘dgViewport1’.
Also, select the viewport component and ensure that it’s ‘Form3D’ property has been correctly set to your main form ‘Form1’.

The resource package editor.

Right click on the Resource Package component and select “Show Editor”

ShowEditor

This will open up the resource package editor window…

ResourceEditor

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 recreate the flapping pigeon sprite that we’ve used previously.

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 we’ll 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, I’ve tried to make the keyboard shortcuts useful for the repetitive tasks which must be performed in this editor, it’s worth learning them.

ResourceSelection

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 select, 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.

Level1

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.)

Level1tree

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 we’ll 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”…

HeroResource

Hero

The sprite sheet resource is the resource which we use to store image data for our sprites. You’ll 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. We’re 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).

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

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”

flapresource

flapdivide

With the ‘flap’ animation resource selected, you’ll 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 we’ll edit the animation frames.

Add a new resource, this time a ‘frame’ resource named ‘0’ (zero)…

frameresource

framezero

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 1 1 155 165
1 1 168 155 165
2 1 335 155 165
3 1 502 155 165
4 158 1 155 165
5 315 1 155 165
6 472 1 155 165
7 629 1 155 165
8 158 168 155 165
9 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

editanimation

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

savepigeon

You now have the resource package that you need for this application.

Resource Loading

I’d 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 it’ll 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, we’ll 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, I’ll resolve this soon.)

Now go ahead and save your project and associated form files.

Resource location.

We’re now at the point where we should wire the demo together and make it function. We’ll start with some initialization code. Double click on the ‘OnCreate’ event for your form and add this code…

procedure TForm1.Form3DCreate(Sender: TObject);
begin
  with TdgSprite.Create(dgScene1.Root) do begin
    Resource := dgResourcePackage1.Resource['ROOT/Level 1/Hero/flap'];
    Width := 150;
    Height := 150;
  end;
  dgScene1.Enabled := True;
  Timer1.Enabled := True;
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 it’s 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 width and height of the sprite we’re rendering (in pixels), and then enable both the dgScene and Timer components.

Our dgScene, once enabled, will begin rendering repeatedly within it’s own thread. Our timer is then used to perform any game / scene logic. This is a temporary arrangement for the purposes of this demo. In later revisions we’ll remove the timer and place game logic inside custom scene components. For now though, the timer works well.

Completing the wiring. 

Drop back to your form and set the timer interval to ’10’, it’s enabled property to ‘FALSE’, and then add the following code to the timer OnTimer event.

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  TdgSprite(dgScene1.Root.Children[0]).Frame := succ(TdgSprite(dgScene1.Root.Children[0]).Frame);
end;

With this code we’re locating the sprite that we created earlier and updating it’s frame counter to display the next frame of animation. Note that we’re locating the sprite component here by addressing the scene component’s tree. To do this more correctly, we should keep a class-global reference to the sprite, which we created during initialization, but as we currently only have a single sprite in our demonstration, this technique will suffice.

Finally, we need to add a single line to the form OnDestroy event…

procedure TForm1.Form3DDestroy(Sender: TObject);
begin
  dgScene1.Enabled := False;
end;

That’s it. Our pigeon demo is complete.
Go ahead and try running it…

FlapsAway

Conclusion

Our sprite engine “DelphiGlass” is functioning as well as the previous engine, but is now far better structured. There are some known bugs, and a lack of code documentation, which I’ll address in pending posts, but I feel we now have a far better basis to start from.

Licensing

I don’t like imposing licenses, and have no commercial aspirations for this code.

As it stands today (1 June 2015, at revision 93), this engine is not exactly of commercial quality and I’m not imposing any licensing restrictions on it. You can consider it public domain and use it for any purpose you see fit, so long as you understand that I do not provide it as fit for any particular purpose, and I can’t be held responsible for it’s use or misuse. If you do correct a bug, or create an enhancement, I’d appreciate you sharing it with me but this is not a requirement.

I may add a license for future versions, in which case it will be an open source license under similar conditions as above. Depending on contributions from others a small fee may be added for commercial use of subsequent versions, but this revision remains free as it is right now.

Thanks for Reading

Print Friendly, PDF & Email
Facebooktwittergoogle_plusredditpinterestlinkedintumblrmail

6 Responses

  1. narcis75@gmail.com says:

    Thank you for your quick and professional response! So , is it doable to even think to develop a multiplatform 2d game with xe8?

    • It’s certainly doable.

      You should keep in mind that RAD Studio / Delphi is not made for, nor sold for the purpose of making games. There are other tools which are specialized for the purpose, and will therefore make much lighter work for you. Having said that, if you want to make games using Delphi, and are willing to put in the extra effort, it’s as capable as any other compiler or toolset.

      I don’t genuinely have time to create a complete game engine myself (which I why I limited the scope back in part-1, and still haven’t managed all of it), but it’s been my purpose to demonstrate that it’s possible.

    • FYI: I just made another update or two, to fix issues deploying to mobile.

  2. narcis75@gmail.com says:

    All is working but is very jerky … Hints for a good game loop?

    • Yes,
      I’m aware of the jerkiness, it’s because the multi-threading isn’t really setup well.

      I’ve still not quite decided how I’m going to address this, but for some good reading on game loops, see: http://otapi.com/2014/09/30/game-engine-architecture-game-loop-vs-parallelism-part-1/

      Right now, you’ll actually get a smoother animation using a “lazy-man” loop (remove the tasks and manually call to update and render per frame). I hope to get some time to work on this in the next couple of weeks, though the new release has me busy right now.

    • So, I made some significant changes to the threading system (to the entire library actually), and now have smooth animation.
      The code is a little rough around the edges, but is working. I’ll be making a few more updates and blogging about them some time next week.
      Recommended: Open the project group and uninstall the packages, update from subversion, install the new packages, restart IDE, open Pigeon sample and run it.
      There is also a potential stability issue under windows, on shut-down I’m getting an access violation because the rendering context is released before the viewport stops rendering to it. This will be one of the things I address next week.
      Library intended for learning purposes more-so than anything else, so please enjoy.

Leave a Reply