Monday, January 23, 2012

This blog is moving

I'm writing about my work as an indie game developer, so I might just as well give this blog a facelift and move it to my own website!

Update your bookmarks: http://blackish.at/blog

Thanks for reading!

Monday, December 19, 2011

NEW ORBIT Released!



NEW ORBIT is now available for iOS! Get it here on the AppStore!

Thursday, December 08, 2011

Building for Distribution with XCode 4.2

A bunch of links that helped:

How to build and submit [...] with Xcode 4
Xcode 4 user guide - but don't set that one value to YES or the archive will never show up in the Organizer as described here on stackoverflow

Just so I remember the next time I need it...

Lion + Xcode 4.2 + Unity 3.4 + iPhone 3G = Disaster?

Or: How to make a universal build that also runs on iPhone 3G or 2nd gen iPod Touch with Unity 3.4.2 and Xcode 4.2 on Lion.


No easy task, but with a little help from all over the web, I figured it out...


Step 1 - Unity:

In the Player Settings, choose SDK Version 4.3 (Yes, Xcode 4.2 comes with iOS SDK 5, but don't set this to iOS latest or you'll get a bunch of errors when building...)
Target iOS Version: 3.1.3 (Choosing something lower doesn't work thanks to a bug in Unity)
Target Platform: Universal (more about this later...)
Target Device: iPhone + iPad (just because I want it to run on both) - not sure these matter, as XCode seems to ignore them...


Build and open it in Xcode

Step 2 - XCode:


• Set the Base SDK of the project and the target to iOS 5.0
• On the target, set the Targeted Device Family to iPhone/iPad
• Still on the target, set the iOS Deployment Target to 3.1.2 (Notice: That's below 3.1.3! I use this just because it's the version my 2nd generation iPod Touch is running)

And now for the part that actually makes it run on old armv6 devices:
•Both in the project and on the target, look for Architectures, click on $(ARCHS_STANDARD_32_BIT) and choose Other... from the popup. Hit the plus and add armv6. (More info on here on stackoverflow)

• Select Unity-iPhone > Your Device from the top and Build+Run!


Tuesday, December 06, 2011

Unity: iPad Splash Screen Rotation


In XCode open Info.plist and look for UISupportedInterfaceOrientations
Add all 4 of the following items and the splash-screen will match the current device-orientation:
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
UIInterfaceOrientationPortrait
UIInterfaceOrientationPortraitUpsideDown
Tested with Unity 3.4.2 and iOS SDK 4.2

Thursday, December 01, 2011

Behind the scenes: Age of Curling Prototype

A blast from the past - This is what the first prototype and the first test-level of Age of Curling looked like.

Wednesday, November 02, 2011

Unity: Editor Selections (EditorScript)

How to set selections in the Editor from an EditorScript:
Selection.activeObject = myAudioClip; //single Object
Selection.objects = new Object[3] {obj1, obj2, obj3}; //multiple objects

Monday, October 31, 2011

Unity: C# Function Survival Guide

Let's say you have a function that fades the screen to black, loads a new level and then fades back in.

public IEnumerator LoadLevel(levelName) {
     yield return StartCoroutine(FadeOut());
     Application.LoadLevel(levelName);
     yield return StartCoroutine(FadeIn());
}


Said function lives in a script on an object that doesn't get destroyed when a new level is loaded. But if you call that function from another script, the screen will fade out, the level will load, but the screen will never fade back in.

WHY?

Well, if the caller gets destroyed the function will stop running and never reach the point where it could fade back in.

And the easy solution is: Create a second function (in the same script as LoadLevel of course) that does nothing but start the primary function.

public void InitiateLoadLevel(string levelName) {
     StartCoroutine(LoadLevel(levelName));
}


Thursday, October 20, 2011

Unity: How to create an array with values in it

C#: public int[] val = new int[3] {1, 2, 3};
JS: var val : int[] = [1, 2, 3];

Tuesday, October 18, 2011

Greatest App Ever - Rejected

In early 2009 the App Store hype was at its max, everyone loved reading stories of one-man-companies getting filthy rich with ridiculous apps. Well, I was getting a bit sick of it to be honest...

So I set out to end it all by creating the ultimate iPhone app.

I stripped away piece after piece until I arrived at the pure and beautiful core - the heart of it, if you will - and created the ultimate App Store app to rule all other apps. Simple, yet deep. Fast to grasp, yet always new.

Unfortunately the Apple App Store review team didn't recognize the artistic value, let alone the cultural importance of this app and rejected it for offering "no value to the user".


Money Generator

An app that does nothing but display 
how much money it has generated.


PS: There was also going to be a free trial version:


Unity: Calling C# functions from a JS script

In short: the C# script has to be compiled before the JS script for this to work. To achieve this you have to move the C# script into one of the following directories: Plugins, Standard Assets or Pro Standard Assets.

It also works the other way around - if you want to call JS from C#, move the JS into one of the folders.

It does not work both ways at once however.

More info about script compilation order can be found in the Unity docs here...

Monday, October 17, 2011

GameDev Audio Workflow

This is my current workflow for recording, mixing and exporting audio for use in my games. (Currently recording dialogue...) I'm very interested in your approach, so leave a comment, tweet to @col000r or send me mail if you want to share your workflow!

Apple Logic Express 8:

This is where I record and mix all the Audio. (Or at least try to, because IMHO Logic is a mess)

To make my life easier I usually cut all the dialog into separate clips and name them with the first few words of the first sentence in the clip, split everything into separate tracks for separate characters and use color-coding to mark the clips I like best if there are multiple takes. (Rarely do I use comps, I just don't find them very helpful...)

I currently keep all the dialogue in one big file (so I can tweak the sound of individual characters all at once). It's not possible to batch-bounce files from markers, so I just add a region marker for each mission, select that range and bounce the whole mission to a WAV.



Adobe Audition CS 5.5: 

That WAV goes into a multitrack session in Audition where I split all the dialogue into small pieces by creating named marker regions and batch-exporting them to files. (They need to be short because they're going to be loaded at runtime and any file larger than a few KB will cause a short pause on iOS devices...)

I add markers to a multitrack session and not directly to the WAV because there is no way to copy/paste markers, and if you copy/paste audio from one file to another it clears the markers. Thus this is the only way to preserve markers when you want to re-export the files from a new bounce without having to set up everything anew.

A short how to:
  • Move the playhead to any position 
  • Press Shift+Cmd+M to create a region marker
  • Adjust the in and out points of the region, open the the Markers window and rename the marker (we'll use the name as the filename later on)
  • When all markers are done, do a Multitrack > Mixdown to new File
  • Select all the markers in the Markers window and click the "Export audio of selected range markers to separate files" button. 
  • Done!


Unity 3.4:

Back in Unity I'll set the audio-files to mono 96 kbs Compressed StreamFromDisk - that's the best setting I've come up with for the iOS version of the game. I wrote an Editor-script that lets me batch-change the audio format of all selected files to this, to save some time. I haven't looked into the best solution for the desktop version yet, but the base files are all 44.1khz, 16 bit stereo WAVs, so there's room for improvement...

Then I'll drop everything into a custom editor window (as blogged about before here), copy/paste the dialog from the script, call the right pieces at the right time from script and finally in the end it looks like this:


(If you want to see and hear it: http://beta.blackish.at)

Tuesday, October 11, 2011

Monetization Horrors

Monetization in progress... Oh the horrible ideas that come from being able to sell stuff via in-app purchases...

Wacom Bamboo Paper: A notebook to draw in. Nice! But if you want more than one notebook you have to pay. This feels so wrong... You're not paying for new functionality, you're paying for more of the same and it feels just plain wrong. It's a bit as if Adobe started charging for colored pixels : "Your blue digital-ink is low, buy 1000 milipixels for $10"... but speaking of Adobe:

Adobe Ideas. It really is a nice and clean app. $5.99, huh, well, okay... Peanuts compared to the huge pile of money I threw at them for the Creative Suite, right? So what have we got here? Pencil, eraser, colors, buy layers... BUY layers?! What the hell! What did I pay $5.99 for? Now I need to pay another $0.99 to use layers? Screw this!

OffMaps 2: The first OffMaps cost $4 and would let you download map-data from the OpenStreetMap project to navigate while offline (on vacation, etc.). You would just select any area, choose how many zoom-levels you wanted to download and go: download! Easy to use, worked well. But then the greed set in and they built OffMaps 2 where you pay $0.99 to find out that the app comes with 2 download tokens that let you download data for only 2 cities and then you need to pay for more tokens. Oh baby, now I'm being properly monetized! I picture suitcase-carrying yuppies triumphantly shaking hands behind a mirrored glass wall while the test-subject eyes the buy button...

I could list many more examples... 


I'm not saying all this it doesn't work, I'm just saying it doesn't feel right. If you care more about short-term income and less about if your customers feel cheated or mocked, go ahead. But your customers will remember. Or maybe you're lucky in this 5-min-attention-span market and they won't. I'm just noting that if your expectations are built up and then you hit pay-wall after pay-wall before you can get to the expected experience, it's not a good feeling. And no, no one reads the description text before getting a free or cheap app, they look at the screenshots - *bam*, expectations created - download - disappointment.


So when do in-app-purchases feel right?

A few criteria:
  • The app has to deliver a satisfying experience by itself. An app that is obviously crippled without purchasable add-ons is no satisfying experience.
  • Don't shove in-app purchases in my face from the get-go. Let me breathe and experience the app. If I want more, I'll check if there's more.
  • Don't charge for something that is obviously just a simple change in code (like more notebooks). Yes, it could work, it could even sell very well, but I'll feel like you're mocking me and I'm not going to buy it out of spite.
  • Be clear about what's included and make it very clear from the get go that there is additional content that can be purchased.
  • Don't tell me you have Downloadable Content (DLC) planned from the start. It's like saying that you're planning to charge me over and over and over again to get the full experience. (Assassins Creed 2 had a few chapters in the story arc reserved for DLC - WTF?! I paid EUR 60,- for this and then there's holes in it?) Rather say something like this: "If a lot of people are interested, we'll think about developing more content in the form of expansion packs. Those will be available at low cost and if it sells well, we might even give some away for free!" - This feels nice, doesn't it?
  • Naming the additional content "expansions" or "expansion packs" also feels nice, it sounds like good stuff, it sounds like the old days. "DLC - Downloadable Content" on the other hand is sounding more and more like corporate talk. When I hear DLC I think of corporate manager-types with dollar-signs in their eyes who use the word "monetizing" in every second sentence...

This is not a guide on how to become rich with in-app purchases, these are my very subjective feelings when encountering them in the wild.

Monday, October 03, 2011

The trouble with Angles

The problem is this:


Both calculations give you the same result!

float a = Vector2.Angle(Vector2.up, new Vector2(5f, 2f)); //a = 68.19859
float b = Vector2.Angle(Vector2.up, new Vector2(-5f, 2f)); //b = 68.19859

So how can you get the angle of a vector between 0 and 360 degrees?

double radians = Mathf.Atan2((y2-y1), (x2-x1));
double degrees = radians * 180f / Mathf.PI;
if(degrees < 0) degrees += 360; // degrees are now between 0 and 360

float angle = (float) degrees; //cast to float if you need to

Just use the correct orientation for (x1, y1) and you're good...

Thursday, August 18, 2011

Life is hard, but so am I

5 years ago I started this blog with a Dresden Dolls quote. Believe me, it was way funnier back then...

The last months weren't easy. I guess brain surgery is never much fun, but being self-employed and the father of a 1-year old kid... well, let's just say that quite a few things were riding on my survival...

With a little less careful planning and a little less luck this could have ended my "career" as an independent developer quite abruptly.

But it seems I made it through and so I'm finally back at work on my next game now.