Friday, August 12, 2011

Online Session Code for Big Objects (Plus a Warning)

In Chapter 9 of More iPhone Development, we wrote a set of classes that mimicked the behavior of GameKit's peer-to-peer connectivity, but for regular network connections (GameKit's only works with BlueTooth and local network connections). Basically, we wrote a class that lets you send and receive anything that can be packaged into an instance of NSData. Since it's relatively trivial to implement NSCoding for most classes, this means passing objects between two iOS apps (or an iOS and a Mac app) becomes pretty easy. You don't have to poll for the data, or worry about chunking out the data. You just make a method call and pass an NSData instance to send data, and then implement a delegate method for receiving data back from the other end. Life is good, right?

Hmm...

Maybe not. There's a pretty big limitation in the book's implementation. That implementation, designed for passing tiny packets of data (TicTacToe game moves), kept everything in memory. If you try to send a good size image to the other connection, likely you'd run out of memory fairly quickly.

A while back, I faced exactly that situation. For a kiosk app that MartianCraft was writing for a client, I needed to send large images shot with a DSLR camera from a Mac Cocoa program to an iPad program and also needed to send pictures taken with the iPad's camera back to the Mac Cocoa app. These images, compressed, ranged from about one to about five megs. I grabbed the OnlineSession class from More, figuring I had the network code basically done, and watched my application go down in a blaze of… well… not glory, that's for sure. Not only did the iPad run out of memory, it ran out of memory FAST… much faster than I expected. Even sending the smaller iPad camera images often caused low-memory crashes.

There were two basic problems with the OnlineSession class when you try to use it for sending larger volumes of data. First, as I said, was that it relied only on physical memory. Given that the physical limitations of the original iPad, this was problematic. But there was another, much bigger problem.

The second issue was that during the process of chunking up the data to send, the code kept making unnecessary copies of the data. Put simply, I made a n00b mistake. The mistake didn't impact the TicTacToe application because the game moves would easily fit into the send buffer, but it's a mistake I've made before and definitely should've known better.

So, what, specifically, was this mistake, you ask?

Using NSData's regular convenience constructor dataWithBytes:length: when creating the new NSData instance to store the portion of the image that won't fit into the send buffer. If you read the description of dataWithBytes:length:, it very clearly says that it makes a copy of the data you provide. So, every time a packet was sent, the code would create a new NSData instance to hold the remainder that wouldn't fit in the buffer, and it would copy all the remaining unsent data for every packet. Ouch.

So, as a simple example, if we were sending a 5 meg image, and the send buffer was set to 128k, the code would make a 4.825 meg copy after the first packet was sent, then a 4.75 meg copy after the second packet was sent, a 4.265 meg copy after the third packet, and so on. After every packet, another slightly smaller copy of the data was made. A descending progression that would eat up memory fast.

After a lot of swearing at myself, I made some modifications to the class to do two things.

First, I switched to using NSData's dataWithBytes:NoCopy:length:, which uses the provided data in place without making a copy. This kept the memory footprint a lot smaller. In some instances, because the DSLR images were so large and our app needed to send so many, I still hit memory problems. So, the second thing I did was to add filesystem caching of the outbound queue so that all the encoded objects waiting to be sent didn't have to fit in memory for the application to function properly.

The new version of the class functions exactly as the one from the book, so you should be able to just drop-in replace the OnlineSession from Chapter 9 with this one without making any changes to your application code.

You can download the new version right here.

Tuesday, July 26, 2011

Working for MartianCraft

In case you missed my tweets earlier this week, MartianCraft is looking to add a few developers. We're looking for a couple of experienced developers, and are also thinking about bringing in one or two entry-level devs without significant experience to be trained up.

Initially, the work would be project-based contracting and would start in late August. Conversion to full-time employment is a possibility, but not right away.

If you're interested in being considered, send an e-mail with relevant work experience and/or résumé/CV to work@martiancraft.com.

Thursday, July 14, 2011

Auto-Incrementing Build Numbers for Release Builds in Xcode

I use the wonderful Test Flight to distribute builds. One thing that Test Flight is a little picky about is build numbers. When you upload a build, it uses the build number to see if you're uploading a replacement or a new build. It will let you create a new build even if you don't remember to increment the build number, but it's an extra manual step, and then you end up with two builds with the same build number.

Because I'm forgetful, I wanted to automated this process. Basically, I wanted to increment the version short string any time we do an Archive and increment the bundle build ID any time we do a Release configuration build, but leave the version numbers alone on Debug builds.

