Thursday, February 10, 2011

Zenburn theme for Xcode 4

I finally upgraded to the GM seed of Xcode 4 today. It's a bit of an adjustment process as usual, but so far I really like what I see.

One of the biggest annoyances is that Xcode 4 doesn't automatically take many of your Xcode 3 settings during the update. Most egregious is the lack of theme compatibility. My favorite theme is the Zenburn theme (I use a similar theme in Visual Studio).

I've taken the liberty of converting the theme to Xcode 4 format and posting it for download here. Enjoy!

Tuesday, February 01, 2011

My first game is shipping February 3 (the personal story behind War of Words)

Always the dream

When I was in middle school, I decided I would create games for a living. I found some friends and created a little seventh-grade "company", Double C Productions, where we created state-of-the-art text adventure games in QBasic. This was in 1992. Then 3D happened, and I got completely left behind by the math and art requirements.

In 1995, at age 15, I got my first "real" job as a graphic designer. In 2002, I became a waiter, then a restaurant manager. In 2006, four years out of college, I finally "grew up" and got back to my roots: software development. But it wasn't games, it was enterprise business intelligence software. It did give me some real-world experience with the software development lifecycle: development, release, support.

In two days, nearly five years after that, my first game will ship.

Of add-ons and fame

But let's go back again, to 2005. That's when I started playing World of Warcraft. I'd been playing MMOs since 1998 (EverQuest), but WoW presented me with an interesting opportunity: add-on development. I ended up creating one of the first add-ons for WoW (it was the 262nd add-on on Curse, if you can believe it) -- HitsMode. That add-on is still alive and well, and it even has a modern successor.

In 2009, I created a little add-on called AVR Encounters. That add-on landed on the front page of MMO Champion (a huge thing in the WoW world), and netted me nearly a million YouTube hits. I was a bit of a celebrity. Then, around 3 months later, AVR Encounters became one of only around three add-ons in the history of the game to be intentionally broken by Blizzard. My run at fame was over, but my taste for game programming wasn't piqued.

But, I hear you saying, add-ons aren't games! So why am I telling you this? Because my add-ons are used by hundreds of thousands, if not millions of players. These players run the gamut from hyper-intelligent to hyper-stupid. I've gotten thousands of comments and hundreds of tickets, and I was starting to understand what supporting a game must be like. It was a valuable experience.

But it wasn't enough.

Then, everything changed

In the summer of 2010, I stumbled upon the old journals of Jordan Mechner. He is the creator of the original Prince of Persia, and the screenwriter of the 2010 movie by the same name. Mechner was a true prodigy: an artist, programmer, and writer. He has an attention to detail and a strong concept of what makes things "fun". Plus he speaks at least five languages, and at one point lived in Paris six months out of the year. He became, that day, my idol.

I spent an entire day reading through all 96 pages of his journals, which encompass his life from 1985-1993. Mechner reminded me a lot of myself -- although he's got some talents I don't have -- and it was almost scary. Yet he had used his talents immediately -- even during college -- to create commercial games. And I hadn't.

It was a wake up call.

Time to write a real game

Games have come a very long way since Mechner's day. I don't have a 100 million dollar budget or a team of artists. The scope of modern console and PC games is far beyond what I am capable of. But there is one platform that is still approachable. It reminds me, even, of those days back in 1992 where myself and a few other classmates hacked out our games and dreamed of the possibilities. That platform is mobile.

So, in September of 2010, I bit the bullet. I bought a Mac and set out on my first project. I wouldn't be able to do some triple-A first-person shooter. I couldn't create an MMORPG or even a good platformer. Since I'm not an artist like Mechner, just a graphic designer (there's a difference), I was a bit limited in my approach.

But I did have a good idea.

Thus was born my first game: War of Words. This is initially an iPhone/iPod Touch/iPad game, but I'll port it to Android and other platforms if it's successful.

