Home > aen's Blog

Humble Beginnings

Build 1 of SMEE, hot off the box!

smee, build 1

Here are a few screenshots that will doubtless drive you mad with desire:

The first thing I did was set up the Main class. I created a JFrame and got it displaying, centered on the screen.

Reading about unit testing has helped me realize that you want constant tiny steps that you can get active feedback on. Never spend hours and hours slaving away on heaps of code without testing each little addition as you go. In the past this was almost always my tack, as my ego was large enough to assume I could write perfect code. After all, the pile of trivialities supporting my grandiose scheme couldn't cause that much trouble. Right?

Wrong! I can say that after many countless hours spent scratching my head on countless occassions, this is one of the worst things you can do when designing any piece of software, be it a throwaway prototype or a large commercial product.

Many frustrating bugs that had me chasing my tail often ended up to be very simple omissions at a very "low level" of the framework. I'm not talking assembler code here, but rather the wealth of code tidbits that serve as your foundation — elements of your susbsystems, such as those for parsing confguration files, loading tile sets, common transformations on various data, etc. Had I tested frequently, I would've quickly nipped these sort of trivial issues in the bud.

Constant feedback keeps your finger on the pulse of your progeny. Plus it's a much more satisfying experience and can keep your motivation high. Barreling through hundreds of lines of code, only to run your program at the end and expect everything to magically work is a dubious practice at best.

Complex systems are made up of solutions to smaller problems, so always pick a small problem and tackle it. In the articles by Noel Llopis which I mentioned yesterday, he states that you should be able to manage a single problem in no more than 15 minutes, with most problems taking up only a couple minutes of your time. I think that's a really great guideline. If it's taking longer than 15 minutes for you to get feedback, you need to rethink your strategy and break down your problem into smaller pieces. Code, test, code, test. Do it like it's going out of style!

For SMEE, the next thing I wanted to do was display a map. Well, I need have a map class and a tileset class, and the tileset needs to be associated with the map, and I need to be able to scroll around the map and stuff, hmm, and it would probably be easy to save the map as a byte array since I'd only have to use one simple line of code to load and save it! Yeah! Awesome. You're so clever, me. And this and this and this. STOP.

That's an explosion of thought that happens within a matter of seconds and is part of the reason why in the past I would crank out hundreds of lines of code before actually running my program. Don't get bogged down in all this minutia prematurely.

There is a term in software development known as "class explosion," which is what you get when the programmer tries to model "too much" or tries to take into account more factors than are practical when designing a piece of software. This was rampant when OO first came on the scene. "Object orientation? Oh my god, it's the best thing since sliced cheese! Sliced cheese is an object, you know..."

In much the same way, I often experience "idea explosion." The trick is to reign yourself in and not immediately try to implement every one of these ideas. if you don't want to forget them, scratch them down on a pad somewhere. But do one thing at a time. You'll get to all that other stuff eventually. No need to rush. You can still get there pretty damn fast if you test as you go too!

Back to SMEE, I wanted to display a map. I create a Map class, add some dimensions, and then a grid for all the tiles. To display the map, I throw together a MapPanel class that is a GUI component in the form of a JPanel. I override the paint() method to draw the map.

But I don't have any tiles to draw! Well, loading tiles is a bigger fish that I'll fry later, so for now I just display a colored rectangle and some text in it. That gets the point across. Then I wire my MapPanel into my JFrame by setting it as the content pane.

Then I fire the sucker up! Only took a few minutes. And I'm rewarded with my centered frame and a small grid of tiles that would make DaVinci weep. For joy, of course. Programmer art is a rare and beautiful thing. Treasure it.

What about some actual tiles now! I create a Tileset class that just has an array of BufferedImage objects in it. I still don't want to bother creating a tile graphic and then loading it, so I just create a tile image programatically and stuff it into the tile set by default when it is created.

I generate the image with a slight variation on the rendering code that I was using in MapPanel for drawing my map, but now it's in a formal image object. Whee! I make it a slightly different color to help diagnose that my new code is being used when I test it again.

I contain a Tileset in my Map class and then tweak MapPanel to look up an image to use from my tileset instead of doing all the drawing manually in the loop. Then I run everything again. Only another couple minutes were occupied, and I can see my new fancy shmancy image-ified map renderification! Saw wheat.

Now that I have image tiles, I want to be able to take some user input and plot tiles. I've only got one awesome computer-generated tile though! So I add another dummy one to my Tileset object that gets generated when a new tile set is created. That way I can plot a different tile than I'm using to display my grid with currently.

I can get user input with a MouseListener so implement the listener on MapPanel and stub out the methods the interface requires. mousePressed() is a good place to start. I can get the mouse coordinates in pixels from the MouseEvent objects, but I want to plot a tile in my map and that requires tile coordinates. My tiles are 32x32 pixels, so I divide the mouse X and Y coordinates by 32 to get my tile coordinates. Now I can use those to plot my tile! This consists of passing them into my map's set method, which takes a pair of coordinates and a tile to use. I use my second fabulous computer-generated tile for the job.

Now I take it all for a spin again. My map shows up, and with baited breath I hover over the grid with my mouse and then click the mouse button! Wait for it... wait for it... *click*... *click*... *click* *click*... wtf m8! Where are my bleedin' plots! Festering pus, and also ooze!

In frustration I grab my window and drag it like crazy around the screen, in a spastic fit of fury. When what to my wondering eyes do appear, but the tiles I had plotted... though the timing was queer. I quickly realize I had forgotten to repaint() after making the update to my map. The map is just data, the "model," as it is often called, and my GUI had no idea it needed to refresh after I modified it.