Unfortunately, several of our projects are ones that we inherited or took over, so not every project uses the same version numbering scheme. How we increment 1.0b5 is different from how we increment 1.0.12, or a simple build number like 1058.

The way I deal with this is a Run Script Build Phase in my application's executable target that runs the following Ruby script (make sure you set the "shell" field to /usr/bin/ruby, and make sure the script is the last build phase in the application). Feel free to use this script if you wish and modify it to meet your needs. If you improve it, I'd be glad to incorporate improvements back into it. One item of note: the way that I differentiate between Archive builds and other Release configuration builds might be a bit fragile since I'm relying on an undocumented naming pattern in an environment variable.

Note: I'm aware of agvtool. I avoided it for two reasons. First, I wanted more control over the numbering schemes, and second, I tried using agvtool in a build script a few years back, but at that time, there were issues when you bumped the version numbers of a project that was currently open. Those issues may have been resolved, but I didn't want to fight that battle again.

def get_file_as_string(filename)
data = ''
f = File.open(filename, "r")
f.each_line do |line|
data += line
end
return data
end


def handle_alpha_beta(old_value, letter, infoplist, start_of_value, end_of_value)
parts = old_value.split(letter)
version_num = parts[0]
alpha_num = parts[1].to_i

alpha_num = alpha_num + 1
new_version = version_num.to_s + letter + alpha_num.to_s
print "Assigning new version: " + new_version + "\n"
new_key = "<string>#{new_version}</string>"

part_1 = infoplist[0, start_of_value - '<string>'.length];
part_2 = new_key
part_3 = infoplist[end_of_value + "</string>".length, infoplist.length - (end_of_value - start_of_value + (new_key.length - 1))]

new_info_plist = part_1 + part_2 + part_3
new_info_plist
end

def find_and_increment_version_number_with_key(key, infoplist)

start_of_key = infoplist.index(key)
start_of_value = infoplist.index("<string>", start_of_key) + "<string>".length
end_of_value = infoplist.index("</string>", start_of_value)
old_value = infoplist[start_of_value, end_of_value - start_of_value]

print "Old version for " + key + ": " + old_value + "\n"
print old_value.class.to_s + "\n"
old_value_int = old_value.to_i
print old_value_int.class.to_s + "\n"
if (old_value.index("a") != nil) # alpha
infoplist = handle_alpha_beta(old_value, "a", infoplist, start_of_value, end_of_value)
elsif (old_value.index("b") != nil) # beta
infoplist = handle_alpha_beta(old_value, "b", infoplist, start_of_value, end_of_value)
elsif (old_value.index(".") != nil) # release dot version
parts = old_value.split(".")
last_part = parts.last.to_i
last_part = last_part + 1
parts.delete(parts.last)

new_version = ""
first = true
parts.each do |one_part|
if (first)
first = false
else
new_version = new_version + "."
end
new_version = new_version + one_part
end
new_version = new_version.to_s + "." + last_part.to_s
print "New version: " + new_version.to_s + "\n"
new_key = "<string>#{new_version}</string>"
infoplist = "#{infoplist[0, start_of_value - '<string>'.length]}#{new_key}#{infoplist[end_of_value + '</string>'.length, infoplist.length - (end_of_value+1)]}"
elsif (old_value.to_i != nil) # straight integer build number
new_version = old_value.to_i + 1
print "New version: " + new_version.to_s + "\n"
new_key = "<string>#{new_version}</string>"

part_1 = infoplist[0, start_of_value - '<string>'.length]
part_2 = new_key
part_3 = infoplist[end_of_value + "</string>".length, infoplist.length - (end_of_value+1)]
infoplist = part_1 + part_2 + part_3
end
infoplist
end



config = ENV['CONFIGURATION'].upcase
config_build_dir = ENV['CONFIGURATION_BUILD_DIR']

archive_action = false
if (config_build_dir.include?("ArchiveIntermediates"))
archive_action = true
end

print "Archive: " + archive_action.to_s + "\n"


print config

if (config == "RELEASE")
print " incrementing build numbers\n"
project_dir = ENV['PROJECT_DIR']
infoplist_file = ENV['INFOPLIST_FILE']
plist_filename = "#{project_dir}/#{infoplist_file}"

infoplist = get_file_as_string(plist_filename)
infoplist = find_and_increment_version_number_with_key("CFBundleVersion", infoplist)
if (archive_action)
infoplist = find_and_increment_version_number_with_key("CFBundleShortVersionString", infoplist)
end
File.open(plist_filename, 'w') {|f| f.write(infoplist) }
else
print " not incrementing build numbers"
end