The general premise can be summarized in three words (the mark, I'm told, of every great piece of art): Scrabble with bombs.

That right there should get you thinking in the right direction. If you want to know more, I've got the marketing story all ready to go over at www.wordswithbombs.com. You should check it out. It's free (ad supported), and it's fun.

Maybe my next game will be a shooter or a platformer, or even the next WoW killer. Maybe I'll create the next Prince of Persia. Who knows. Until then, enjoy.

Monday, January 24, 2011

How to tap a 5 liter mini-keg

On a whim, I brought home a 5 liter mini-keg of EKU, a German pilsner. Figuring out how to open it was a bit tricky, given that all the text on the keg is written in German. In fact, since this was the first time I'd ever attempted to open one of these, I was quite stumped. I Googled around, and came up dry. So I stopped back by the market where I bought it and asked the guys there what to do.

Turns out it's quite simple, and you don't need any extra hardware. No taps, no adapters, no kegerator.
  1. Pull up the lever on top and twist it hard counter-clockwise. The plastic won't break on you, and you do have to twist very hard.
  2. There's a big (usually red) lever at the base of the keg. Pull the top of the lever down hard. Then take the lever by the hand and pull the entire thing out from the keg. It will travel maybe an inch or so away from the keg, pulling a plastic bar with it.
  3. You're done! Now just twist the bar at the base of the keg to the left when you want to pour the beer.
The CO2 and everything is built in already, and it's fed into your glass by gravity.

Most people recommend chilling a keg in your refrigerator for 10 hours if it's completely at room temperature. If you just brought it home cold from the store, I'd still recommend letting it sit in there for 2-4 hours, depending on how long it was in your car.

I'm not sure yet how long these kegs last once opened -- I'll be experimenting with that this week.

Thursday, December 23, 2010

Total line count for your Xcode project

The Apple developer ecosystem definitely takes some getting used to when you come from the Microsoft world. Visual Studio is millions of light years ahead of Xcode. That being said, most things are possible with a little work (and bash skills).

I don't claim to have any experience with *nix command line flavors. My command line experience is limited to several versions of MS-DOS back in the day. Luckily, that's where Google comes in handy.

After trying lots of different things, I landed on the following bash command. Open Terminal on your Mac and navigate to your project's root directory. (Handy trick: type cd with a space after it, then drag the root folder into Terminal, and it'll automatically paste in the entire path).
find . -name "*.[hm]" -print0 | xargs -0 wc -l
You'll get a list of every .h and .m file in your project (searching subdirectories recursively), with how many lines are in each file. Finally, you'll get a total line count for all files.

Note that this counts all lines, even blank lines and comments.

Sunday, December 19, 2010

Push notifications in distribution builds

Push notifications may have been sailing along just fine for your app during development. But once you flip the switch to distribution, things may not work as smoothly.

Firstly, I recommend implementing the selector:
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
Throw an alert to tell you what's going on here (you can't NSLog it because you can't attach a debugger to a distribution build -- yet another annoyance).

If you get a message something like: "no valid aps-environment entitlement string found for application," like I did today, you may wonder what that means.

It's a simple solution. Go to developer.apple.com and create a new provisioning profile. Make sure the app ID is set to your actual app ID (without any wildcards). Also make sure that your app has Production push notifications enabled properly. The trick is this new provisioning profile. Get it made, download it, and install it in Xcode. Switch your project to sign using this new profile, and you should be golden.

Sunday, December 05, 2010

Learning the value of CATransaction

The iOS API is full of side effects. Although some developers might find these useful, when designing APIs as a product manager, I always insist on limiting side effects as much as possible -- it leads to developer confusion.

One side effect in particular perplexed me to no end this week, and required a lot of crafty Googling to narrow down. I'll post it here in hopes that spreading the information makes it easier to find for others.

The problem
I've got a CALayer, and I'm doing a variety of things including changing it's contents, hidden, and center properties. Changing the contents in particular is what got me -- there was a strange flicker from one image to the next whenever I did it. This made things look terrible, since I'm reusing these CALayers quite a bit in my engine.

The solution
It turns out that CALayer properties all include an implicit animation of 0.25 seconds whenever you set them. When you change the contents property from one image to another, for example, Core Animation actually crossfades between the two images. This is cool, but it really doesn't work for what I'm doing.

You can tell it to stop doing this, luckily, by manually opening a CATransaction before you make your batch of changes, like this:
[CATransaction begin];
[CATransaction setAnimationDuration:0];
When you've finished making changes to the CALayer, commit the transaction like so:
[CATransaction commit];
One gotcha -- if you forget to commit a transaction, the run loop appears to hang (without crashing), causing all sorts of fun problems. Never ever forget to commit your transaction if you open one.

Now let's say you want to nest animations. The outer transaction is set for a 0 second duration, while you want the inner transaction to take 0.5 seconds. Surround the inner transaction with some additional code like this:
[UIView beginAnimations:nil context:NULL];
[CATransaction begin];
[CATransaction setAnimationDuration:0.5];
// Perform animations by changing properties
[CATransaction commit];
[UIView commitAnimations];
Hope it helps!

Saturday, December 04, 2010

