Another day, another app store. Here's Shikaku Madness on Google Play. I was able to use my brand new Nexus 7 to test/publish an Android Cordova build of the game.
Unfortunately, the Javascript performance of the Android WebView isn't quite as good as the iOS UIWebView; the main user interaction (tap + drag to create rectangles) is faster on my two year-old iPhone 4 than on the Nexus 7. However, CSS transitions perform great on both devices.
I was hoping to integrate a Google Play in-app billing Cordova plugin, so as to make the game available for free (and sell additional puzzles), but the only plugin I could find didn't seem to be very robust, and didn't work for me "out of the box." I considered writing my own plugin, but that in itself is kind of a daunting task, due to the fact that I have no Android/Java experience. Who knows, maybe if I get real bored (or if the Android version doesn't sell any copies) I might try to make a free version.
In any case, if you have an Android device, check it out!
Just a quick note to say that I ponied up the $100 to publish apps on the Mac App Store, and dropped a copy of Shikaku Madness in there. It's part of my experiment to port "HTML5" games to as many platforms as I possibly can. I was inspired by Lost Decade Games, who wrote up a basic tutorial about embedding a Javascript app into a Cocoa WebView. Unfortunately, their tutorial is slightly out of date, so I spent a bit of time struggling to find a way around some annoying issues. One of the weird quirks of the WebView is that it doesn't persist localStorage by default, and there is apparently no way to enable it without resorting to a private API call (which we all know, are the devil). The app was approved, though, which I guess means that particular call can slide under the radar. When I have more time, I'd like to learn more about writing an Objective-C/Javascript bridge (this is something that's officially supported; it's how Cordova/PhoneGap plugins are able to work) to implement in-app purchase.
I probably wasted a bit of time fussing around with creating my own custom project. However, the OS X Cordova port is pretty bare bones at this point, which is why I didn't use it. Maybe a better choice would have been to try MacGap?
In my last post I complained about dealing with all the static images that one has to create in order to support multiple iOS device resolutions. I was just getting so frustrated dealing with layouts that required 3 different static images, and having to jump through hoops whenever I wanted to display some sort of variable length text. About that time, I was tasked with creating a Javascript-based game at work (the very first of my life, which was kind of a milestone), with the requirements that it be DOM-based (not sure why that requirement existed, since the graphics were all bitmap, but I digress). Anyway, my thoughts turned to using HTML and CSS for the interface of Shikaku Madness, the game I was working on. It was a perfect fit, actually, since the UI could be easily recreated with HTML elements. While I promised myself that I would finish the Objective-C version of the game first, before experimenting with unproven technology, the lure of doing something new proved to be too much. Within a week I had ported most of my main game logic over to Javascript (CoffeeScript, actually).
Another plus for creating a game with Javascript is its' portability and multiple deployment targets. I could put a version on the web for prospective players to try for free, and also port it to a variety of different platforms using a tool like Apache Cordova. And there could be additional porting opportunities in the future: Microsoft is getting on the Javascript bandwagon these days, making Javascript a first-class citizen for both Windows 8 and Windows Phone 8, and Mozilla is working on the B2G (Boot-to-Gecko) project which will allow web apps native access to a mobile device. While it's not a "write once, run anywhere" situation, Javascript has the potential to run on many more platforms than Objective-C does.
In the process of porting my game, I learned quite a few interesting things about developing "HTML5" games (kind of a misnomer), which I thought I would share here. First off, Javascript performance in Mobile Safari is quite crap. Even running with the "Nitro" Javascript engine, it's considerably slower than your desktop. Shikaku Madness is about as simple as you can get for user interaction — only one touch is being tracked at a time, and a single HTML element's size/position is being updated based on that input. Even still, the UI lags behind user input... it's not unplayable, but it's noticeably slower than native code.
There are a few tricks you can do to make some interactions feel more responsive. Using the CSS3 transform property, you can animate HTML elements with hardware acceleration, which makes movement much smoother. Instead of relying on a "click" event to trigger button actions, you can listen for a "touchstart" event, which fires instantly when the user taps an element. Even with these improvements, though, the app isn't quite as responsive as you would expect. I feel like the performance is almost there, though.
Mobile Safari's audio support is also quite crap. Even if a sound is preloaded, there's a noticeable delay when the sound is first played. Another annoying problem is that sound effects played through Mobile Safari ignore the hardware mute button, and the volume can't easily be changed by using the hardware buttons (you have to hit the +/- buttons at the exact same time the sound is being played, otherwise iOS thinks you're trying to adjust the ringer volume).
Although I chose a DOM-based UI instead of using the canvas element (so that I could use CSS to style my interface), I learned that it's not quite as easy as I thought to have a totally dynamic application size. In fact, the version of Shikaku Madness that I submitted to the App Store uses fixed element width/height and font sizes. I'm still working on making my CSS dynamic enough to handle arbitrary viewport sizes, which will be a necessity if I try to publish an Android version. The key problem is that it's not possible to dynamically scale font size using CSS alone. The other problem is that I need to maintain a 2:3 aspect ratio in order to keep the layout consistent. I think I'm going to solve this by querying the viewport size, then modifying the app's container element and font sizes at runtime.
I've just scratched the surface in regards to getting Shikaku Madness on multiple platforms. My next step is going to be publishing on the Mac App Store, then trying for a Google Play release. After that I might try submitting to the Mozilla Marketplace and whatever the Windows app store is called... as long as I can get enough motivation to keep working.
Regardless, you should definitely check out the Shikaku Madness demo page I set up. The app should run with recent versions of Firefox, Safari, and Chrome. It might or might not work with IE9/10. Sadly, I don't have a recent enough copy of Windows to try out the modern versions of IE. Let me know in the comments if it borks on IE. Other comments and suggestions are welcome too!
Oh, my unbridled naivety! My last post discussed a new game, and how quickly it was coming together. Of course, any project is easy to start, and the real difficulty is dealing with the soul-crushing minor issues that rear their heads when you're almost done. In my case, dealing with image assets for 3 different screen resolutions (I use iPhone @2x assets for iPad) has sucked up a lot of time, and caused me to write my own custom class to superimpose TTF labels on top of image-based buttons, instead of creating hundreds of static button images. My game isn't even very complex, so I can't imagine the pain that an extra @4x resolution would cause for other developers.
All this annoyance from creating UI using static images has made me wish for the flexibility of CSS when programming interfaces. So much so, in fact, that the next project I start is going to use a combination of HTML, CSS3, Backbone.js and CoffeeScript, all wrapped together with Apache Cordova. It's not the Canvas- or WebGL-based future that many envision, but manipulating the DOM will allow me to use CSS3 for graphics. It's not like the puzzle games I've been making are very demanding in terms or performance, either. The lure of this new tech combination is so tempting that I have to actively resist the urge to re-write Shikaku Madness, but I'm holding strong and will finish up the Objective-C version before starting anything else.
Anyway, for you fine folks who are getting annoyed with creating UI assets in multiple resolutions, here's a simple class I wrote to hopefully streamline the process: CCMenuItemImageWithLabel.
// CCMenuItemImageWithLabel.h
//
// Created by Nathan Demick on 4/17/12.
// Copyright 2012 Ganbaru Games. All rights reserved.
//
#import "cocos2d.h"
@interface CCMenuItemImageWithLabel : CCMenuItemImage
{
// Label w/ text on the button
CCLabelTTF *label;
}
@property (nonatomic, retain) CCLabelTTF *label;
+ (id)itemWithText:(NSString *)text block:(void(^)(id sender))block;
+ (id)itemWithText:(NSString *)text size:(NSString *)size block:(void(^)(id sender))block;
@end
Basically all this class does is help you add a TTF label onto a blank button background. There's a method for creating a default sized button, and another that you can customize to handle multiple button sizes (I have small, normal, and large in my app). Check the implementation:
// CCMenuItemImageWithLabel.m
//
// Created by Nathan Demick on 4/17/12.
// Copyright 2012 Ganbaru Games. All rights reserved.
//
#import "CCMenuItemImageWithLabel.h"
#define kFontName @"insolent.otf"
#define kDefaultFontSize 24
#define kLargeFontSize 26
#define kSmallFontSize 14
#define kDefaultNormalImage @"button-background.png"
#define kDefaultSelectedImage @"button-background-selected.png"
#define kLargeNormalImage @"large-button-background.png"
#define kLargeSelectedImage @"large-button-background-selected.png"
#define kSmallNormalImage @"small-button-background.png"
#define kSmallSelectedImage @"small-button-background-selected.png"
@implementation CCMenuItemImageWithLabel
@synthesize label;
- (id)init
{
if ((self = [super init]))
{
}
return self;
}
+ (id)itemWithText:(NSString *)text block:(void(^)(id sender))block
{
int fontMultiplier = 1;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
fontMultiplier = 2;
}
// Create button
CCMenuItemImageWithLabel *item = [self itemFromNormalImage:kDefaultNormalImage selectedImage:kDefaultSelectedImage disabledImage:nil block:block];
// Create label + append to button
item.label = [CCLabelTTF labelWithString:text dimensions:CGSizeMake(item.contentSize.width, item.contentSize.height) alignment:CCTextAlignmentCenter fontName:kFontName fontSize:kDefaultFontSize * fontMultiplier];
item.label.color = ccc3(0, 0, 0); // Tweak this based on your button background image
item.label.position = ccp(item.contentSize.width / 2.1, item.contentSize.height / 8); // Tweak this based on your button background image
[item addChild:item.label];
// Return button
return item;
}
+ (id)itemWithText:(NSString *)text size:(NSString *)size block:(void(^)(id sender))block
{
int fontMultiplier = 1;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
fontMultiplier = 2;
}
// Create button
CCMenuItemImageWithLabel *item;
if ([size isEqualToString:@"small"])
{
item = [self itemFromNormalImage:kSmallNormalImage selectedImage:kSmallSelectedImage disabledImage:nil block:block];
item.label = [CCLabelTTF labelWithString:text dimensions:CGSizeMake(item.contentSize.width, item.contentSize.height) alignment:CCTextAlignmentCenter fontName:kFontName fontSize:kSmallFontSize * fontMultiplier];
item.label.position = ccp(item.contentSize.width / 2.1, item.contentSize.height / 10.5); // Tweak this based on your button background image
}
else if ([size isEqualToString:@"large"])
{
item = [self itemFromNormalImage:kLargeNormalImage selectedImage:kLargeSelectedImage disabledImage:nil block:block];
item.label = [CCLabelTTF labelWithString:text dimensions:CGSizeMake(item.contentSize.width, item.contentSize.height) alignment:CCTextAlignmentLeft fontName:kFontName fontSize:kLargeFontSize * fontMultiplier];
item.label.position = ccp(item.contentSize.width / 1.8, item.contentSize.height / 8); // Tweak this based on your button background image
}
// Default
else
{
item = [self itemFromNormalImage:kDefaultNormalImage selectedImage:kDefaultSelectedImage disabledImage:nil block:block];
item.label = [CCLabelTTF labelWithString:text dimensions:CGSizeMake(item.contentSize.width, item.contentSize.height) alignment:CCTextAlignmentCenter fontName:kFontName fontSize:kDefaultFontSize * fontMultiplier];
item.label.position = ccp(item.contentSize.width / 2.1, item.contentSize.height / 8); // Tweak this based on your button background image
}
// Create label + append to button
item.label.color = ccc3(0, 0, 0); // Tweak this based on your button background image
[item addChild:item.label];
// Return button
return item;
}
@end
Not very elegant, but you get the idea. Hopefully you can use this as a starting point to help shorten the time you spend doing grunt production work on UI resources. Any other tips or suggestions for dealing with multiple devices and screen resolutions? Hit me up in the comments.
It's been a day of breaking blog silence! Part two of the Unity tutorial probably isn't coming any time soon, in part due to the fact that I've been pluggin' and chuggin' on a new game. Yes, you can kick me now: it's called Shikaku Madness, and yes, it's a game based on an obscure Japanese puzzle. I'm trying out a few new ideas based on what I've learned from developing iOS apps for the past two years.
First, here's the sad story: I was pretty lucky that the first app I developed was based on an established game. People know what "nonograms" are, and are actively searching the App Store for such games. I get more downloads of Nonogram Madness now than when I first released it, even. My second app, Revolve Ball, suffers from a "discoverability" problem. Even though most people who try it out think it's pretty interesting, there's no way that people can find it organically. They don't know it exists, and therefore don't know to search for it. If my first app (that I'd spent so much time on) had clunked the way that Revolve Ball has, I might have given up.
So, for my next game, I'm making an adaptation of an existing genre: something that appeals to logic puzzle fans, and can be discovered organically. I'm also going the "freemium" route of making the app free, while selling more puzzles via IAP. Theoretically this will lower the barrier for entry, and get more potential customers. And, in another attempt to get organic promotion, I'm including an in-game puzzle editor, and allowing players to upload/share levels via the app's website. Who knows if all this nonsense will be successful at all? Not me, that's for sure. But the positive thing is that I've been able to crank this game out pretty quickly in the past three weeks, due to actually having some experience w/ Cocoa and cocos2d now.
Anyway, enough about bizness philosophy. What is "shikaku?" It's a type of logic puzzle, of course, where the objective is to cover a grid with squares. The grid has numeric clues scattered on top of it, which help you know where each square is supposed to go. Each square can only overlap one clue, and the number on the clue tells you how big the square is. That's probably not a great description; I'm still working on the best way to explain the concept. However, once you try a puzzle, you figure it out pretty quickly.
I'm hoping to have development wrapped up in about another month or so, and I'll be sure to post my continued progress and the results of my app experiment here. Hit me up if you'd like to get an ad-hoc copy of the game!