This is one of those tiny little omissions I mentioned earlier that can drive you crazy if you forget it initially and don't get around to testing until much later. It's much easier to diagnose at this stage in the game, even if you're not a spastic window dragger.

I toss in the missing repaint() and run my app again. This time I am rewarded with sweet precious plotty goodness! And it still only took me a few minutes to get here, even though I ran into some trouble. The problem was small because the amount of code I added was small! Not always true later in the game, which is what unit testing helps us combat, but the concept is a valuable one.

Now that I can plot a mosaic masterpiece of a map, I want to be able to save my results to disk! I'll need a file format to store my map dimensions and my grid of tiles. After giving it a little thought, I decide to go with a simple text file. It's easy to read and edit by hand if I want to make silly little tweaks while experimenting.

I flesh out a quick litttle stub of a save() method that just writes the text "Smee" to disk. I don't need to write out the entire thing yet, I just want to verify my file saving code is working.

I need to be able to trigger the saving code somewhere, so it's high time I crank out a little menu with a Save menu item. I add a simple little JMenuBar with a JMenu which I title File and a menu item I title Save. I hook it to my JFrame with the setJMenuBar() method and run my app again.

I'm rewarded with my menu at the top of my frame, and I can click on File and see my Save option. Clicking on it doesn't seem to be very productive right now though! I need to wire in some code that'll call my map's new save() method when I click on Save!

To do this, I use the save menu item's addActionListener() method to add a quick little temporary inner class that uses the requisite ActionListener interface and performs my desired action in its actionPerformed() method. I stuff this inside: map.save("smee.map");

I just hard code the saved filenamed to smee.map because I don't want to bother trying to set up a file picker dialog. I just want something that works. Now that my action is all wired up, I run things again. This time when I click on Save I'm rewarded with a smee.map file that gets dumped to disk! Upon opening it up I see the text "Smee!" inside. Most excellent.

Now that I've verified my file saving code is working. I flesh out my save() method with actual map savery code. I dump out a file type signature, a version, the map dimensions and my grid of tiles, which are just numbers. 0 represents my first computer-generated tile, and 1 represents the second one. I want to write my tile set to disk too, but that's another whole saver! I'll do that after I get this map saver squared away.

I run things a couple times after small tweaks to work the kinks out, and am finally rewarded with a smee.map file that looks something like this:

SMEEMAP
1
10
10
1 1 1 0 0 0 0 1 1 1 
1 0 0 0 1 1 0 0 0 1
1 0 0 0 1 1 0 1 0 1
0 0 1 0 1 1 0 0 1 0
0 1 0 0 1 1 0 1 0 0
0 0 1 0 1 1 0 0 1 0
0 1 0 0 1 1 0 0 0 0
1 0 0 1 1 1 1 0 0 1
1 0 0 0 1 1 0 0 0 1
1 1 1 0 0 0 0 1 1 1

Incredible! I can now impress all my friends with my incredible text-file-map-to-disk-writery skills. They're in high demand, and I can meet that demand with the best of them. The best I say!

Before I get to my tile set saver, I write up a load() method using the same tiny little steps. Pretty soon I'm up and running with a genuine map loader. It's one layer, sure, but it works! Oh how it works.

Now I'm ready for some tile set saving action. Each map will have an associated tile set, so I'll want to refer to the tile set my map uses in my map file. I modify my map saver to dump out a tileset filename afer my map dimensions. I test to verify.

To save my tiles to disk, one of the easiest ways would be to just save a single image file for each tile. No need to write some binary format of my own, I can just leverage well-known formats like GIF and PNG. People can edit those directly with whatever tools they prefer, after all.

This also allows me to use the standard Java javax.imageio package to splat out some images to disk. The ImageIO.write() method takes an image object as an argument, so I can just pass my tiles to it directly. Easy peasy.

But aren't my tiles just going to end up as a bunch of images floating around on the disk? How do I know they're all a part of one tile set? I want to be able to have multiple tile sets without everything being all mixed together.

Well, in addition to saving out the individual images for each tile, I'll be generating another text-ish file format for my tile set which lists the filenames of the images to use for each tile. My map file refers to a tile set file, so I can just as easily refer to images n my tile set file. Yet again I'll be able to tweak it by hand really easily in a text editor. I could add new tiles to my tile set just by creating an image in the paint program of my choice and then adding its filename to my tile set list.

I flesh out my tile set saver as described, and then run things a few times to iron out the wrinkles. After a couple minutes I end up with a smee.map.set file that contains the following:

SMEESET
1
2
tile0.png
tile1.png

SMEESET is the file type signature, 1 is the version, 2 is the number of tiles in my tile set, and tile0.png and tile1.png are my tiles. Stupendous!

I load up tile0.png in my favorite image editor just to bask in the warm glow of my computer-generated tile. I ever so gently add the single pixel that brings this masterpiece of craftmanship to a digital climax. Then I save it!

I make small incremental steps writing a map loader, tile set loader, and a Load menu item to call them from. Eventually I am able to launch my app with a blank map and hit Load, after which one of my previously saved maps with a couple choice plots is displayed! And what's this! My tile0.png has that extra pixel of love!

Now that I have the core functionality needed to call my baby a fledgling map editor, I just have a little fun for a while. I poke and prod a few things, add a little error dialog here, a little helper method there. Just some gravy for the road.

Thus was SMEE conceived! Nothing to it, as you can see! Just five small Java files and a whole lotta TLC. Take a look inside and add a few things. It's fun for the whole family.

My next post will start making SMEE a little more useful. Being able to plot tiles by dragging the mouse would be nice, as well as an undo history. And would it kill me to have a tile selector? Certainly not! We'll get it all sorted out. Creaminess will not be denied!

All this and more, coming your way Saturday!