touchesShouldCancelInContentView is a life saver!

After a few days of horrible experiences with UIScrollView on iOS, I finally came across the solution from someone at Stack Overflow.

The problem
I have a UIScrollView with a grid of tiles inside it (it's a letter game kind of like Scrabble). When a user places a tile on the board, we want them to be able to pick up the tile again without scrolling the scroll view (and without waiting 150ms). For anyone who's worked with UIScrollView on iOS before, you'll immediately recognize that this is an incredibly difficult thing to do.

The solution
My board controller knows exactly when the user has picked up a piece, and signals it in the isPlaying flag. Armed with this, all I needed to do was put an innocuous little function in my custom UIScrollView subclass:
- (BOOL)touchesShouldCancelInContentView:(UIView *)view
{
 return !controller.isPlaying;
}
This tells the scroll view to stop messing around with anything if the user has picked up a piece. Magic!

Thursday, December 02, 2010

Various changes in WoW 4.0.3a and beyond

Finally spent some time in World of Warcraft today. My goal was to grab all the pets I would need to fill out Zee's Logical Pet Usage Flowchart. During my travels, I noticed a few interesting things that Google didn't help me out with.

Flight points
Cataclysm added lots of new flight points to nearly every zone in the old world. So I immediately fired up Google to look for a comprehensive listing of them. And came up dry. Turns out, Blizzard now shows you every flight point directly in-game on the zone maps. The icon looks sort of like a foot with wings. Blue means Alliance only, red means Horde only, and the neutral flight points are sort of... neutral colored.

Pets
Hunters need to grab tons of pets to stay competitive in the new WoW. With the expanded 20-slot stable, combined with hunter pets being given nearly every important raid buff and debuff in the game (yes, including Heroism/Bloodlust), today's hunter really needs a comprehensive collection. As of today, a few days before the release of Cataclysm, it's possible to obtain every hunter pet buff in the game except that given by the Shale Spider, a new type of pet found only in Deepholm. The Shale spider gives out the much-needed 5% to all stats buff, otherwise known as Blessing of Kings/Mark of the Wild.

My personal favorite new pet type in Cataclysm is the red fox. These awfully cute creatures happen to belong to the wolf family. I named mine Robin, because he reminds me of the Robin Hood character in Disney's old animated movie. Today I headed out to grab myself a fox, but to my surprise, the Fox Cubs in Redridge are no longer tamable! After a quick trip to Petopia, I hoofed it up to Loch Modan to pick one up (also grabbing the new flight point at Farstrider Lodge while I was there). If you're hankering for the red fox skin, you'll need to do the same.

Reputation
There's a new rep for both Alliance and Horde this expansion -- worgen and goblin. I popped into Darnassus to grab the new flight point just inside the portal from Rut'theran Village, and happened upon a new pair of quartermasters. The Gilneas Quartermaster was happy to furnish me with a Gilneas Tabard. This tabard gives you Gilneas reputation when you fight in any Classic, WotLK, or Cataclysm dungeons (but not Outland dungeons, by design according to Blizzard).

So I got to thinking -- couldn't I just runecloth my way to exalted with Gilneas? After all, I am tantalizingly close to the new 45 Exalted Reputations achievement -- why wait? But after casting about Darnassus for several minutes looking for the elusive cloth donation guy, I came up empty handed. Turns out runecloth donations are out in Cataclysm, in favor of the new tabard method. Oh, well, you can't get everything for free. Guess they need more stuff to keep us doing the new Heroic Deadmines until we're blue in the face!

That's it for now, but I'm sure there's plenty more to see and do in the shattered World of Warcraft. Until next time!

Wednesday, December 01, 2010

Alpha for CALayer

One of the purposes of this blog is for me to post information that might help other developers, where the info isn't easily found on the web.

Some of these things are silly, and probably obvious to most people. But in today's age of "Google first, ask questions later," it makes sense for me to collect these helpful hints online.

So here's today's tip of the day.

On iOS (and likely OS X -- I don't develop for that platform yet), let's say you've been used to working with UIView-derived objects like UIImageView and UILabel. Let's say you recently decided to change many of your objects to CALayers in order to optimize things a bit.

(I should take a quick time out here to note that in most cases, CALayers won't perform better than UIViews on iOS. But there are advantages, and CALayers are surprisingly easy to work with.)

Anyway, let's say you need to vary the opacity of this CALayer. On your UIView object, the property would be alpha. But on a CALayer object, the property is opacity. That's it. Simple. Profound. And surprisingly difficult to find when you've been programming all day.