Joe White’s Blog

Life, .NET, and Cats


Archive for January, 2008

A “Hello world” (un)DelphiX application

Sunday, January 13th, 2008

There comes a time, in any development project, where you have to actually write some code. So let’s see about doing some of that.

This post is part of my “Tyler” series, which aims to make a tile-based game-development engine in Delphi.

Why should I read this article, instead of just opening one of the samples that comes with (un)DelphiX?

How is this article any different from looking at the “Hello, world” (un)DelphiX sample application? Well, three ways:

  1. This doesn’t use third-party components on the designer, so it’ll work in the free Turbo Delphi Explorer.
  2. This code uses frames, which may help reusability when it comes time to make a game IDE.
  3. And last, this code has an author who knows about a bug that needs to be fixed. It’s not an (un)DelphiX bug, though, so the (un)DelphiX samples don’t fix it.

Prerequisite #1: a RAR extractor

(un)DelphiX is available in two forms: as an installer, and as a RAR file. Component installers are a huge pain, and probably wouldn’t work with Turbo Delphi anyway. So I recommend downloading the RAR. It’s a pain too, but less so than an installer.

But most people don’t have any programs that can open RAR files. So you’ll probably need to install one.

I recommend 7-Zip. It’s free and open-source. It has shell integration (i.e., you get a nifty right-click menu in Explorer). It supports ZIP files, and it can read (but not create) RAR files. And did I mention that it’s free?

Prerequisite #2: (un)DelphiX

Okay, this one is no surprise: if we’re going to use the (un)DelphiX components, we need to download them first.

Find them at the (un)DelphiX page; scroll down to the “Source file” section. Decide whether you care about having sample code for the DelphiX components, and download either the “all in one” (includes sample code, much larger) or the “source only” (no sample code) archive. If you’re not sure, “source only” is fine.

These directions were written for (un)DelphiX version 1.07f.

Note: I’m not going to walk you through installing the design-time components. If you want to look at the sample code, you’ll need to install the packages yourself. (Actually, their sample code will compile just fine without the design packages, you just won’t be able to use the form designer.)

Design principles

Let me explain the ideas behind some of the decisions I made in the following code.

  • Use frames. You could create all the (un)DelphiX components directly on the form, but I prefer to put them on a frame. Partly that’s because I think the form is going to end up with enough to do, what with supporting fullscreen mode and all. Partly it’s because I think I’ll want to reuse the animation GUI later, when I make a map designer. I’ll let you decide whether that’s YAGNI or just good separation of responsibilities.

  • Don’t drop frames onto other design surfaces at design-time. Delphi’s visual form inheritance (and I include “dropping frames onto other design surfaces” into this category) is unusable. I have a theory that one day, the Delphi developers said, “Encapsulation? Pshaw. Who needs it?”, and wrote Visual Form Inheritance. I, on the other hand, like to separate concerns, so that my code won’t be quite as fragile as VFI-infested code tends to be. In the long run, I find it’s far better to instantiate frames in code, rather than using the visual form inheritance and then wondering why my properties aren’t updating.

  • Enjoy the goodness that is strict private. Encapsulation. Something Delphi should have had from the beginning (see my Theory of Visual Form Inheritance, above). I only use “loose private” and “loose protected” when I absolutely can’t get away without “friend” semantics, and for whatever odd reason can’t live with something being public either. All other times, I use the real thing. Delphi is the only programming language I know of that makes you add an extra keyword to do the Right Thing (TM).

</rant>

Now, let’s make some code happen.

Setting up a development directory and creating a project

  • Create a directory on your hard drive to hold all of the source files. For the sake of discussion, I’ll assume you named this folder Tyler.
  • Create two subdirectories under Tyler named:
    • Dcu
    • UnDelphiX
  • Copy everything from the (un)DelphiX archive’s Source directory into your Tyler\UnDelphiX directory. (If you don’t plan to install the design-time components, you can leave out the .dcr files and the package files.)
  • Open Delphi.
  • Create a new app (File > New > VCL Forms Application – Delphi for Win32).
  • Go into Project > Options > Directories/Conditionals, and make these changes:
    • Unit output directory = Dcu
    • Search path = UnDelphiX

Creating a frame and setting some properties

  • Set the following properties on the form:
    • BorderIcons = [biSystemMenu, biMinimize] (i.e., turn off biMaximize)
    • BorderStyle = bsSingle
    • Caption = Tyler
    • Name = frmMain
  • Add a new frame with File > New > Frame (or File > New > Other > Delphi Projects > Delphi Files > Frame).
  • Set the following property on the frame:
    • Name = fraViewer
  • Save the form as Tyler\uiMain.pas.
  • Save the frame as Tyler\frViewer.pas.
  • Save the project as Tyler\Tyler.dpr.

