With this one I wanted to try my hand at a very popular game genre at the moment, match-three. Because this is a rather simple style of game I thought it would be really easy and fast to make. Wrong. I spent hours without touching a keyboard trying to come up with some novel twist on the genre.
I toyed around with a gem dropping mechanic but that didn't work so well, then I thought about how someone interacts with match-three games on mobile, by dragging the icons around. So I decided to base my game play on dragging gems around until they make a match.
I originally wanted a "mindless" match-three game like Montezuma, one where you play it as fast as you can without thinking too much about the moves. As I started developing the game play for this one however it seemed to fit a puzzle like game better so that's what I ended up with.
Anyways give it a go and let me know what you think:
This one took a little less time at the others at 31hours over 7 days, as per the other two I recorded my time spent:
17th April 11:00 - 12:00 15:00-18:00
20th April 10:00-12:00
21st April 13:00-17:00
22nd April 11:00-13:00 14:00-16:00 20:00-21:00
23rd April 11:00-16:00
24th April 11:00-16:00 18:00-21:00
25th April 11:00-12:00
Well that's the end of my challenge! Im surprised I managed to do it. A little over a week per game seems like hardly any time at all but having that time restriction really is good as it helps to limit your scope, preventing the game from ballooning out into something that could otherwise have taken months and still not have been any fun to play.
As for the tools I used. I used Richard Lord's excellent Ash Game Framework to develop them all alongside Starling with Feathers on top of Flash for the rendering. For rapidly prototyping games they have been excellent. Now im more familiar with Ash I may write a post or two about how to go about structuring certain parts of a game in an enitity-component like manner.
For this one I decided on a type of game that I have been wanting to make for a while, a turn based strategy game. Many moons ago I used to play a J2ME (shudder) game called Ancient Empires. It was a great little game with lots of depth and replayabliltiy and I think it deserved a remake of sorts.
This game was far more complex to create than Latesha's Crib and hence I spent about 43 hours over the last 9 days making this one.
I was able to save some time by reusing parts of the rendering and hopping logic from Latesha's Crib. As usual I used the Ash Framework with Adobe Starling and Feathers. To code the AI for the computer player I used Tim Conkling's Excellent Godmode Behaviour Trees library which is truly an excellent AS3 library. The way Behaviour Trees work is really cool, I strongly recommend checking them out if you are going to design any sort of AI.
Special thanks go to Mike Rigby for helping with play testing and Ian Callaghan for suggesting I check out Behaviour Trees :)
Let me know what you think of it, if people are feeling it then I may turn it into a full-blown mobile game!
Again I recorded the my time spent making this one, heres the log:
4th April 9:30am - 2pm 3pm - 7pm
5th April 9:30am - 10:30am
6th April 12:00pm - 4pm (-1 for josh, -1 for lunch & spanish)
7th April 2pm - 19:30pm
8th April 1pm - 3:30pm (-1 spanish and lunch) 4pm - 6pm
For this one I started off with an idea that I wanted to make a time-based puzzle game. Well it didn't turn out that way, also somewhere along the way I discovered an Ebonics Translator so I decided I would incorporate that for a giggle.
Well as announced in my last post im going to be spending most of this year travelling around various parts of the world. I'm leaving on the 7th on May so I have about a month left before I go so I have been thinking on what small project I can do in the meantime to keep me out of mischief.
I had a great idea for a game which I may write about in a later post but decided that perhaps now may not be the time to make it. So instead what I have decided to do is create a number of prototype games, three to be precise then let you guys decide which one I should take further.
The rules are the games all have to be designed as if they are going to work on mobile so that means only touch based controls. Ill be using Adobe AIR with Ash for all these prototypes so ill make sure that they all run in the browser with PC based controls too.
Im also going to get myself another ground rule. I can only use the free Lost Garden Planet Cute assets.
I hope that this limitation will mean I don't spend too much time worrying about the tech and how it looks and instead concentrate on what has traditionally been tough for me, making it fun!
Ill be updating regularly on my progress and then throw a vote out at the end of it and see which one people like the most, wish me luck!
Just before leaving Playdemic we had a company-wide ship-it-day. If you haven't done one of these before they are great and work very similar to game jams like Ludum Dare. Basically you have 24 hours to create something related to the company then you have to present it.
I decided to team up with Laura Whyte a rather excellent artist colleague I have have worked with for years. I knew I wanted to make a quick mobile game using Adobe AIR and Richard Lord's Ash framework but I didn't really have any idea of what game specifically I wanted to make. She had the brilliant idea of making a clone of the classic "helicopter" game that you control with your voice.
So that's what we made. Check out a video of my playing it below:
You will have to excuse the fact I do sound a little like a drunk owl at some points in there.
By the end of the 8-hour day we pretty much had a working game. Since then I have spent a few more evenings here and there tidying up the source code and fixing a few bugs and submitting a build to Apple and Google. In total I would say that 24-hours is a fair time frame for the development of the game.
As previously mentioned we developed the game using Adobe AIR and Ash. The reason being was that I had previously experimented with cross-platform game development with Mr Nibbles which was coded in Haxe and NME and I was keen to see how AIR would compare.
Im pleased to say it held up very well. Where I had a little difficulty was with the Ash Framework. Its a great framework however its still rather new and there aren't many examples out there for how to do certain parts of the game development such as menus and event handling. For this reason I have decided to open source the game so that others can have a look at how I went about constructing parts of the game in Ash.
I plan on refining the source as I have discussions with others on the Ash Mailing List about how best to go about implementing some of the features, so I suspect that source code may change over the coming weeks.
Because the game is cross-platform that means that it will be accessible on the Apple App Store and Google Play store as soon as it gets approved, for now however you can play the web version over at: http://mikecann.co.uk/projects/ectoplasm/
You may want to toggle your microphone settings (or disable the microphone entirely ;))
Well its been a couple of weeks since we launched on iOS and a few weeks since Android, so I have decided to write one more post on the Mr Nibbles project. A post mortem into what went right, what went not so right which should hopefully serve to solidify my learning from the process and perhaps help others who should embark on something similar.
Before I dive into that, first some statistics thus far..
Statistics
Android
We have had 2,867 User installs and 1,623 active device installs. Not bad :)
iOS
3,789 iOS installs, making a grand total of 6,656 mobile installs, not bad :)
The Process
So from the beginning the this project was to be a quick turn around project. The idea was to try and complete the whole thing in three weeks. It actually ended up being more like 5 weeks for me and 4 weeks for Moh the artist (he was on holiday for one of those) but still not months of development which I wanted to stay clear of.
For me, short time-scale games are so much more fun to make than long games. In a short time you can iterate quickly over the core idea then get it out there quickly and see what people think. When time constrained you are are forced to only implement the core functionality and discard the rest. In all likelihood the other stuff is probably just fluff anyways and not really necessary to the game.
As for the time-overrun, there is no one main reason for it. Lots of small things such as; unfamiliarity with NME / iOS / Android development, windows / osx development workflow and testing taking longer than expected. Making and testing each of the 30 levels took alot more time that I had anticipated. Working around some small NME issues (more on that below) took some time. Writing these blog posts every week (sometimes more) took some time out of what could have been development time too (more on blogging below).
The hours we worked on the project were actually okay. Because this was a personal project and both of us have full time jobs at Playdemic we could only work on the game during our evenings and weekends. During week days we tended to start about 6-6:30pm then work until we were kicked out of the office by the security guard. 8:30-ish usually. Occasionally (mostly towards the end of the project) we stayed a little later to get stuff done before release.
This sort of schedule and working pattern suited us both. Being able to see who you are working with and talking face to face I think really adds to the experience and fun of making an indie game. Its definitely more exiting to run over and show the other person some cool new level on your iPad rather than try to describe it over MSN from your bedroom. Working like this obviously wouldn't have been possible if the management at Playdemic weren't so awesome by allowing us to use the office after normal office hours so definitely a massive thanks there.
Blogging
I strongly feel that blogging everything is one part of the process I definitely wouldn't change. Sometimes I choose to work in private on some projects however this one I decided to be very public, announcing upfront the intent and regularly updating on progress. Blogging regularly and getting feedback on progress really encouraged me to stick with it every singe evening. It also served as a way to keep the project fun (absolutely critical for me) as the immediate feedback from people would serve as a basis for ideas and improvements.
Also writing things down has been proven to encourage stronger learning and memory retention, so hopefully in the future I wont make the same mistakes ;)
Haxe + NME
I have been using Haxe for over a year now for various personal and professional projects so I was fairly familiar with the language going into this project. Haxe is a good language, similar to AS3 / ECMA based langues and so it shouldn't feel too alien to a seasoned flash developer. It does however come loaded with many improvements over AS3 such as proper generics, type inference, macros and many more awesome features.
NME is a library built ontop of Haxe. It provides a familiar flash api but is able to many platforms natively. Using exactly the same Haxe code base I was able to compile Mr Nibbles to Flash, iOS, Android, Blackberry, Windows and OSX thanks to NME. The library really is great for this sort of fast-turn-around game. Mr Nibbles certainty wouldn't have been possible without it (not in 5-weeks anyways).
The NME developers have put a lot of time into making the build and certification process as easy as possible for the various platforms. So with "nme setup android" from the command line for example you are able to download and setup the android SDKs and get your certs setup in the correct place for building. Its really great to see that sort of dedication to making the cross platform compilation process as smooth as possible.
The performance of NME is truly great. Thanks to its batched sprite rendering there are very few draw calls and expensive texture-switching. Mr Nibbles runs at solid frame rates on pretty much every smartphone you can think of from the Android G1to the Galaxy S III to the iPhone 3G to the iPad3. After having worked with the nightmare that is J2ME mobile games in the past, being able to run your game on so many devices without any code changes really is a pleasure.
NME isn't perfect however and does have a few issues such as slow and imperfect vector rendering from SWF files. It is however constantly constantly being worked on and im sure it wont be long before the issues are ironed out.
Because both NME, Haxe and most of the third-party libraries are open source you are always free to dive in and fix any issues yourself. For Mr Nibbles, the SWF rendering was "good enough", such that we were able to make nice looking menu screens that dynamically scaled to any device resolution and pixel density (a real time saver!). Most of the time when I ran into the issue I would post it on the NME mailing list. More often than not it would be answered (often by the NME-super-hero Joshua Granick) within an hour or less!
iOS
iOS was definitely the primary platform we were developing Mr Nibbles for. Mostly because it was the platform I owned a mobile and tablet device for ;) To develop for iOS you need to use osx and xcode, so I took my MBP into the office and hooked it up via Ethernet to the network. I setup a shared folder on my desktop machine and the pointed xcode on my MBP to that shared folder. That way whenever I made a change on my windows computer I would only have to hit one button in OSX to rebuild and test on my iOS device. It was a real time saver over the other approach I was previously using, syncing over Dropbox.
There were are few frustrating moments towards the end of the project when I was running into issues regarding ArmV6 and ArmV7 when NME was compiling. Fortunately I noticed in the nmml spec for NME there was an option to compile to "FAT" which seemed to make the issue to away ;)
Android
Android was a lot easier to iterate and test because it could all be done from my desktop machine. In hindsight, and with the devices that only became available towards the end of the project, it would have been better if we had concentrated on the Android build first then ported to iOS later. Once we had some devices to test on however I was incredibly impressed with how easy it was to build and launch the game onto an Android device with NME. Literally just "nme test android" from the command line and it would compile and launch directly to the device, no faffing about.
Since launch there have been a few issues with audio crashing on Android. However I believe some people have been working on this so I may do a new build of Android in the near future and push an update of the game to the Google Play store.
Blackberry
The blackberry build has only been finished very recently, hence the lack of stats thus far. It was actually really easy to build for Blackberry, again just a matter of changing a single word on the command line "nme rebuild blackberry" and that was it.
The hardest part was testing it as I had no Blackberry device to test on. Joshua Granick was extremely kind and offered to test the game out on his device. Not only that, he put in a kind word and managed to get a Playbook shipped out to me so I could test too, thanks Joshua :)
Conclusion
Overall I had an absolute blast working on this game. Sure it was hard work, alot of evenings and weekend spent in the office till 9pm and sure it went over budget by two week but it doesnt matter because I was having fun the entire time.
This is what making games is all about for me, if you want to have fun programming I cant think of a better way than to make than to develop short-project games. Haxe with NME were instrumental in making the process as fun as possible by taking out much of the bull sh*t with cross-platform development, letting you get down to business of turning your ideas into reality. So massive thanks to everyone that has worked on developing Haxe and NME.
Finally I want to say a massive thanks to my colleague and artist partner on this project Moh who's time and hard work made Mr Nibbles come alive, nice one Moh!
Well it may have taken two weeks longer than my original plan but I can say that after five weeks of development the game is pretty much complete.
All 30 levels have been finished, all the menus are in, all the game play elements and artwork are in. The only thing outstanding really is some better sound effects which I plan to put together tomorrow.
So unlike in my previous development posts where I have provided a playable version of the game in the post I have decided instead to hold off till the game is 100% complete before uploading it. I have also grabbed www.mr-nibbles.com as a place for the web version of the game to live.
So a short post this one, this week will be about packaging the game together and submitting to the various stores.
Before I go tho here are the three stages in the game, showing off the awesome artwork of my colleague Moh:
Another week, another progress update. As usual you can check out the progress above (use the arrow keys to move and jump).
This week has mostly been about all the remaining "bits'n'bobs" that go into a game. That includes level progression, scoring, level and stage unlocking, persistence, settings, audio, pause and resuming, player death, fonts and a whole host of other small things.
Moh my artist partner on this project arrived back from his week holiday this week and since back he has made incredible progress getting all the menu screens and the majority of the graphics for the first stage of levels in. I really love the bright cartoony style he has gone for on the menus, the style really works on mobile too. Looks really professional for less than a weeks worth of work (only evenings dont forget).
I have also spent some time sourcing and putting in the rudiments of music and sound effects. The music theme for the game has been supplied by another colleague from Playdemic, Jake Rigby. Before Jake got into game programming he used to make music and audio effects for games (infact he provided some of the audio for my previous flash games) and he was very kind to provide me one of his tracks he made a while back for the project. The sound effects in the game are temporary at the moment, I hope to improve them this week :)
I have also been working hard on trying to get as many levels in as I can. As mentioned previously the idea is to have 30 levels split over three 'stages' each stage will have its own theme and general style of levels. So far I have made the first 10 levels in:
As mentioned in my last update I noticed that people were having trouble understanding how to play the game with the world rotating. To combat that I decided that the first few levels the world were going to be static, doing so should reduce some complexities with the control system. Well I have extended that "few levels" to now mean that the entirety of the first stage doesn't auto-rotate.
What I have done instead is to re purpose some tile types I had originally planned. You are introduced to the first of these new tile types on level 3 of the game. I think it brings that added sense of dimension to the game, making it more than "just a platformer". Play it above and see what I mean, or check out the video I have made of it working on the devices:
Incredibly the game actually runs at a very acceptable 30FPS on my iPhone 3G! Eat your heart out Adobe AIR!
Good level design really is a talent that I admire as I im absolutely terrible at it. It takes alot of time, patience and testing none of which I have in abundance. It is however critical to making this game fun to play so unfortunately I will be concentrating on level design the most of this week. I would love to hear about any feedback you have on the levels, whether they are too hard, too easy, whether the progression is right. It would be a massive help to me and is only something I can get a rough idea by testing myself, it needs others to play test it :)
With that in mind, onto time-scales. In my first post on this project I declared that I was going to finish this game within three weeks. I then extended it by a week due to my artist being out of action for a week. I am now going to extend it by one final week. I am starting to feel really proud of this little game and I think it would be a shame not to put one final weeks worth of polish in before calling it complete. So although I can no longer call it a "three week game" I can be proud to call it "my first mobile game" ;)
As usual you can have a play with the game in its current form above. (Up to jump, left and right to control the player).
The first new thing to notice are there are now menus, yey! Incredibly poor, made by a toddler, looking menus, but menus nonetheless. My artist and collaborator on this project, Moh, has been away for the last week so the game has had to suffer at the hands of my terrible programmer art. Now that he is back however, he will be whipping the visuals into shape.
Again this week I have had far less time to work on the project that I was hoping for (damn real life). With the little time I have afforded myself, I mostly concentrated on menus, resolutions, DPIs and aspect rations. These topics are the rather unglamorous side of mobile programming but are a necessary part of writing a game for multiple devices.
First a note on my testing environment and devices:
(I currently don't have access to an android device but I hope to test on it soon.)
As you can see there is a rather large variation in resolutions and Dots Per square Inch (DPI). There is also two different aspect ratio's there 4:3 for the iPad and 3:2 for iPhone. The iPhone 3GS is the same aspect ratio but half the resolution as the iPhone 4 and the iPhone 4S is the same aspect and DPI as the iPhone 4. The iPad 2 is half the resolution of the iPad 3 but has the same aspect ratio.
Basically all this equates into a few days of headaches while I try to wrap my brain around how to handle this mess. My solution to this conundrum is set my default target to the iPhone aspect ratio of 3:2 as it is more restrictive in the Y direction than the 4:3 ratio of the iPads. If I can build menus that work in 3:2 then they should look okay (if a little bit more spaced out) in 4:3.
To handle the various resolutions within an aspect ratio I then need to provide various versions of the assets that would roughly take up the same amount of screen space at that resolution. So for example if the screen is 1000px wide for example and the title for a menu is 800px wide when I design it on my PC, I would then need to provide a 1600px wide title image when running on a screen 2000px wide.
So the question is now how to provide these various sizes of my title? Well I could make a PNG for my title 800px wide then when I need one 1600px wide I just scale it up by two. The problem with this as anyone who has played around in Photoshop will know is that you will end up with a blurry up-scaled image. The other solution is to start at the other end with the highest quality 1600px then scale it down when you need one 800px wide, the problem with this is that you are wasting a whole load of memory by supplying and loading an image twice the size of whats required. The third method is to provide a different size for every single resolution required, this is not ideal as it would take alot of time to make all these PNGs and there are a great many device resolutions out there.
Haxe and NME however give me an alternative solution. Using the SWF library I am able to design my menus in Flash then at runtime pull out the vector graphic and scale it to the required size to display on screen. What this means is that I only need supply a single resolution of an asset in flash then it is scaled up as a vector and looks great no matter the resolution.
So to work out the correct size for the various parts of the menus initially I created a few "mock screens" in flash and laid out the various parts in the mocks:
By designing the mocks to 960x640 (3:2) I need only then at runtime divide the screen width by 960 to work out the correct scale for the game. Simples!
Well not quite as simples at that. There were a coupple of issues. Firstly the vector rendering performance of NME on mobile is pretty poor. To get around this problem you must pre-raster your vector to a Bitmap first. This should be fairly normal to anyone having worked with Blitting engines in AS3 before. This step however raised another issue.
I noticed it was taking a long time to raster the background for my menus to a bitmap. I did a few experiments and posted the question on the NME mailing list. It turns out that NME currently doesnt handle Radial gradients too well, also performance is generally poor when building in debug mode on iOS. With both this issues rectified however the performance was alot better. There is still a small lag between different menus when on iOS but its acceptable for now.
There also appears to be another issue regarding TextFields on iOS. It appears as if the text is being cut off half way. I have also posted about this issue on the mailing list and am awaiting a reply. My solution for now is to provide a background behind my text set to alpha 0, this forces the width and hight to be correct:
Well with that boring stuff out of the way onto the changes in the game itself.
As mentioned previously the gameplay changes have been fairly minimal. I have made some small changes to how the player is controlled to make it more fun. You can no longer scale vertical cliffs and jumping has been modified. The largest gameplay change has been to how the player is eased into the game.
After watching people play the game on the iPhone and iPad I noticed that alot of the time people didnt seem to understand how to control the player. They assumed the game worked like one of those "move a ball around a maze" type games. So understandably they kept turning the device on its side in an attempt to make the player "go up". With the world constantly rotating too, the person was trying to keep the world level, so they kept twisting the device in odd directions to keep it level. This problem is difficult to explain if you haven't tried it out on a device for yourself.
Once you understand whats going on however (the world is rotating, you tap to jump and tilt to move the player) it all becomes alot more clear and you can play the game normally. So with that in mind I have decided that the first few levels the world will not rotate. I hope this will ease a player into the control system a little better rather than just throwing them at a strange control system in a continually rotating world. Fingers crossed this will do the trick.
So that's that for this week. This week we hope to get as much of the art work in as possible. Hopefully menu systems, and the various bits of artwork for the stages will all go in this week. In addition I want to get audio and testing on Android in. Alot to cover in very little time!
I'm now two weeks into my original 3-week-deadline mobile game project. You can checkout the progress thus far by playing the game above ('up' to jump, 'left' and 'right' to control the player).
As you can see there has been some progress since my last update a week ago. I was hoping to have gotten a little further by this point but my personal life has been somewhat hectic the past week reducing the amount of game-development time to a few measly hours. In the few hours we have had, we have managed to get a general story / theme for the game and the rudiments of for the first 'stage'.
So the story is that our furry marsupial protagonist has ambitions of breaking free of his earth-bound cage to become the first hamster in space. The game is about his adventure to realise this dream.
The plan is to break the game levels in into 'stages' like other mobile puzzle games such as cut the rope or angry birds:
Each stage will have its own theme, giving the player a refreshed experience per stage. The first of these is going to be set within the cage itself so the tileset and background should hopefully represent that.
The bulk of my work this week however has been spent trying to get the game controls working just right. It needs to be fun to play, not too frustrating but not too easy. This is easier said than done. Im not sure its quite there yet, but play the game above and let me know what you think, im keen to hear your opinion.
During the last week I also ran into a few nasty issues with the physics which I thought I had nailed in the previous post. The problem was that the issue only occurs at low frame rates, this was really noticeable in the last post when I tried the game out on my iPhone 3G, the player was extremely jerky and unplayable.
To test the problem quickly (without needing to do an iPhone build every time) I used the handy "fps" attribute in the NME .nmml file. Setting the fps to 60 then having the player jump then setting the fps to 10 and having the player do the same jump there was a noticeable problem. On the 10fps build the player couldn't jump anywhere nearly as high as in the 60fps build.
I knew that the reason for this problem lay somewhere in my update function in my Player class. My usual method of running update loops is to work out the time between two frames (delta) in milliseconds, pass that into the update loop for a game object then have the game modulate its update based on that delta. So as a very simple example:
[codesyntax lang="actionscript3" lines="normal"]
class Player { public var position : Vector2; public var velocity : Vector2;
...
public function update(delta:Int) { position.x += velocity.x * delta; position.y += velocity.y * delta; }
...
}
[/codesyntax]
This is pretty standard stuff and should be familiar to any game programmer. In theory it shouldn't matter what frame rate the game is running at, the player movement should be consistent because the velocity is being modulated by the time between the previous frame and now.
The code for the Player class however is quite abit more complicated than the simple example given above and something in the logic was causing problems. I had my suspicions but no matter what I tweaked or changed I couldn't track it down.
I had however read of a different technique documented by deWiTTERS that involved a different way of writing your game loop that didnt take into account of frame delta but was still able to provide a consistent gameplay at differing framerates.
I wont detail the process here as dewitter explains it very well in his post. The result is a greatly simplified update loop for my Player by removing all the delta time factors. My resulting update loop now looks like:
[codesyntax lang="actionscript3" lines="normal"]
class Game { public static var TICKS_PER_SECOND : Float = 50; public static var SKIP_TICKS : Float = 1000 / TICKS_PER_SECOND; public static var MAX_FRAMESKIP : Float = 10;
...
private function onEnterFrame(e:Event):Void { var loops : Int = 0; while( Lib.getTimer() > nextFrameTime && loops < MAX_FRAMESKIP) { update(); nextFrameTime += SKIP_TICKS; loops++; } render(); }
private function update() { Ctrl.instance.update(); objects.update(); camera.update(); bg.update(); }
private function render() { tiles.render(); }
...
}
[/codesyntax]
The result of doing this is that the game now runs consistently at 60fps or 10fps. I haven't tried it out on my 3G yet but I have high hopes for the technique.
The moral of that story, if you bang your head against a problem for a while, just take a breath, think outside the box and attack it from a different angle ;)
Well that's about it for this week. The original intention was that is was going to be my last week of working on this game. (Un)fortunately my artist partner has gone on holiday this week so he is unable to work on the game, the result being I have given us an extra week to complete the game, yey! :)
Oh BTW, the title for the game is hidden in the demo above, see if you can work out what it is ;)
Its been a few more days now and quite a bit of progress has been made so I thought I would update. First, have a go at the game in its current form above (use the keyboard to control).
Since last time I have concentrated on the parts I was less technically confident about such as the correct rendering method of animated sprites and how I was going to handle the rotation of the world.
First I tackled rendering. In my previous update the tiles in the world rendered simply by drawing vector squares to Sprite.graphics, then shifting their container to move them. When I tried to run this on an actual mobile device however the performance was extremely poor, less than 1FPS. The reason for this was because drawing vectors on mobile devices doesn't really fit well into their GPU-orientated graphics pipeline. To get decent frame rates on mobile I needed to find a way to take advantage of the mobile GPU.
Fortunately for me those clever guys who wrote NME were way ahead of me and developed the "Tilesheet.drawTiles()" API:
[codesyntax lang="actionscript3" lines="normal"]
extern class Tilesheet { static var TILE_SCALE:Int; static var TILE_ROTATION:Int; static var TILE_RGB:Int; static var TILE_ALPHA:Int; static var TILE_TRANS_2x2:Int;
static var TILE_BLEND_NORMAL:Int; static var TILE_BLEND_ADD:Int;
function new(inImage:BitmapData):Void; function addTileRect(rectangle:Rectangle, centerPoint:Point = null):Void;
/** * Fast method to draw a batch of tiles using a Tilesheet * * The input array accepts the x, y and tile ID for each tile you wish to draw. * For example, an array of [ 0, 0, 0, 10, 10, 1 ] would draw tile 0 to (0, 0) and * tile 1 to (10, 10) * * You can also set flags for TILE_SCALE, TILE_ROTATION, TILE_RGB and * TILE_ALPHA. * * Depending on which flags are active, this is the full order of the array: * * [ x, y, tile ID, scale, rotation, red, green, blue, alpha, x, y ... ] * * @param graphics The nme.display.Graphics object to use for drawing * @param tileData An array of all position, ID and optional values for use in drawing * @param smooth (Optional) Whether drawn tiles should be smoothed (Default: false) * @param flags (Optional) Flags to enable scale, rotation, RGB and/or alpha when drawing (Default: 0) */ function drawTiles (graphics:Graphics, tileData:Array<Float>, smooth:Bool = false, flags:Int = 0):Void; }
[/codesyntax]
With it you pass an array of tile data with a number of optional properties such as Rotation, Alpha and Scale. It then does the heavy lifting behind the scenes of building a vertex buffer and sending it to the GPU for rendering with your Tilesheet texture. This results in two big wins for performance; 1) you can render a great many sprites on the GPU in the same render, 2) you dont perform any expensive texture switches if all your sprites are on the same texture.
As for the tilesheet itself. At first I was planning on using a manual method of putting the sprites onto the tilesheet such that each sprite was the same size and we arranged in a very simplistic "one row per animation" fashion:
The problem with this however was that it was going to be a fairly inefficient way of organising the assets and it was going to be tricky for Moh (the artist) to build these animations frame by frame and put them on there while keeping them looking good.
Fortunately for me this problem has already been encountered by those before me. Philippe Elsass (of Flash Develop fame) has written a library called TileLayer that solves two problems. Firstly it abstracts out the some of the hard work of using the low-level drawTiles() API into a more friendly parent-child-like syntax which should be very familiar to any flash developer. Secondly it provides a parser for "Sparrow" spritesheets.
Sparrow is a library for game development in pure Objective-C. Part of that library is a method for loading tightly packed, animated spritesheets. These spritesheets can be generated by a number of tools but the best one I found was Texture Packer (by Lee Brimelow Andreas Löw):
Using the tool you can take a number of input's such as SWFs or other images and output a compact spritesheet and an XML document containing the data NME will use to render the sprites on the screen.
One problem I encountered with using Texture Packer with SWFs however was that for it to work you must do all your animating on the timeline, it wasn't smart enough to populate named sprites from the library. Another thing to remember is that you must arrange your animations from the top left of the stage. If you put the animation in the middle of the stage it will be offset when it is rendered in the game. Not big problems but it did cause some head-scratching for a while before I realised what was going on.
With my sprites now loading and animating correctly in flash I decided to have a go at getting the game to run on mobile devices. Thanks to the way NME works it was a pretty simple process to generate a template project "nme update application.nmml ios" then run the generated xcode project file. And it runs! The only problems was that the frame rate was around 15FPS on my iPhone 4, which was far lower than I was hoping for.
So I started thinking about what could be causing a slow down. In flash when you try to render objects that are off-screen, flash automatically works out if it needs to be rendered and if not then it skips the render and hence improves the performance. I had a suspicion that Tilelayer / NME couldnt perform this operation so all the tiles that weren't on the screen were being unnecessarily drawn to an off-screen buffer and hence lowering the performance.
Because in this game the player is always in the centre of the screen I knew that a crude way to calculate whether a tile was on screen was to simply check the distance of the tile from the player, if it was greater than the width + height of the screen (because the world rotates) then I knew it would be off-screen and thus didn't need to be rendered:
As suspected this had a marked improvement in FPS, the game was now running on my iPhone at about 30FPS. An improvement but still short of what I was hoping for.
After some more experimentation I discovered another problem. In the code for checking whether to render a tile or not I was doing the following:
[codesyntax lang="actionscript3" lines="normal"]
public function update(delta:Int) : Void { var dx = x - game.player.x; var dy = y - game.player.y; visible = animated = (dx * dx) + (dy * dy) < game.root.stage.stageWidth * game.root.stage.stageHeight; }
[/codesyntax]
The problem is that apparently the call to "stage.stageWidth" and "stage.stageHeight" is very expensive. I presume behind the scenes NME is making an expensive call to the device for width and height information.
Once I took out those calls (cacheing them in the game object instead) and tested it again on the device the FPS was now up to more what I was hoping for at 60FPS :)
The final technical hurdle was to rotate the world. I was worried about this one as I was contemplating all the mathematical calculations that would be needed to work out the rotation for each tile in the game. Fortunately however there was a better solution. Because NME is based on the flash API I was able to put the world within a container Sprite, then offset the container by the player's position - screenH/2 and screenW/2 then rotate the container thus giving the illusion of the world rotating.
To my relief this idea actually worked and even more incredibly it didn't seem to affect the frame rate! See the video below of it running one my iPhone 4, iPad 3 and my old iPhone 3G.
The 3G will need some more work to make it run at acceptable framerates, but im impressed it even runs on that thing, you cant do this with Adobe's Stage3D!
Well that enough of me talking about this for now, I need to get on with making the damn thing! Next up is some more game-play elements and perhaps some menu structure.
.. and we have progress! See above. Use the keyboard to control the 'player'.
The first thing you will noticed is that there is a world populated with solid blocks and some orange circle things that may look a little like collectables :P To generate this level I decided to take a page out of Notch's book and build the levels at the pixel level in Paint.NET. That way the image editing software becomes the level editor. So as an example, the data that builds the level for above looks like:
That's a 50x50 pixel image which represents a world 50x50 tiles wide. Each pixel in the image has a colour which corresponds to a tile type in the game. Black is a solid wall, red is the player spawn point and orange is a 'collectable'. As the game develops we will be adding more tile types and hence colours.
At runtime all I do is load the level data image and loop through the pixels, grab the colour value and populate the world with the appropriate object. A problem I soon encountered however is that for some reason on different platforms Haxe reads the colour value different. This made things problematic so instead what I have done is make another 8x8 image as a "key":
Loading this key first I can then determine what colour the platform will be recognising a particular tile type as. So to generate the level the code looks like:
[codesyntax lang="actionscript3"]
class LevelManager { public static var TYPES : Hash<Class<Dynamic>>;
public var gridW : Int; public var gridH : Int; public var tiles : Array<BaseObject>;
public function new() { if (TYPES == null) { var objectTypes = [null, SolidBlock, SpawnPoint, Ring]; TYPES = new Hash<Class<Dynamic>>(); var bmd = Assets.getBitmapData("assets/levels/key.png"); if (bmd == null) throw new Error("key png is null for some reason!"); var i = 0; for (y in 0...bmd.height) for (x in 0...bmd.width) if(i<objectTypes.length) TYPES.set("" + StringTools.hex(bmd.getPixel(x, y), 6), objectTypes[i++]); } }
public function loadLevel(stageIndex:Int, levelIndex:Int) { var bmd = Assets.getBitmapData("assets/levels/s" + stageIndex + "_l" + levelIndex+"/world.png"); gridW = bmd.width; gridH = bmd.height; tiles = [];
for (y in 0...gridH) { for (x in 0...gridW) { var c = StringTools.hex(bmd.getPixel(x, y), 6); var t = TYPES.get(c + ""); if (t == null) { continue; } var o : BaseObject = Type.createInstance(t, []); o.x = x * Game.GRID_SIZE; o.y = y * Game.GRID_SIZE; Game.I.addObject(o); tiles[y * gridW + x] = o; } } } ...
[/codesyntax]
Once I had the level populating I started getting the basics of the physics sorted. At first I thought it was going to be a nightmare as in the original version of the game it appeared as if the whole world rotated about the player (see video for reminder), I worried about how I was going to handle the complex physics of a grid at odd angles while continually rotating. After a while however I realised that what was actually going on was that the world was standing still and all that was happening was that the camera was rotating at same rate at which the gravity vectyor was changing, thus giving the illusion of a rotating world, eg:
Once I realised this fact it made my life a whole lot easier. Calculating the physics for the world should now just be a matter of solving a circle against a static grid without rotations. I decided to go with my own physics solution rather one of the existing solutions such as Box2D or Nape as I thought that it should be pretty simple to calculate and I knew from a previous project that using Box2D or Nape would have issues at the joins between tiles.
The solution it turns out took a little longer than I thought but I eventually cracked it. The key was to use the Separating Axis Theorem with Voroni Regions, there is a great tutorial on it over at magnet software, they have a handy SWF that demonstrates the concept really well:
As can be seen from above that all you need do is split the problem up into a grid, then in turn check each of the 8 neighbouring cells from the current cell. The north, east, south and west cells can be classed as one type and only need to have their relevant axis checked against the radius of the player wheres the corner cells need to be checked against the distance from the closest point. In code this looks something like:
[codesyntax lang="actionscript3"]
// From Player.hx
override public function update(delta:Int) : Void { #if !mobile if (Ctrl.instance.isDown("up")) vel.y -= 1; if (Ctrl.instance.isDown("left")) vel.x -= 1; if (Ctrl.instance.isDown("right")) vel.x += 1; //if (Ctrl.instance.isDown("down")) vel.y += 1; #end
if (Ctrl.instance.mouseDown) vel.y -= 1;
var d = delta * 0.01; vel.x += gravity.x * d; vel.y += gravity.y * d;
var newPos = new Vec2(x + vel.x * d, y + vel.y * d); var ntx : Int = Std.int(newPos.x / Game.GRID_SIZE); var nty : Int = Std.int(newPos.y / Game.GRID_SIZE);
private function checkTileCollide(fromTX:Int, fromTY:Int, toTX:Int, toTY:Int, pos:Vec2, vel:Vec2) : Bool { var tile = game.level.getTile(toTX, toTY); var dTX = fromTX - toTX; var dTY = fromTY - toTY; if (tile != null && tile.is(SolidBlock)) { if (dTX == 0) { var d = Math.abs(pos.y-((toTY - fromTY) > 0?toTY * Game.GRID_SIZE:fromTY * Game.GRID_SIZE)); if (d < radius) { pos.y += dTY * (radius - d); vel.y = 0; return true; } } if (dTY == 0) { var d = Math.abs(pos.x-((toTX - fromTX) > 0?toTX * Game.GRID_SIZE:fromTX * Game.GRID_SIZE)); if (d < radius) { pos.x += dTX * (radius - d); vel.x = 0; return true; } } else { var tp = new Vec2((dTX>0?fromTX:toTX)*Game.GRID_SIZE, (dTY>0?fromTY:toTY)*Game.GRID_SIZE); var vToCorner = new Vec2(tp.x - pos.x, tp.y - pos.y); if (vToCorner.lengthSqr() < radius * radius) { var ang = Math.atan2(vToCorner.y, vToCorner.x); pos.x = tp.x - Math.cos(ang) * radius; pos.y = tp.y - Math.sin(ang) * radius; //vel.x = vel.y = 0; return true; } } } return false; }
[/codesyntax]
Its not 100% perfect, there is some oddness when the player hits a corner but will do for now.
On the art side of the project Moh has been making good progress coming up with themes for the game. We have been playing around with the idea that the player is a Hamster lost in space, which I really like the idea of. To test this idea he made little mock-up, which looks great:
You may have noticed that currently the game is in Flash. That's because with NME you can target Flash as one of your outputs. This makes developing and testing the game alot easyier (a lot faster to compile and run). I have however been very aware of the problems I could cause myself if I developed the whole game solely in flash and only testing on mobile right at the end. Trying to track down an obscure problem in a fully written game would be a nightmare. So I have been making progress with getting the game to run on my iPhone 4.
One of the problems I faced (and I banged my against the wall for a while on this one) was that for some reason when the level was populating from the PNG, certain tiles weren't being built. I couldn't for the life of me work out why. To cut a long story short, apparently when building for iOS in Haxe you MUST put the super call in the constructor BEFORE any other call, else the code before the super call in the constructor wont be executed:
[codesyntax lang="actionscript3"]
class Player extends BaseObject {
public function new() { trace("This will not be executed when built for iOS but WILL be executed when built for flash"); super(); trace("This will be execute on flash AND iOS"); }
...
[/codesyntax]
A small thing to remember but quite a gotcha for the NME Haxe newbie!
Another issue I have run into is the fact that my iPhone currently has iOS 5.1 on it, this means that to use it as a testing platform I had to upgrade my Macbook to OSX Lion which meant I have to leave it running over night and this morning downloading and installing. Not a biggie as I have been meaning to upgrade for a while anyways, but an inconvenience when you want to sit down to test your shiny new game out!
We have quite a way to go, but im happy with the progress we have made in 3 days thus far :)
I know I have been posting alot of videos lately but this one caught my eye for a couple of reasons this morning.
Firstly its an impressive video showing how easy it is to intercept 80% of the worlds calls with just $1,500 worth of equipment. Also the source article over on Venture Beat says that he can also jam a band preventing calls just as easily which is quite scary.
The second reason why this video caught my eye is it appears that this brit-hacker is wearing high-heels throughout his presentation, check it out: