As many people know, I've been spending a lot of time over the past year or so on my own personal isometric rendering engine. What started out as a small undertaking to replace functionality I lost from another platform became a grand vision for a new social networking platform on which to build my own little enterprise of games: IsoKit. The more I worked on it and showed it to people, the more they began telling me that I needed to license out the platform. That is now my goal; to create a true multiplayer isometric world-building platform that is fast and easy to use.
It was not my intent in the beginning to create a full featured social package; rather I desired a replacement for the former virtual world platform I had been doing a lot of my development on, Metaplace.
Metaplace was a platform created in 2007 intended to allow anyone the chance to create their own virtual world. While it enabled users to create multiplayer environments in overhead or side-view 2D, the true beauty of the platform was in its isometric, or 2.5D, overhead view. Users had the option of using either stepped tiles (flat platforms you could raise and lower) or heightmapped terrain. It was this second option where the platform really shined; many virtual worlds in Metaplace featured pseudo-3D rolling hills your avatar could walk up and down.
I began beta testing the Metaplace toolset in 2008. Over the next 18 months or so, I became intimately familiar with the tools and inner workings of the platform, developing several highly popular modules, or plug-ins, that people could use in their own worlds. It was my goal to learn the tools, and make money selling plug-ins to other users of the service, and for a while I thought I was going to succeed.
An Unfortunate Turn of Events
On January 1, 2010, MetaPlace closed the doors on its UGC service, and I lost access to a fantastic virtual world platform and almost two years of hard work. While Metaplace had state-of-the-art technology, it could not gain enough traction as a viable business model.
In frustration and maybe a bit of self-pity, I closed the doors on my game design goals for the next two months. I knew that I would have to start from scratch; there was no platform out there to rival what I had lost, and it just seemed like too big of an undertaking to put all my energy into learning and creating a new system.
Finally, after my self-imposed moratorium on development, I decided that I needed to either start developing again, or hang up my game design hat and find a desk job.
Finding An Engine
Up until this point, I had only dabbled in flash games, and hadn't done any mainstream flash development. I had acquired a good understanding of general game design and coding principles, however, and decided that flash was the way to go. Since I didn't want to start completely from scratch, I did a preliminary search for isometric game engines. The lack of a quality renderer surprised me; I was able to find engines like as3isolib that could handle flat terrain and isometric sorting, but nothing that could handle heightmapped terrain.
Understanding that I would have to recreate the functionality I desired for my games, I abandoned the search for an isometric renderer and focused on general game engines. Finally, I came across the PushButton Engine, an MIT licensed, component-based framework for making flash games. Having used the Unity3D engine and being familiar with component-based architecture, I found it to be exactly what I was looking for.
Down The Rendering Rabbit Hole
I come from a design background. I never really desired to get into the nitty-gritty world of rendering and performance optimization, so I knew this would be a challenge. Reading through a myriad of tutorials and as3 documentation, I began writing what would eventually become the foundation for IsoKit.
Since PBE had a Rendering2D (R2D) library, my first thought was to extend that. Everything in R2D was based off of the DisplayObjectRenderer class, so I decided that each isometric tile could be one of these DisplayObjectRenderers. I rigged up a tilemap class that handled the positioning and sorting of these tiles, and tried it out.
Success! I was able to get a sprite seated on a flat isometric map. Moving the sprite around, I checked the resource monitor.
FPS: 60 - Not too shabby.
Memory Usage: 150MB - Eep.
A 50x50 tilemap was taking up 150MB of memory?! I was hoping for 500x500 sized worlds! Out of curiosity, I loaded up a 500x500 tilemap and...promptly crashed my browser. I decided this would have to be passable for the time being and proceeded to work on my heightmapping.
Using a series of Graphics.drawTriangles() calls and adding to the math I borrowed from as3isolib, I was able to tweak the isometric corners to start forming height fields. Setting the height on a tile set its North corner to that height. The West corner was set to the height of the tile to the left of it, and likewise the East corner was set to the height of the tile to the right of it. Finally, the South corner was set to the height of the tile below it. In this way, the tile vertices all met and created an (almost) seamless flow of terrain from a heightmap, which I produced using a Perlin noise algorithm.
I ran the resource monitor again:
FPS: 45 - Shoot.
Memory Usage: 151MB - Not much change here.
Performance was really starting to stutter. While 45FPS is an acceptable rate for your basic flash game, this was just to render the terrain. On a Core i7 workstation. I knew something had to change. Still going with R2D, I decided to bite the bullet and write my own custom class instead of extending the default DisplayObjectScene. Instead of having a series of displayObjectRenderers, I created a dirty rendering system using an array of displayObjects that had the tiles drawn to them, and then just had the tiles within the dirtied areas drawn to the screen. Running the resource monitor again, I was quite pleased with the results:
FPS: Back to a full 60 (up to 120 if I ran it that high)
Memory Usage: 80MB
Woot! Performance back to where I wanted it, and cut memory usage in half. I was back in business. I even attempted my 500x500 tilemap test again. It took 20 seconds to load, and used 750MB of memory... but it loaded. I'd have to tackle that later. Since I was fairly happy with the rendering at this point, I turned to sorting. My main sprite would appear over top of hills that should have been drawn over top of it, and would keep blinking into and out of existence. Some quick poking around in the #PBE chat room landed me a nice response from one Ben Garney: use a point cloud. Sorting first by screenspace y coordinate and then by x coordinate, my sprite finally could roam the hills sorted properly. Things were coming along nicely.
Since I was happy with where the rendering was at, I decided to start adding the features I missed from Metaplace, starting with terrain decals. Terrain decals are sprites that conform to the terrain, allowing for neat effects that would be otherwise near impossible to do with tiles. After spending a couple of nights creating a decent decal renderer to sit overtop of the tilemap, I ran the resource monitor again:
Memory Usage: 85MB
ONE terrain decal dropped performance down to less than half what it was, and my engine needed to support dozens! I knew that my renderer needed an overhaul if I was ever going to get the engine I wanted to make my games with.
Back to the Drawing Board. IsoKit, Version ...3?
Gritting my teeth, I decided to do away with PBE's rendering2D library completely and start from scratch on my own custom component set. I ditched the as3isolib functions as well, since at this time I was intimately familiar with how isometric tiles were to be rendered, and was able to come up with my own methods. This was quite a painful process as it was my third complete rewrite of the code. I ditched the array of displayObjects that stored the tile graphic data, and opted instead to generate them on the fly as this would conserve memory. I created an entirely different dirty rectangle system, opting for a spatial grid that allowed greater freedom in heightmap dimensions. After refactoring the terrain decal system and feeling bold, I decided to try a stress test of ONE MILLION TILES.
And the results:
Memory Usage: 42MB
That's right, a 1000x1000 tilemap ran at full speed and used only 42MB of memory. I knew my engine was almost ready for primetime.
Since that milestone, I've been improving IsoKit, adding realtime terrain deformations, basic 3D terrain physics, and planning new features such as lighting and radial sound. There is still plenty I would like to do with it, but I am hoping to release an alpha here soon.
I'm quite excited about the final product. Using ZaaLabs' Eden tooling platform, I intend to make a world editor, and with Player.IO server integration, allow uploading and downloading of game worlds, as well as realtime multiplayer interaction. isoKit will become a true social/casual games platform, allowing anything to be created from virtual worlds to isometric platformers.
I am actively seeking sponsorship, as I would like to be able to start working on this full time. Development right now is slow, bit I feel the potential for this product is huge, and given the time and resources I can make it great. Either way, though, I plan on releasing this as soon as I am able, and then making some great games with it!
For a pre-alpha demo of IsoKit in its current form, try the demo here.
If you're interested in this project, please don't hesitate to contact me.