Setting up the animation-viewer frame

Switch to the frame file (frViewer) and make these changes:

  • Add DXClass, DXDraws to the interface uses clause.
  • Add the following to the class declaration:
    strict private
      FDXDraw: TDXDraw;
      FDXTimer: TDXTimer;
      procedure DXDrawFinalize(Sender: TObject);
      procedure DXDrawInitialize(Sender: TObject);
      procedure DXTimerTimer(Sender: TObject; LagCount: Integer);
    public
      constructor Create(AOwner: TComponent); override;
      procedure Initialize;
  • Implement the methods:
    constructor TfraViewer.Create(AOwner: TComponent);
    begin
      inherited;
      FDXDraw := TDXDraw.Create(Self);
      FDXDraw.Align := alClient;
      FDXDraw.AutoInitialize := False;
      FDXDraw.AutoSize := False;
      FDXDraw.OnFinalize := DXDrawFinalize;
      FDXDraw.OnInitialize := DXDrawInitialize;
    
      FDXTimer := TDXTimer.Create(Self);
      FDXTimer.Enabled := False;
      FDXTimer.Interval := 0;
      FDXTimer.OnTimer := DXTimerTimer;
    end;
    
    procedure TfraViewer.DXDrawFinalize(Sender: TObject);
    begin
      if FDXTimer <> nil then
        FDXTimer.Enabled := False;
    end;
    
    procedure TfraViewer.DXDrawInitialize(Sender: TObject);
    begin
      if FDXTimer <> nil then
        FDXTimer.Enabled := True;
    end;
    
    procedure TfraViewer.DXTimerTimer(Sender: TObject; LagCount: Integer);
    begin
      if not FDXDraw.CanDraw then
        Exit;
    
      FDXDraw.Surface.Fill(0);
      with FDXDraw.Surface.Canvas do
      begin
        Brush.Style := bsClear;
        Font.Color := clWhite;
        Font.Size := 30;
        TextOut(30, 30, 'FPS: ' + IntToStr(FDXTimer.FrameRate));
        Release;
      end;
    
      FDXDraw.Flip;
    end;
    
    procedure TfraViewer.Initialize;
    begin
      FDXDraw.Parent := Self;
      FDXDraw.Initialize;
      FDXDraw.SetSize(ClientWidth, ClientHeight);
    end;

Setting up the form

Switch to the form file (uiMain) and make these changes:

  • Add frViewer to the interface section’s uses clause.
  • Add the following to the class declaration:
    strict private
      FViewer: TfraViewer;
    public
      constructor Create(AOwner: TComponent); override;
  • Implement the constructor:
    constructor TfrmMain.Create(AOwner: TComponent);
    begin
      inherited;
      ClientWidth := 640;
      ClientHeight := 480;
      FViewer := TfraViewer.Create(Self);
      FViewer.Align := alClient;
      FViewer.Parent := Self;
      FViewer.Initialize;
    end;

Just add CPU cycles

Compile and run. Don’t be surprised if you see a lot of hints and warnings from the (un)DelphiX code… it was originally written for Delphi 3, and hasn’t had a control freak like me maintaining it.

If all the code is right, you should see a black window with big white text in the top left, saying “FPS:” and a number. That number is the Frames Per Second: how often the screen is getting updated.

No, it’s not that fancy, but we do have an animation loop going here. The screen is furiously updating as fast as it can. We’re filling in the background with a default color, and we’re drawing stuff onto the window. Given the complexity of DirectX, that’s not too shabby.

What we’ve worked around already

DelphiX was written with the assumption that it would be put on the form’s design surface. That assumption is baked into it pretty deeply, and it’s brittle if it’s used any other way (like creating it at runtime, or putting it on a frame). A lot of this code is here to work around that weakness. (I already did the troubleshooting and the working-around, so you don’t have to.)

AutoInitialize, in particular, simply doesn’t work if the TDXDraw isn’t on a design surface (the auto-initialization happens in Loaded, which is only called by the DFM-loading mechanism), so we set it to False and call Initialize manually.