Thursday, July 7, 2011

Happenings and Prospects

I apologize for the relative dearth of posts here since WWDC. That week in San Francisco always tends to backlog me pretty badly (I returned from WWDC at inbox 1138 - and that's after spending the return flight answering e-mails), so I've been pretty much heads down on work-related stuff ever since. I've also, at the same time, been consciously trying to back away from the 12-15 hour days, 7 days a week schedule that I'd fallen into trying to help get MartianCraft off the ground. Those two have conspired to give me very little time for writing lately, but I think things are under control now.

I've got my semi-mythical OpenGL ES from the Ground Up 10th installment on skeletal animation nearly finished and hope to have it posted within the next couple of weeks, and it's bit of a doozy. My goal with this post is to make one of the more intimidating topics in graphics programming approachable. Fingers crossed, it's been a challenge making this topic approachable, but I think I'm getting there.

A few other bits of news.

First, work has officially started on Beginning iPhone 5 Development. Yes, I know it probably should be called Beginning iOS 5 Development, but as of right now, we're sticking with the naming sequence Apress established with the first book. Dave, Jack, and I have already started updating the book for Xcode 4, ARC, Storyboards, and all the other cool new stuff and are hoping to have the book ready to go to press when the GM version of iOS 5 ships this fall.

Second, I purchased a new domain today. There's nothing there yet, but OpenGLESBook.com is now mine, and I have big plans for it. Once Beginning iPhone 5 Development is in the can, I'm going to revisit my partially written OpenGL ES 2.0 book. I plan to revise it to use GLKit and to add material about the very cool new OpenGL ES tools we're getting with iOS 5.

As of right now, my plan is to self-publish. I'm still researching the exact process, tools, and services that I'll use, but my plan is to sell the book without DRM and at a reasonable price. I'd like to have an early access program, however since a lot of the material I'll be covering will be under NDA until iOS 5 goes GM, I can't promise that at this time.

Update on UpdateConf

Just wanted to let you know that I'm speaking at another conference. I'll be speaking at the inaugural UpdateConf in Brighton, UK on September 5th.

As part of the conference, I'm also giving a two-day intensive workshop on OpenGL ES. The workshop assumes no prior experience with graphics program, but will be fairly fast-paced and will cover a lot of ground because there's a lot of new ground to cover. I'll be going over the new GLKit and the incredible new OpenGL debugger as well as the updated OpenGL ES Instruments templates.

 Not interested in OpenGL ES? There are four other two-day workshops going on with some really awesome instructors. Marcus Zarra is doing a two-day workshop on Core Data, Drew McCormack is doing a workshop on Core Animation, Sarah Parmenter is doing one on iOS UI design, and Remy Sharp is doing one on HTML5 for mobile. The worst thing about giving this workshop is that doing so will prevent me from going to one of the other great workshops.

The conference proper has a number of speakers, including the workshop instructors and it's really shaping up to to be quite a conference, and also quite a steal at £99 for early bird tickets to the conference (the workshops are extra). You can sign up for the conference on EventBrite.

 Hope to see you there!

Saturday, June 4, 2011

On Being Excellent to Each Other

Marcus Zarra has a somewhat depressing post today on the excellent Cocoa is My Girlfriend blog. It's about the release of The Daily. Because of all the secrecy around The Daily prior to its official launch, I think not a lot of people knew Marcus was not just involved with it, but was actually leading much of the development effort.

I knew.

I wasn't on the main development team for The Daily, but I did most of implementation work on a single component (the 360° panorama). I wasn't in NYC every day the way Marcus was, but I went down there enough to know the conditions under which the application was written. I saw the endless late nights (actually, it was usually early mornings) and the stress and difficulties under which the app was written.

A personal stake always makes it harder to watch negative publicity. The Daily's launch was especially hard because I was in a position where I couldn't really come to Marcus' team's defense, since my involvement with the project wasn't yet public knowledge. If it had been, I would have been written off as biased.

So, I had to just sit back and watch it the way you'd watch a trainwreck. It was painful watching the snark. It was painful watching Loren Brichter, a well-respected member of our community put together a carousel demo in a complete vacuum and post a video of it as if it proved something about performance in an incredibly large and complex application. It was even more painful seeing John Gruber link to that video, spreading a false impression to a far wider audience.

They both enough about software development that they should have known better. There's almost no part of The Daily that can't be re-implemented in a few hours as a standalone application using static data and with great performance.

Doing the same thing as part of a large development application developed by a large team, working with a larger management team and a huge content and production teams under an unreasonable deadline and constant pressure? That takes more than being a competent developer. A lot more. It takes patience and diplomacy and a very high tolerance for frustration. I don't think I would've survived in Marcus' shoes all those months. I would've walked out or been escorted out long before the launch ever happened. It was honestly that tough.

I'm not sure that our community is getting quite as bad as it appears to Marcus at the moment, but there is no doubt that we are capable of producing our share of snark. And let's be honest… I am capable of producing more than my individual share. I don't think we think about being mean when we fire off a smug comment. I think it's usually just a side effect of expressing a myopic, partially informed opinion. I'm sure Loren honestly believes he could have done better with the carousel had he been working on The Daily. But he's wrong. He just doesn't know enough about the situation to realize it.

But it's not my intention to point fingers here. I'm been just as guilty at times. Last year, during the WWDC keynote, I was extraordinarily snarky about the Farmville demo, completely forgetting that I know people who worked on it. I wasn't intending to shit on their work, but I did, and I'm sincerely sorry about it. There have been other examples. I hope there won't be more in the future, but only time will tell.

I think Marcus' post should be read and taken to heart by all of us. I think it should serve as a reminder that real people — very often our friends and colleagues — are behind the software and hardware that we express opinions on. We should keep in mind that a lot of work went into it. We should also keep in mind that in most cases, we have no idea the circumstances under which the application was written.

Being critical is not only fine, but a necessary part of driving each other to be better developers. But we should try and avoid being a dick about it. It can be done respectfully, and should be.

I promise to try if you will.

Friday, June 3, 2011

Thoughts on Unity3D

As I stated in my previous post on 3D engines, I'm going to do four blog posts giving my thoughts on each of the four engines that I looked into using for a project recently. Those four engines are Unity3D, Sio2, Ogre3D, and Cocos3D. I'm starting with Unity3D, which we selected for one of our recent projects.

Now, let me state up front, that all four of these are competent engines and I could see situations where I would recommend three of the four engines for client projects and could easily imagine situations where all four of them would be good choices.These four do not make an exhaustive list; there are other engines out there, including some really good ones, but these were the four that we looked at for this specific project. As much as I like the UDK, for example, I don't like it enough to spend quality time in Windows, so that one is off the table for me until the fine folks at Epic decide to port their dev and content tools to the Mac.

Unity Overview

Unity3D actually predates the iPhone, and of the four engines, it's the one that feels the most mature and has the most robust developer tools. It also has one of the most active developer communities. Unity3D is a closed-source commercial product that you must pay for, which might be a turn-off for some, but it is worth every penny of the license fee.

Unity Pros

Unity is actually fairly easy to learn, yet has a feature set that is compares favorably to most other engines. Unity's asset pipeline is really robust and their tools give you the ability to very quickly make changes and test those changes.

One of the best things about Unity3D is the fact that it supports both Windows and Mac OS equally as development platforms. Regardless of which platform you develop on, you can generate games that runs on every platform Unity supports (assuming you're licensed for it). You can even have part of your team developing on Macs and the other part on Windows with no problems.

At the time I'm writing this, it is possible to generate games from Unity that run on Mac OS X and Windows (both native apps as well as games playable through a web plug-in), iOS, Android, Wii, Xbox 360, and PS3. The Unity folks are also actively working on adding Linux support. Console licenses are negotiated on an individual basis and are likely quite expensive, but it's nice to have the option to take a successful game to so many platforms without having to do a full port each time.

Having had to port OpenGL ES apps from iOS to Android more than once (which is no fun), I can honestly state that the Android support should be a huge selling point you're giving any thought to supporting both iOS and Android.

Unity Cons

The downsides to Unity3D are relatively few, actually.It is the most expensive of the engines I looked at, and the costs are completely front-loaded. You pay a flat per-developer license per platform up front, but then you can create as many games as you want for the platforms you're licensed for. For the basic iOS license, it's $400 per seat (one developer using up to two machines), for the Pro license, you're looking at $3,000 (because the $1,500 iOS Pro requires the $1,500 Unity Pro), which might sound like a lot of money, but it's really a pittance compared to the amount of development time it can save you. The "Basic" version of Unity, which allows you to generate Mac, Windows, and Web games (though excludes some of the more advanced features) is available completely free of charge.

The other potential downside in some situations is that the underlying C++ source code is not available. You work primarily in either JavaScript or C# (MonoScript) and at a higher level of abstraction. Unity supports a few other languages besides Javascript and C#, but only those two languages work for mobile development.

After about two weeks of spending my evenings with Unity, I actually came to the conclusion that not having access to the source isn't really much of a drawback, and for many developers, working this way will be better than working in C++. The engine takes care of almost all the low-level stuff you'd need to do, but even if it doesn't, Unity has a shader language that lets you write code that runs on the GPU and anything that needs to run on the CPU can be done by scripting inside Unity.

Although the programming in Unity3D is done with scripting languages, the scripts you write are actually compiled, so there's not a huge performance overhead to using them. In the exceedingly rare situation where C# or Javascript isn't sufficient, it is possible to send messages into and out of Unity from your application's C or Objective-C code.

Learning Curve

I found Unity surprisingly easy to learn. There are some really good resources out there, including lots of tutorials and instructional videos. I actually had a game functioning after about two hours of playing. It was ugly and the game mechanics were simple, but it was playable. Unity also has a fairly active developer community forum where you can go and get help when you get stuck.

If you don't already know something about graphics programming, you still don't need to be too scared or intimidated. Most of the gnarly stuff is squirreled away where you won't see it until you need it, and you can do a surprising amount by just configuring things in the development GUI. You can create, for example, a fairly full-featured physics simulations without ever writing a line of code. Want to stack up a bunch of crates and roll a ball into them and watch them all fall? You can do it without ever opening a text editor. Heck, you can even do that without opening a 3D program.

Complexity

I was scared of one thing going into learning Unity: I was concerned that because it was so easy (at least I had heard it was), that it was going to be a dumbed down game maker that sacrificed more advanced features for the sake of lowering the obstacles to entry. My fears were completely misplaced, though. That's not the case at all. The Unity folks have done a really great job of making their tools easy to use while still giving you the ability to do most anything you'd ever need to do. It's even possible (though the process is a bit convoluted) to integrate UIKit and Unity3D within the same application.

Asset Pipeline

The thing that actually impressed me most about Unity is what they call their "asset pipeline", which is the process by which you get 3d models, textures, and other assets into your game. If you've ever developed a game or game mod for the UDK, a Valve Source game, or other commercial engine, usually there's kind of a convoluted process you have to go through to get your game assets including characters, textures, props, and environments, into the game. You usually have to use separate applications to specify shaders and physics options, sometimes write a compile script or other text document to define certain trains, and then compile the object and package it all up into some kind of bundle or package. While developing a single mod, you typically iterate through this process many, many times. It can be a bit tedious and hard to learn and usually requires the use of multiple tools and lots of trial and error.

Unity bypasses almost all of this tedium. When you save your assets from your 3D program or Photoshop, you simply save them in your Unity project's Assets folder. When you launch Unity, or navigate back to it if it's already open, Unity detects the new file, or any changes you've made to an existing file. It imports it and adds it to the list of available assets.Anything you need to do that can't be done in your 3D program, such as specify a shader or identify the physics engine properties for the object, you do right in Unity, and most of that can be done without writing code (though everything you can configure without code can also be changed from code).

Which 3D program do you need to use? Pretty much any one you're comfortable with. Unity3D has direct import support for the native file formats from several programs including Blender , but any package that can export to Autodesk FBX or Collada can be used with full support for all features like bones, textures, animations, etc.It also supports native Photoshop files (.psd) for textures, flattening layers in a non-destructive manner when you build your app.Assets can even be given different characteristics for different platforms. You could have, for example, a 2048x2048 texture asset for the desktop version of your game but tell Unity to use a 1024x1024 version for game consoles and a 512x512 version on iOS and Android.

The Bottom Line

As I stated earlier, there are situations where I could see any of the four engines I looked at being a good choice, as could many of the other engines I didn't look at. But, honestly, if I had to make an engine decision without detailed information, Unity3D would be my first recommendation. The tools are solid, the company and community support is awesome, it supports many platforms, and getting assets into your game couldn't be easier. It's relatively easy to start using for both experienced developers and those who aren't.

A good, experienced graphics programmer working in mobile right now can easily ask $200 per hour or more because the demand far outstrips the supply. Figure it out. Unity's full iOS license is basically the equivalent of 15 hours of a graphics programmer's time. Yet, Unity will easily cut five times that many hours off of any decent size software game project's schedule, probably a lot more.

I'm giving Unity3D two big thumbs up. I'm using it on a project now and hope to use it on many more in the future because it's fun to use and removes much of the tedium associated with 3D programming without removing power.


1 Many of the more advanced features require the more expensive "Pro" license, however, and really cutting edge features from the latest games generally take a little while to show up in the tools