AutoSize is a little weirder. This isn’t the usual VCL AutoSize you already know. Instead, it means “automatically set the DirectX drawing surface to be the same size as the TDXDraw control”. It would be pretty silly to do anything else, really. But (at least when you’re creating controls at runtime) it only seems to work when the TDXDraw is parented directly to the form. If you so much as put it on a panel, things break — all the DirectX drawing is suddenly on a small square in the middle of the control, instead of filling the entire TDXDraw area. So once again, we turn off the automatic stuff and set the size manually.

The last trick is parenting the TDXDraw to the frame only after the frame is parented to the form. That’s because the TDXDraw looks up its parent form, and caches the reference, from its SetParent method. That’s the wrong thing to do when you need to support frames. (Handle-creation time would be better. But I digress.) The upshot is that the parentage chain, all the way up to the form, needs to be stable before you ever set the TDXDraw‘s Parent property. Otherwise you’ll get “Form not found” exceptions.

If you come away from this with the feeling that it’s not easy to write high-quality Delphi components, you’re right.

Next time: working around Delphi

In later posts, I’ll talk more about this code and what it does. But I want to talk about working code. This code works okay much of the time, but there’s still a bug in here, and this time it’s not an (un)DelphiX limitation that needs to be worked around — it’s actually a bug in the VCL.

But this post is long enough already, so I’ll save that topic till next time, when I talk about DelphiX’s “pause on inactive” feature, and the TApplication bug that gives it grief.

Calvin and Hobbes: all the quotes, all online

Saturday, January 12th, 2008

Just happened upon this terrific find: a Web site that has the full text of every Calvin and Hobbes strip. This guy typed them all in by hand!

The front page (JavaScript required) lets you view all the Calvin and Hobbes comics, stepping through them one by one (click on the comic image to advance), and shows the text above each one. You can also search.

But the real treasure is the yearly pages. They require JavaScript if you want to use the Search function, but not just to scroll through.

Game programming in Delphi: a journey begins

Saturday, January 12th, 2008

I’ve always wanted to write a video game. So, never one to start small, I’m going to write a game development kit in Delphi.

I’ve been wanting to do this for years, ever since I downloaded Verge v1 and was sorely disappointed in the development experience. It’s entirely possible that there are better game-development engines out there today, but dang it, I still want to write my own. It poses a number of intriguing challenges — and hey, I’ve never been one to pass up a chance to overengineer something.

So prepare yourself for a revolutionary, Delphi-like, RAD, OO game development environment. If I ever finish writing it.

This post is the first in my “Tyler” series, which aims to make a tile-based game-development engine in Delphi.

DelphiX, (un)DelphiX, and beyond

The first step is figuring out how to do decent animation. Video games kinda need, er, video.

I started tinkering with this last weekend. I had a couple of false starts — one was overengineered, with background threads (worked, but didn’t get good enough performance to be worth the complexity); one was underpowered, with the OnPaint handler ending with a call to Invalidate (so-so performance, too slow at larger window sizes). So I settled on (un)DelphiX, a set of Delphi components for DirectX development.

(un)DelphiX has questionable legal status. The original DelphiX was written by Hiroyuki Hori, who ceased development on it sometime in 1998 or 1999. The original included source code, but was not licensed as open source, and did not allow redistribution of modified versions. Later, someone named Micrel picked it up, made some enhancements, made it run on the latest versions of Delphi, plugged in the Jedi header translations, and made it available as the “unofficial version of DelphiX”, aka (un)DelphiX. Early versions of (un)DelphiX were distributed as a patch on top of the unmodified DelphiX, and so were clearly legal. But the latest versions just have the updated, (un)DelphiX code — packaged along with Hori’s original copyright notice, and the requirement that Hori’s original archive be distributed unmodified. So obviously Hori never gave permission for the new version. But it doesn’t look like he’s complained, either.

I’ve decided that, since I’m not the one distributing it, I’m in kind of a light-gray area. But I’m kind of figuring to eventually work my way toward my own implementation, independent of DelphiX. We’ll see how it goes.

A note on design-time components

The latest version of Delphi that I personally own is Delphi 3 or 5, I don’t remember which. It’s in a box somewhere in the basement. And I won’t use it anymore. I’m too spoiled by the recent features that made Delphi a reasonably competitive programming language again — things like strict private and records with methods.

So I’ll be developing with the free Turbo Delphi Explorer for Win32. It’s not quite the latest and greatest, but it’s only one version behind the expensive editions, and it’s got the good features.

What it doesn’t have is support for third-party design-time components. So I’ll be creating the DelphiX components in code, not with drag-and-drop. This raises a few interesting issues, since they weren’t designed to be used that way, but they’re easily worked around.

It also doesn’t have a command-line compiler, so expect me to grumble about that from time to time when I wish I could have a rakefile.

Every project needs a good name

And a good logo.

My main goal is to be able to develop tile-based RPGs, along the lines of Final Fantasy 1 through 6. And since it’s all about tiles, I’m naming the project “Tyler”.

Actually, I started on another, similar (but .NET-based) “Tyler” project back in 2003, so I’m really just recycling the name. And I also get to recycle the spiffy logo I made:

Tune in next time…

I’ll try to keep my game-programming posts from getting too long. This choice was inspired by Scott Guthrie, who posts absolutely amazing content — but he does not post articles, he posts chapters. It’s impossible to read one of his posts in a single sitting. So I’ll err on the side of multiple, shorter posts, so you can read a few at a time (and then mark them as read in your feed reader, so you know where you left off).

So, until next time, when I build a simple “Hello, world” program with (un)DelphiX, and then work around the Delphi bug that makes it not work quite right.

Want to see Delphi jobs on HiddenNetwork?

Saturday, January 12th, 2008

HiddenNetwork is a cool idea, but they’re not so great for Delphi programmers. Yet.

We’d like to change that.

Today, if you go to the job board and click on “Keywords [change]”, you’ll see that there’s no search keyword for Delphi. That’s because, well, so far they haven’t had anyone who wants to post a Delphi job. As far as I know, I’m the first person on DelphiFeeds to join HiddenNetwork; most of their exposure so far has been more along the lines of the Java and .NET crowds.

But I think DelphiFeeds is probably a force to be reckoned with. I’ve been talking to Alex at HiddenNetwork, and we’d like to give it a trial run, and see whether HiddenNetwork readers will respond to Delphi jobs.

So if you’re looking to hire Delphi developers, and willing to work with us to give this a try, drop me a line at delphijobs01@excastle.com, and we’ll put your Delphi job on HiddenNetwork for FREE.

HiddenNetwork job postings are usually active for a month. For this free deal, Alex said we’d probably go for a couple of weeks to see how it goes. If there’s a good response from readers, Delphi jobs may become a permanent part of HiddenNetwork. And if not, then hey, it’s free, you didn’t lose anything but a bit of time.

(Note that the above e-mail address is not my regular e-mail address, and will cease to be active a few weeks from now, when I get sick of all the inevitable spambot spam and quit checking it anymore. So this is, in a sense, a limited-time offer.)

HiddenNetwork: a cool idea in job searching

Saturday, January 12th, 2008

Of the four tech jobs I’ve had, three came along when I wasn’t actively looking. They came through people I knew, who happened to say, “By the way…”

That’s why HiddenNetwork is such a cool idea. And that’s why I’ve started putting their job ads on my blog. I’m now one of over 50 blogs in the HiddenNetwork.

Widen the field: people who aren’t actively looking

Suppose your department is looking to hire a programmer. You could post a job on a job-search site, detailing the position and the requirements. Who will see it? People who are actively looking for jobs. Meaning, most of the time, not people like me.

But suppose you post your job opening on HiddenNetwork. Now who will see it? People who aren’t actively looking. The kind of people who stop to chat with an old friend who says, “By the way, we’re looking for a great programmer. Are you interested?” You’ve just opened up your audience.

Narrow the field: passionate people

And there’s another perk. Let’s say you post a job in the newsgroups, or on a job board. What happens? You’ll get a few good applicants (maybe) and a load of bad ones. These are people who are motivated to find a job, but some of them are a little too motivated. You’ll get people who obviously didn’t read the job description and have none of the qualifications.

But by posting jobs on tech blogs, you’re not only widening the field in one direction, you’re also narrowing it in another. Now you’re only showing your jobs to the kind of people who read tech blogs… the kind of people who keep up on trends and keep their skills current. The kind of people you want to hire.

Commutability

When HiddenNetwork decides which job ads to show to any given visitor, they give preference to jobs within “commuting distance” of the visitor’s location, which they define as 60 miles. They also don’t bother showing jobs from other countries (although you can search for them by looking at the job board — see the “browse more jobs” link, in the yellow box in the sidebar).

That’s kind of a “so what?” if you live in Omaha, because HiddenNetwork doesn’t have any jobs for Omaha (last I looked, anyway), so you just see a random sampling of all the postings. But if you live in a major-enough metro area, you’d see local jobs more often than non-local.

Special offer for employers outside the U.S. and Canada

They’re running a January promotion. If you’re outside the U.S. and Canada, you can post jobs for FREE. Just use discount code INTLFREE7.

Go check out their site

The HiddenNetwork site has some good information, including a full list of member blogs and several testimonials. Whether you’re looking to hire, looking for a job, or not exactly looking right now, go check them out — or just keep half an eye on my sidebar to see if there’s anything good.

Why design-time components shouldn’t have install EXEs

Sunday, January 6th, 2008

Lots of Delphi components and .NET controls ship as EXEs that “install” the design-time component for you. I hate that, and here’s why.

  1. Revision-control unfriendly. I don’t want you sticking code under C:\Program Files. It’s code. It needs to be in source control with all my other code. That’s kind of the point of revision control. I want a ZIP that I can extract into my source tree and check in, so that later I can go back to that same point in time and rebuild my code as it was then.

    Xceed is the worst offender here. Good WinForms controls, but you can’t put them in Subversion — they have to be “installed” if you’re even going to be able to compile. You have to actually run their stupid installer on every development computer, including the automated build machines. I cannot express how deeply this offends me.

  2. Delphi-version unfriendly. If Delphi 2007 comes out and there’s no installer for FooComponentLibrary for Delphi 2007, what do you do? Sure, you can make a new package, compile it, and install. But you could do the same thing if you had a source ZIP, and you wouldn’t have to sit through an installer that might or might not work correctly if its intended version of Delphi isn’t installed.

    Of course, if it’s a Delphi component that ships without source code, then you’re screwed anyway, since DCUs aren’t compatible across Delphi versions. But no sane person would ever use a Delphi component that shipped without source code, mainly for this very reason. (Not an issue for WinForms controls, as compiled DLLs are much more version-agnostic than Delphi DCUs.)

  3. What about Turbo? The last Delphi version that I personally own is either 3 or 5, I don’t remember which. There’s no way I’ll use it anymore. I’ve been spoiled by strict private and records with methods. When I’m tinkering at home, I use Turbo Delphi Explorer, ’cause it’s free, and sort of recent. But it doesn’t support third-party design-time components. And it doesn’t have a command-line compiler. So what’s going to happen when the installer gets to the “compile and install the package” step? It wouldn’t surprise me if the installer failed and rolled back, leaving me back at square one. I try not to find out.

Component installers are probably meant to make life easier for hobbyists and beginning Delphi developers, but they make life harder for experienced developers — sometimes much harder. So component authors, please, provide a ZIP file. (And please, make it an actual, industry-standard ZIP, not a fringe format like RAR.) For Delphi components, the ZIP should contain the source code. For WinForms components, it could have either the source code, or the compiled DLLs. And in either case, of course you’re including sample code, right?

You can provide an installer too, if you must. But it should be a separate download.

(This rant was provoked by (un)DelphiX, which does provide both an installer and a source RAR (grr), but states fairly prominently that the source archive will be discontinued at some point in the future.)

Installing Subversion 1.4 as a Windows service

Wednesday, January 2nd, 2008

I just upgraded the command-line Subversion on my home laptop. The way you install Subversion as a service has changed since I wrote the Mere-Moments Guide to installing a Subversion server on Windows, so it’s time for me to blog the details (so I can find them if I need them again).

If you had an older SVN service

First, if you used to have Subversion pre-1.4 running as a service (either via the SVN 1-Click Setup installer, or by following the steps in the Mere-Moments Guide), you’ll need to uninstall it:

  • Open a command prompt.
  • Change into your Subversion bin directory (probably C:\Program Files\Subversion\bin).
  • Type svnservice -remove
  • It should report that it’s stopping (may take a while) and uninstalling the service.
  • Delete svnservice.exe.
  • Uninstall your old version of Subversion (may not be necessary, but I did).

Installing the new Subversion service

Starting with Subversion 1.4, there’s built-in support for running as a Windows service — but not for actually creating a Windows service.

I recommend downloading the Subversion installer directly from the Subversion downloads page on tigris.

Then, go read Configuring svnserve to Run as a Windows Service.

If you have Windows 2000

The above instructions should work on Windows XP and later. But if you have Windows 2000, you need to download sc.exe from Microsoft’s Resource Kit.

I did the research so you don’t have to. Here’s Microsoft’s download for sc.exe for Windows 2000.


Joe White's Blog copyright © 2004-2011. Portions of the site layout use Yahoo! YUI Reset, Fonts, and Grids.
Proudly powered by WordPress. Entries (RSS) and Comments (RSS). Privacy policy