2011年10月31日月曜日

Worrrm Preview動画

現在、アプリ申請中(リリースされました!)の iPhone ゲーム「Worrrm」
Wormにrが2個追加されちゃったのがゲーム名になっています。

プレイヤーが標的の中に居続けると得点が加算されるという、シンプルだけどついついハマってしまうゲームです。
よろしくお願いいたします。m(_ _)m

プレイ動画を作ってみましたが、なんかホームムービーチックになってしまった。(汗)

カスタムジェスチャーを作ろう!

iPhone アプリでジェスチャーを使うというと、特殊なアプリを想像しがちですが、メインで使うのではなく、ゲームの裏コマンド入力や、隠し機能、デバッグモードのオープンなどに積極的に使ってみるのはどうでしょうか。

オリジナルのジェスチャーを作るには、UIGestureRecognizer のサブクラスを作成して実装します。UIGestureRecognizer の詳細については、Apple の Event Handling Guide for iOS ドキュメント、またはこちらの日本語PDFをご覧ください。

UIGestureRecognizer は状態マシンとして機能する抽象クラスです。
ユーザーがこのクラスに関連付けられたビューをタッチすると、そのイベントがこのクラスにリダイレクトされます。そのタッチイベントを読み取って、ジェスチャーの進行中・失敗・成功の状態を遷移させるのがこのクラスの役目となります。

UIGestureRecognizer のサブクラスは、以下の5つのメソッドをオーバーライドします。

- (void)reset;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
reset 以外は、UIResponder のタッチメイベントそのままなので、馴染み深いでしょう。

ジェスチャーには大きく分けて、単発的なジェスチャーと、連続的なジェスチャーがあります。今回は、ジェスチャーの中でも比較的簡単な単発的なジェスチャーを扱います。
単発的なジェスチャーでは、タッチイベントの中でジェスチャーが確定したと判断できたら、state プロパティを UIGestureRecognizerStateRecognized に設定します。逆に失敗したと判断したら、UIGestureRecognizerStateFailed を設定します。
非常に簡単です。

公式ドキュメントには、チェックマーク(✔)ジェスチャーを実装する例がありますので、ここではもうちょっと複雑に以下のような三角旗ジェスチャーを作ってみました。

ヘッダファイルは以下のような変数を定義しておきます。
@interface FlagGesture : UIGestureRecognizer
{
 unsigned int actMode;  // ジェスチャー管理用ワーク
 CGPoint startPoint;      // ジェスチャー開始座標
 CGPoint topPoint;        // 三角旗の天井座標
}
touchesBegan:メソッドは以下のようになります。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
 [super touchesBegan:touches withEvent:event];
 if ([touches count]!=1) {
   // マルチタッチを感知したら失敗とする
  self.state = UIGestureRecognizerStateFailed;
  return;
 }
 // ジェスチャー開始座標格納
 startPoint = [[touches anyObject] locationInView:self.view];
}

touchesMoved:メソッドは以下のようになります。
actMode が 0 の時は、ジェスチャーが開始座標から旗の天井に向かうところを判定します。ここは垂直に上に伸びるジェスチャーなので、15°以上傾いたら失敗させるようにしています。
actMode が 1 の時は、ジェスチャーが旗の右端突端に向かうところを判定します。
actMode が 2 の時は、右端突端からジェスチャー開始地点まで戻るところを判定します。
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
 [super touchesMoved:touches withEvent:event];
 
 CGPoint nowPoint = [[touches anyObject] locationInView:self.view];
 CGPoint prevPoint = [[touches anyObject] previousLocationInView:self.view];
 
 switch (actMode) {
  case 0:  // 旗の天井に向かうアクション
   if (nowPoint.y<prevPoint.y) {
    float rot = atanf(fabsf(nowPoint.x-startPoint.x)/(startPoint.y-nowPoint.y));
    if (rot>=15) {
     // 15°以上傾いたら失敗
     self.state = UIGestureRecognizerStateFailed;
     return;
    }
   } else {
    if (nowPoint.x>prevPoint.x) {
     CGRect rect = self.view.bounds;
     // 垂直線が画面の高さの3分の1あるかを判定材料とする
     if ((startPoint.y-nowPoint.y)>=(rect.size.height*0.33f)) {
      // 旗の右端突端に向かうジェスチャーになったと仮定する
      actMode = 1;
      topPoint = nowPoint; // 旗の天井座標格納
      break;
     }
    }
    self.state = UIGestureRecognizerStateFailed;
   }
   break;
   
  case 1:  // 旗の右端突起に向かうアクション
   if ((nowPoint.x>=prevPoint.x)&&(nowPoint.y>=prevPoint.y)) {
    // OK
   } else if ((nowPoint.x<prevPoint.x)&&(nowPoint.y>=prevPoint.y)) {
    float dy = startPoint.y-topPoint.y;
    if (((topPoint.y+dy*0.35f)>nowPoint.y)||((startPoint.y-dy*0.35f)<nowPoint.y)) {
     // 突端のY座標が旗の垂直線の中央にないと失敗とする
     self.state = UIGestureRecognizerStateFailed;
     return;
    }
    actMode = 2;
   }
   break;
   
  case 2:  // 開始座標に戻るアクション
   if ((nowPoint.x<=prevPoint.x)&&(nowPoint.y>=prevPoint.y)) {
    // OK
   } else {
    self.state = UIGestureRecognizerStateFailed;
   }
   break;
 }
}

touchesEnded:メソッドは以下のようになります。
タッチが離れた時、actMode が 2 であり、且つ開始座標に近ければ、ジェスチャーが成功したとします。
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
 [super touchesEnded:touches withEvent:event];
 
 if ((self.state == UIGestureRecognizerStatePossible)&&(actMode==2)) {
  CGPoint nowPoint = [[touches anyObject] locationInView:self.view];
  CGPoint delta = CGPointMake(nowPoint.x-startPoint.x,nowPoint.y-startPoint.y);
  float k = delta.x*delta.x + delta.y*delta.y;
  if (k<(50*50)) {
   self.state = UIGestureRecognizerStateRecognized;
  }
 }
}

touchesCancelled:メソッドは以下のようになります。
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
 [super touchesCancelled:touches withEvent:event];
 self.state = UIGestureRecognizerStateFailed;
}

reset メソッドは以下のようになります。
ジェスチャーが失敗・成功後に自動的に呼ばれるメソッドです。管理用ワークを初期化しておきます。
- (void)reset
{
 [super reset];
 actMode = 0;
}

カスタムジェスチャークラスができたら、実際にビューに登録してみます。
以下の例では、ビューコントローラーの viewDidLoad メソッドから登録しています。 viewDidUnload メソッドでは、登録したジェスチャーを取り除いています。
ジェスチャーの登録・削除は、各アプリで必要な箇所に組み込む必要があるでしょう。
- (void)viewDidLoad
{
 [super viewDidLoad];
 FlagGesture *gesture = [[[FlagGesture alloc] initWithTarget:self action:@selector(handleGestureFlag:)] autorelease];
 [self.view addGestureRecognizer:gesture];
}

- (void)viewDidUnload
{
 [super viewDidUnload];
 NSEnumerator *e = [self.view.gestureRecognizers objectEnumerator];

 while (UIGestureRecognizer *gesture = (UIGestureRecognizer*)[e nextObject]) {
  [self.view removeGestureRecognizer:gesture];
 }
}

- (void)handleGestureFlag:(UIGestureRecognizer*)sender
{
 NSLog(@"FlagGesture OK! : state = %d",sender.state);
}

今回は、ちょっと長くなってしまいました。ぜひ、ご自分用の楽しいジェスチャーを作ってみて下さい。

2011年10月26日水曜日

Blocksを使ってUIViewアニメーション

Children's Blocks by lobo235
Children's Blocks, a photo by lobo235 on Flickr.
iOS 4.0 から Blocks が使えるようになって、UIView アニメーションにも Blocks が使えるようになりました。
UIView アニメーションにBlocks が使えると、とても便利なのですが、iOS 3 デバイスをサポートする関係上、なかなか使用する機会がありませんでした。
今回初めて使用してみたので、その使い方を書いてみたいと思います。

Blocks の詳細については、ドキュメントが日本語化されているので、そちらをご覧下さい。

UIView アニメーション Blocks 以前
複数のアニメーションを直列につなげて実行させたい場合、beginAnimations:context: と commitAnimations メソッドを使用すると、以下のように一つのアニメーション毎にメソッドを用意してやったりします。これだと見通しが悪いし、繋げるアニメーションが多いと絶望的に面倒くさいです。
// 一番最初のアニメーション開始場所
- (void)startAnimation
{
  someView.frame = CGRectMake(0,-40,480,40);

  CGContextRef ctx = UIGraphicsGetCurrentContext();
  [UIView beginAnimations:nil context:ctx];
  [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
  [UIView setAnimationDuration:0.5];
  [UIView setAnimationDelegate:self];
  [UIView setAnimationDidStopSelector:@selector(endAnimation1)]; // 次のアニメーション開始メソッド

  someView.frame = CGRectMake(0,0,480,40);
 
  [UIView commitAnimations];
}

// 前のアニメーションの終わりに呼ばれるメソッド
// ここが次のアニメーションの開始場所
- (void)endAnimation1
{
  CGContextRef ctx = UIGraphicsGetCurrentContext();
  [UIView beginAnimations:nil context:ctx];
  [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
  [UIView setAnimationDuration:3.0];
  [UIView setAnimationDelegate:self];
  [UIView setAnimationDidStopSelector:@selector(endAnimation2)];

  someView.frame = CGRectMake(0,1,480,40);

  [UIView commitAnimations];
}
// ・・・以下続く

UIView アニメーション Blocks 以後
対して、animateWithDuration:animations:completion: メソッドだとどうでしょうか?
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
animations: の後にアニメーション処理をブロックで、completion: の後にアニメーション後の処理をブロックで指定します。
複数のアニメーションを直列につなげる場合は、以下のようにします。
UIView *someView = ビューをインスタンス化してローカル変数に退避;
someView.frame = CGRectMake(0,-40,480,40);

[self.view addSubview:someView];

[UIView animateWithDuration:0.5
     animations:^{someView.frame = CGRectMake(0,0,480,40);}
     completion:^(BOOL finished) {
[UIView animateWithDuration:3.0
     animations:^{someView.frame = CGRectMake(0,1,480,40);}
     completion:^(BOOL finished) {
[UIView animateWithDuration:0.5
     animations:^{someView.frame = CGRectMake(0,-40,480,40);}
     completion:^(BOOL finished) {
[someView removeFromSuperview];
}];  }];  }];
ずいぶん見通しが良くなりました。
まあ、最後の括弧が見ため悪いですが、一連のアニメーションを一つのメソッド内で記述できてしまうのは楽です。

さらに特筆すべきは、メソッド内のローカル変数(someView)にブロック内からもアクセスできるという点です。
詳しい仕組みは Blocks のドキュメントに譲りますが、大雑把に言うとブロック内からアクセスするローカル変数(スタック上にある変数)はコピーされて保存され、そのブロックが生きている限り保持されるようです。更にヒープ領域にあるオブジェクトには、retain して保持するようです。(ブロックの消滅とともに release するもよう)

最後に、上記2つ目アニメーションで、Y座標を1だけ動かすのに3秒を割り当てているのは、ウエイトの代わりです。ここを仮に CGRectMake(0,0,480,40)のままにしたり、何も命令を記述しないと、3秒を待たず直ぐに completion ブロックが起動されてしまいます。

2011年10月24日月曜日

Xcode4.2でのDistributionビルド

Worrrm という iPhone/iPod touch 用ゲームを Apple に申請しました。
シンプルにして、短時間で遊べます。
ついついもう一回プレイしたくなるところを狙っていますが、さてどうなることでしょう?
今回、初の Xcode 4シリーズでのアプリ登録だったので、ビルド設定等でつまづいたところをチェックしてみます。

  • Architectures に armv6 を追加し忘れないようにする。デフォルト設定では $(ARCHS_STANDARD_32_BIT) と変数が使われていて分かりづらい。これが armv7 ということらしいですが、では、armv6 は何と記述すればいいのかと悩んでみれば、単に armv6 で良かったというオチ。

  • 静的ライブラリ (cocos2dなど) を別ターゲットでビルドしている場合、その静的ライブラリの Deployment の Skip install を「YES」にする。これを忘れると Validate 前に「does not contain a single–bundle application or contains multiple products」と怒られる。

  • Distribution 用ビルドは、Product → Archive で行う。ビルドが成功すれば オーガナイザーが起動され、Archives タブで Validate チェック及び、Submit が行える。


Xcode 3 と比べて特に注目すべきところは Application Loader を使わなくなった点と、Distribution 用ビルドが Archive という名前にまとめられて、分かりやすくなったところでしょうか。
あとはドキドキしながら Review を待つという点では、以前から何も変わりありません。

2011年10月23日日曜日

libSystem.B.dylibってなんだろう

これまで作ってきた iPhone アプリは、何とか iOS 3 デバイスでも動くように作ってきました。
しかし、現在開発中のアプリを iOS 3 デバイスでテストしてみると、以下のようなメッセージがコンソールに出て停止してしまいます。
dyld: Symbol not found: __NSConcreteStackBlock
  Referenced from: 〜省略〜
  Expected in: /usr/lib/libSystem.B.dylib
NSConcreteStackBlock ってなんですか?
なんだろうと思って調べたら、Blocks が原因だと分かりました。

Blocks 引用
ブロックオブジェクトは、C言語レベルの構文によるランタイム機能です。標準Cの関数に似ていま すが、実行可能なコードのほかに、自動(スタック)メモリまたはマネージド(ヒープ)メモリに バインドされている変数を含むことができます。したがって、ブロックでは、実行時の動作に影響 を与える状態(データ)セットを維持できます。
Blocks は iOS 4 から導入されました。
今回、初めて自アプリで GameKit を使用する際に Blocks を使っていました。

GameKit フレームワークは iOS 3デバイスには無いので、その際、無視出来るように、リンク設定で Optional (Xcode 3時代のWeakと同じ意味らしい) に設定していましたが、 どうやらこの libSystem.B.dylib も Optional に設定しないといけないらしいです。
設定方法は、下記画像を参考にして下さい。

libSystem.B.dylib を Optional にすると、iOS 3デバイスでも実行することができました。

とは言え、もうそろそろ iOS 3 が乗っている古いデバイスでは、処理速度的に対応が難しくなってきました。次回のアプリからは iOS 4 以上を対象にしたいと思っています。

2011年10月21日金曜日

Filter Forge 2.0

みんな大好き、パンダ!

Filter Forge というグラフィックにフィルターをかけるツールがあります。
Photoshop プラグインとして利用できますが、単体アプリとしても使用出来ます。
このツールは単にグラフィックにフィルターをかけるだけでなく、そのフィルターを作成できるエディターがついています。
そのフィルターの作り方が、Apple の QuartzComposer に似ていて、ちょっと面白いです。
エディター画面
エディターは、小さな機能をパイプで繋げて、最終的に複雑なフィルターを作成できるようになっています。複雑なものはそれだけレンダリングにも時間がかかります。

Filter Forge には web上にある沢山のフィルターをダウンロードする仕組みがあって、それを使って見るだけでも結構楽しめます。

その中から、いくつかを紹介したいと思います。
一つ目は、Mess Painter というフィルター。
油絵風な効果がステキです。ちなみに写真の枠もフィルターで作成されています。

同じく Mess Painter のパラメータ変化バージョン。
ガラっと雰囲気が変わりました。

おなじみ LOMO カメラ風フィルター。
暗い所がつぶれて、青味がかった写真になります。

これは Bad Trip というフィルター。
まさに悪夢…。

Shark Tank! というフィルター。
パンダが深海にダイビングしています。

写真に付加するフィルターだけでなく、いわゆるジェネレーター系のフィルターも充実しています。

これは Spiral Galaxy。
銀河中央のボケ具合がすばらしい。

変わりダネとしてはこれ。その名も Filter Force (笑)
あなどってはいけません。ソードの柄の部分もなんとフィルターで作られています。
エディターでみると、その努力に敬服します。どうやって作られているかは、ご自身の目でお確かめ下さい。



Filter Forge 2.0 は 30日間のトライアル版が、サイトよりダウンロードできます。
トライアル期間中の機能は、無制限に解放されているようです。

また、僕の環境だけかもしれませんが、Mac版の Filter Forge 2.0 をインストールしたところ、アプリケーションフォルダーに作られる Filter Forge フォルダのアクセス権が無く、インストールできてもツールを立ち上げるすべがありませんでした。その場合、Filter Forge フォルダを読み書きできるように、Finder でアクセス権を変更する必要があります。(最近、こんなのばっかり…)

インストール直後、フィルターは最小限のものしかインストールされていないので、サイトからダウンロードする必要があります。
Filter Forge サイトから、Filters タブを選び、自分の好きなフィルターのページへ行きます。そこから「Open this filter in Filter Forge」をクリックして下さい。
最初はメッセージが表示され、「Click 〜」というところをクリックすると Filter Forgeが起動され、フィルターのダウンロードを開始します。
この辺は、環境によって動作が変わると思いますが、メッセージを読んで対応していけば問題ないと思います。

Filtersのページ 

この Filter Forge というソフト、3D モデルのテクスチャーを作成したり、画像処理プログラムの勉強をしたりと、いろいろな使い道がありそうです。

2011年10月20日木曜日

iOS5のleaderboard問題、その後

iOS 5 の sandbox 環境で、leaderboard を見るとスコアが表示されない、という問題について3回目のポストです。

結論から先に言いますと、大丈夫かもしれない。(笑)

まず最初に、この問題は sandbox 環境だけでなく、live 環境でも起こりうるかもしれません。ですが Apple のスタッフがはっきり答えてくれた訳ではないので、なんとも言えません。

Apple Developer Forum の中で、Apple のスタッフが「この場所は Apple のオフィシャルサポートチャンネルではないが」と前提を置いて語ってくれたことによると、一つの leaderboard に一つのアカウントがスコアを登録した時に、この問題が起こるのではないかということでした。

そこで、3つの sandbox 用アカウントを追加で作って、合計4つのアカウントでスコアを登録してみました。
通常ですと、サーバーの負荷を軽減するため、他人のアカウントのスコアがすぐに反映される訳ではありません。Game Centerも同じようです。
追加で作った3つのアカウントでは、自分のスコアは即、leaderboard に反映されました。しかし、一番最初のアカウントでは、自分のスコアを見ることがまだできません。

ですが一夜明けて、一番最初のアカウントで leaderboard を確認すると、自分のスコアを含めて、他の3つのアカウントのスコアを見ることができるようになっていました。
これは、前日の段階では他の3つのアカウントのスコアが、一番最初のアカウントからは確認できず、自分も含め全てのスコアが表示されなかったということなのでしょうか?
そうだとすれば、Apple のスタッフが言っていたことが証明されたことになります。

真相は分かりません。
この問題は iOS 4.3 では出ていなかったので、iOS 5 に移行したデベロッパーの中には、混乱した人も多くいると思います。
私、もしくはあなたのアプリが、たった一人のユーザーしか獲得できないなら、この問題は重大なものになるかもしれません。いえ、私、もしくはあなたが、誰よりも早くスコアを登録しておけば、問題にすらならないかもしれません。

2011年10月19日水曜日

Xcode4.2のコンソールウィンドウの開け閉め

Xcode 4.2 のコンソールウィンドウが、勝手に開いたり閉じたりするのがちょっと厄介に感じていたので、環境設定の Behaviors タブで設定しました。

僕はデバッグ実行中も、実行が終わった後もコンソールウィンドウのログをよく調べるので、常に開いていて欲しいと思うタイプです。その設定方法を以下に示します。

まず、Run Starts の「Show debuger with Console View」にチェックを入れます。
Run starts

それから、Run Completes の「if no output, hide debugger」がデフォルトでチェックが入っていると思いますので、これを外します。
Run completes
以上です。

Behaviors タブには、その他にも Xcode 上で行ったユーザーアクションに合わせて、自動実行させるアクションが山ほどあります。
便利なものを見つけて活用して下さい。

やっぱりInterface Builderは信用できない?

バギーな Xcode 4.2 にやられたお話しです。

iOS 5 の Storyboard を試すために、Xcode 4.2 でサンプルプロジェクトを作りました。
テンプレートから Tabbed Application を選び、とりあえず iPhone/iPad の Universal 対応にしました。
この時点で一回ビルドして、シミュレーター上で動作を確認しました。
ソースコードがほとんど空にも関わらず、画面遷移するアプリケーションが出来上がりました。
楽だなと思いつつ、若干の不安も覚えます。極度にブラックボックス化された環境で、バグや問題が起きると、何が起きてるのかを追うのにとても苦労するからです。

MainStoryboard_iPhone.storyboard を Interface Builder で開いて、2つ表示されるビューのうち1つを改造して、UITableView を配置しました。
UITableView の delegate、dataSource を UIViewController に接続して、必要なメソッドも追記します。

自分のミスもあり、何度かビルド&実行を繰り返しましたが、UITableView にデータが表示されません。 最初は自分のミスかと思い、色々調べてみると、くだんの UITableView と IBOutlet 接続した myTableView 変数が nil を示しています。なのに UITableView 本体は表示されています。この時点で???となりました。
myTableView 変数をチェックしたのは viewDidLoad メソッド内なので、アウトレットの接続は済んでいるはずです。

まったく分からないまま途方にくれて、2つ目のビューにも UITableView を配置してビルド&実行しました。
するとなんと!変更が反映されておらず、編集前のビューが表示されています。
Interface Builder 上では、もちろん UITableView が配置されています。
2つのUITableViewを配置したところ
 もうこれは Xcode を疑った方が良いと思い、Product -> Clean を行い、DerivedData フォルダを削除しました。
それでも直りません…。
そして今度は、iOSシミュレータ上にインストールされている、このアプリ自体を削除しました。ゴミが残っているかと思ったからです。
すると今度は、以下の UIApplicationMain 部分で、実行時エラーがでました。
int main(int argc, char *argv[])
{
 @autoreleasepool {
     return UIApplicationMain(argc, argv, nil, NSStringFromClass([TestTableAppDelegate class]));
 }
}
そもそも TestTableAppDelegate クラスはテンプレートのままなので、自分にバグを挿入できる余地はありません。

いろいろな実験結果から、MainStoryboard_iPhone.storyboard ファイルのキャッシュか何かが、悪さをしていることは明らかです。
そこで、MainStoryboard_iPhone.storyboard をリネームして、MainStoryboard.storyboard にしました。
Universal アプリもやめて、iPhone 専用にしました。
以下のように設定します。

これでビルド&実行すると、めでたく Storyboard を編集した通りの表示が行われました。私の1日を返して下さい…。

2011年10月18日火曜日

iOS5のStoryboardサンプル

まだよくわからない iOS 5 の Storyboard。
セグエってなんだ?
CustomとModalとPushってなんだ?

画面遷移をInterface Builder上で設定できるようになったのが Storyboard。
以下のサイトからサンプルプロジェクトがダウンロードできます。

iPhone Tutorial One — Introduction to Storyboarding

このプロジェクトを Xcode 4.2 で開いて、MainStoryboard.storyboard (くどいネーミングだw)を覗くと、なんとなく Segue の Modal については分かってきました。

UIButton のターゲットをドラッグして直接 UIViewController にくっつけると、ボタンを押した時の画面遷移先として指定することができます。この時に、Custom、Modal、Pushを選ぶことができます。
Modal は上記のサンプルでも使用されていて、前の画面から新しい画面に完全に切り替わります。Custom はいろいろカスタムできるということなのでしょう。(笑)
Push はまだよく分かりませんが、名前からするとスタック形式の画面遷移なのでしょうか?

たくさんある画面(UIViewController)を管理するのには重宝しそうですね。

iOS5のleaderboardバグ?

先日ポストした、iOS 5 の leaderboard の挙動の件についてですが、Apple Developer Forumでの見解によると、どうも Apple のバグくさいぞという感じになってきました。
一番嫌なパターンです。

しかも leaderboard でスコアが表示されない問題は、sandboxモードに留まらず liveモードでも起きているとも言われています。

ストレスが溜まるのは、この問題について Apple が正式にレポートと解決策を示さない限り、Game Center 対応アプリの 新規 submit は待った方がいいかもしれないという事です。
せっかく新作を投入したのに、leaderboard が不完全なままでは、アプリの出だしとしては良くないでしょうからね。

この問題のその後をポストしました。

2011年10月17日月曜日

AdMaker SDK 3.4導入

AdMaker SDK 3.4がリリースされました。

更新履歴については今のところ表示されていないようです。

新規に組み込まれる場合は、SDK に入っているサンプルと Readme.txt を読めば特に問題無く組み込めると思いました。注意点追記しました。

3.3 から 3.4 へ SDK を更新する場合には、以下の点に注意するといいと思います。
  • AdMakerView クラスが UIViewController から UIView のサブクラスに変わった。これは 3.1 の時は UIView のサブクラスだったので、一周して結局また元に戻ったということになります。(笑)
  • 上記変更により viewWillAppear と viewWillDisappear メソッドが使えなくなるかと思いきや、互換性を重視してかメソッドは残っている。しかしドキュメントには何も記述されていない。
  • AdMakerDelegate の didLoadAdMakerView メソッドは、広告が読み込み直される度にコールされるようになった。(これは嬉しい) 
  • 内部ブラウザで広告を開く場合、AdMaker.useSafari = NO にする。しかし、 requestAdURL:(NSURLRequest*)request メソッドが呼ばれるだけで、それ以外のアクションは起こらない。内部ブラウザは自分で用意せよということなのかも?
  • AdMakerView をインスタンス化して start メソッドで開始させてから、インスタンスを解放しようとするとクラッシュする。SDK 添付のサンプルプログラムを改造して確認しました。
クラッシュさせるサンプルコードは以下のようになります。
ビルド設定の ARC は OFF にします。
設置したボタンをタップすると、AdMakerView をリリースします。
// SingleViewViewController.m
- (void)viewDidLoad
{
  [super viewDidLoad];
  AdMaker = [[AdMakerView alloc]init];
  [AdMaker setAdMakerDelegate:self];
  AdMaker.viewController = self;
  AdMaker.useSafari = YES;
  AdMaker.backgroundColor = [UIColor clearColor];
  [AdMaker setFrame:CGRectMake(0,360,320,50)];
  [self.view addSubview:AdMaker];
  [AdMaker start];

  UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
  [button setTitle:@"end AdMaker" forState:UIControlStateNormal];
  button.frame = CGRectMake(85,50,150,40);
  [button addTarget:self action:@selector(endAdMakerTouched:) forControlEvents:UIControlEventTouchDown];
  [self.view addSubview:button];
}
- (void)endAdMakerTouched:(id)sender
{
  NSLog(@"endAdMakerTouched");
  if (AdMaker) {
    // 注:この時点でAdMakerのリファレンスカウンタは2になっているので release を忘れないこと!
    [AdMaker removeFromSuperview];
    [AdMaker release];
    AdMaker = nil;
  }
}
クラッシュに関してはサポートに問い合わせてみるつもりです。
今回の SDK バージョンは iOS 5 に対応したと書いてあったので、もしかしたらインスタンスの解放を Automatic Reference Counting 任せにしているのかもしれません
とはいえ、すべてのプログラムが ARC の設定を ON にしているわけでもないので、そういう仕様は厳しいのではないかと思いますが、どうでしょうか。

2011年10月15日土曜日

iOS5のsandbox leaderboardの挙動

iOS 5 になって、Game Center 対応の開発中のゲームで、leaderboard の表示がおかしくなりました。
Appleのフォーラムでも、同様の問題を抱えている人がいるようでした。
現在、情報の収集中です。

開発中なので Sandbox 環境なのが前提です。

スコアを Game Center に送信し、それが成功した後に、GameKit 標準の leaderboard ビューを見ると、以下のように「スコアがありません」と出てしまいます。

でも、スコアはちゃんとあるんです。
 GKLeaderboard クラスの loadScoresWithCompletionHandler メソッドでスコアを直接取得してみると、正常にスコアを取得できました。
そこから、GameKit 標準の leaderboard ビューに問題があるのではないかと推測しているのですが、さて、どうでしょうか?

ちなみに iOS 4.3 の iPhone Simulator では、正しく表示されます。

この問題のその後をポストしました。

2011年10月13日木曜日

Xcode4への移行 [Destinationトラブル編]

Xcode 3 で作ったプロジェクトを Xcode 4.2 へ移行した時に、アクティブターゲットが「My Mac 64bit」になって、iPhoneが選択できなくなってしまうことがあります。

そういう時は、ビルド設定がうまく引き継がれていない可能性があります。
まず、ワークスペースウィンドウからスキームをエディットしてみて下さい。
以下のようなウィンドウが出ます。
Destination が My Mac 64bit になって iPhone に変更できない時は、Buildのターゲットが「0 Target」になっている可能性があります。
まずそこで Build ターゲットを追加してあげます。

それでも Destination に iPhone が選択できない場合は、Build Setting の Base SDK を Mac OSに設定してから、iOSに設定しなおすと直ることがあるらしいです。
(※参考サイト: http://akisute.com/2011/04/xcode-4-scheme-my-mac-64bit-iphone.html)

Art Text 2で透過PNGを使う

Art Text 2(ver.2.4)で、透過PNG形式の画像を読み込んでみます。

まず、レイヤーに適当なシェイプを配置します。
そのシェイプに画像を貼りますので、四角いシェイプを選ぶとよいでしょう。
シェイプの「塗り」をテクスチャに合わせ、Finderから透過PNGファイルをドラッグアンドドロップします。

白い背景は実際は透過しています。

こんな感じになりました。
画像は、次回公開予定のiPhoneゲーム「Worrrm」のアイコンです。(笑)
見事に透過しているのがわかります。

よさげなので、ファイルにエクスポートしてみましょう。
白い部分は実際は透過しています。
あれ? 透過部分がおかしいですね?
どうやら ver. 2.4 でのバグみたいです。早速、メーカーにフィードバックしましたが、日本語でメールを送ったので、通じてるのかどうかわかりません。(汗)
今のところ、何も返事はないです。

これだと困るので、なんとか抜け道はないのでしょうか?
あります!

シェイプの「シャドウ」スイッチがOFFの場合、上記のようなバグが出るので、これを ON にしてやります。
シャドウを表示させたくない場合は、以下のように距離、角度ともに0に設定し、さらにシャドウカラーを透明に設定してやれば大丈夫です。(実際は、シャドウカラーを透明にするだけで良いと思います。ここでは念には念を入れてこのように設定しました)

これでファイルにエクスポートすると、見事、透過部分が正しく処理されます。
透過PNG画像をご使用の際は、ぜひお試し下さい。

BeLight Software Art Text 2

Art Text 2 は上記ホームページからデモ版をダウンロードすることができます。
また、MacのApp Storeで購入が可能です。

2011年10月12日水曜日

Xcode4への移行 [ローカライズ編]

Xcode 4 における、リソースファイルのローカライズ化は、Xcode 3と同じく簡単です。むしろもっと分かりやすくなったと言えるかもしれません。

ワークスペースウィンドウ左の Project Navigator からローカライズしたいファイルを選びます。
既にローカライズされているとこのように表示される。

それからワークスペースウィンドウ右上の「Hide or show the Utilities」スイッチを押して、ユーティリティーパネルを表示させます。
その中で、Localization の+ボタンを押して、追加したい言語を選択します。

以上です。

ただし、Xcode 3から移行してきたプロジェクトが、既にローカライズされていたとすると、ちょっと問題が起きるかもしれません。

Xcode 3 でリソースファイルを日本語にローカライズすると、そのファイルは Resources/Japanese.lproj フォルダに格納されていると思います。
新たに Xcode 4 で別のリソースファイルを日本語にローカライズすると、そのファイルは Resources/ja.lproj フォルダに格納されます。
どうやら、日本語ローカライズファイルの格納先が変更されたようなのです。渋い変更です。
英語ファイルは、以前と変わらず English.lproj のままでした。

さて、このままだと2つの日本語ローカライズフォルダが存在してしまいます。
このまま実行するとどうなるのでしょうか?

僕の環境では、Xcode 3 で作成した日本語ローカライズファイルが無視されました。iPhone が日本語環境になっていても、英語ローカライズファイルが選択されてしまいます。
Xcode 4 で作成した日本語ローカライズファイルは、正しく読み取れました。

問題点はもっと深いのかもしれませんが、とりあえずの対処を行わなければなりません。
もっと簡単な方法があるのかもしれませんが、僕は以下のようにして対処しました。
  1. Xcode 3 で作成したローカライズファイルをひとまず安全なところへ退避
  2. 日本語ローカライズファイルをプロジェクトから削除(英語の方は残す)
  3. Xcode 4 で英語ファイルを日本語ローカライズ化する
  4. 退避しておいたローカライズファイルを上書き

2011年10月11日火曜日

Xcode4への移行 [Provisioning編]

本日、iPhoneアプリ開発証明書の期限が切れたので、証明書を作りなおしました。
それに合わせて、開発用 Provisioning Profile(以下、Profile)も作り直さないといけません。

Profile を作成し、後は iPhone へ転送するだけになりました。
この作業は何度もやっていますが、Xcode 4でやるのは初めてです。

オーガナイザーから古い Profileを削除して、新しいものをインストールします。
ですが、読み込まれた Profile の Status が「Valid singing identity not found」となってしまいます。
作成日、期限日を見ても正しいですし、もちろん AppID も正しい。
何がいけないのかまったく分からず、おたおたするばかり…。

そこで使い慣れた Xcode 3 のオーガナイザーを起動して iPhone にインストールされている Profile を見ると、新しく入れたものの他に、古いものもまだ残っているじゃあないですか!
どうやらこの辺りが問題を起こしていると推測しました。
やりきれなさを胸に秘め(笑)、Xcode 3 のオーガナイザーで全ての Profile を一旦削除。新しい Profile をインストールし直しました。
すると Xcode 4 のオーガナイザーでも正しく認識してくれて、無事一安心。

同様の現象にお悩みでしたら、一度試してみる価値はあるかもしれません。

2011年10月10日月曜日

はっぱ、バージョン1.2

葉っぱが舞い落ちるお絵かきアプリ「はっぱ」のバージョン1.2がリリースされました。

パンプキ〜ン!
今回の更新では、
  • 画用紙が選択できるようになりました。
  • iPhoneの傾き方向に合わせて、葉っぱが舞い落ちるようになりました。

今後とも、はっぱの応援をよろしくお願いいたします。m(_ _)m

アプリの基本情報

アプリ名:はっぱ (海外名称:Leaves Paint)
価格:無料
ダウンロードURL:http://itunes.apple.com/jp/app/id464291736?mt=8

CCMenuのisTouchEnabled

CCMenu の isTouchEnabled を NO に設定すれば、その中のすべてのボタンを効かなくする事ができるので便利です。

しかし、CCMenu 内に入れたボタンのタッチメソッド内で、その CCMenu の isTouchEnabled を NO にして、さらに UIAlertView を表示すると、BADアクセスでクラッシュしますね。

タッチイベントが終わる前に UIAlertViewを表示することで、cocos2d内で整合性がとれなくなってるのかな? どのボタンのタッチメソッド中かによって、状況は変わるかもしれませんが…。
何はともあれ、isTouchEnabled を YES のままにしておけば問題ありませんでした。

2011年10月9日日曜日

presentModalViewControllerを使う

UIViewの上に、一時的に別の画面(UIView)を表示させたい時、おおまかに二通りの方法が考えられると思います。
  • 表示中の UIView に addSubview メソッドで新しい UIView を載せる。
  • UIViewController の presentModalViewController:animated: メソッドを使って、新しいUIViewController と入れ替える。
どちらを使うのが良いのかは、UIをどうするかで判断すると良いと思います。

下層の画面と UI を一時的にOFFにして、新しい画面を表示させたい場合は、presentModalViewController:animated: メソッドを使用すると便利です。
animated を YES とした場合のトランジション中にも、下層の UI の反応が無くなっているようなので、面倒な管理を必要としません。(かなり重要)
また、UIViewController を生成する時に、インスタンスを autorelease プールに入れておけば、dismissModalViewControllerAnimated: メソッドで元の画面に戻った時に、自動でインスタンスが解放されます。

実装としてはこんな感じになります。
// 新しいViewをpresentModalViewControllerで表示
// autorelease しておくと便利!
OtherViewController *controller = [[[OtherViewController alloc] initWithNibName:@"OtherViewController" bundle:[NSBundle mainBundle]] autorelease];
[rootViewController presentModalViewController:controller animated:YES];

ユーザーの操作などで、元の画面に戻るには以下のようにします。
// modalViewを破棄して、元の画面に戻る
[rootViewController dismissModalViewControllerAnimated:YES];
cocos2d を使ってゲームなどを作っている場合でも、ゲームの操作説明には UIWebView などの UIKit を使って表示させたい事は多いと思います。そういう時に、presentModalViewController:animated: メソッドの利用は便利だと思いました。

2011年10月7日金曜日

Xcode4への移行 [git編]

Xcode 3 の頃からソース管理には git を使ってきました。
その場合、ターミナルから git コマンドを入力して使用していました。
別にこれで何の不満も無かったのですが、Xcode 4 になって Xcode 上から git を使用できるようになりました。

リポジトリはワークスペースウィンドウではなく、オーガナイザーから管理します。
Window メニューから Organizer を起動し、Repositories を選択します。
左のリストに管理しているリポジトリが表示されます。左下の+とーボタンで、リストへの追加、削除ができます。あくまでもオーガナイザーへの追加・削除なので、削除したからといってリポジトリが消えることはありません。
更新したファイルをコミットするには、管理したいリポジトリのフォルダ名ボタンを押し、ウィンドウの下に表示される Commit ボタンを押します。すると更新したファイルがリスト表示され、コミットするかどうかを選択できます。

しかし、表示がバグる時があるので注意です。
僕の環境(Snow Leopard,Xcode 4.1)だと、ファイルが何も表示されない時があって、その場合、一旦 Flat Viewボタンを押すと、ファイルが表示されるようになりました。

またファイル名左側のチェックボタンをON/OFFする事で、コミットするかしないかを選択できるのですが、Flat View と File View モードで、チェックが入ったり外れたりすることがあります。以下のような感じです。
FIle View (階層表示)
Flat View
これは怖い!

オーガナイザーで管理しつつも、今まで通りターミナルから git コマンドを使用できるので、しばらくは両方で確認しつつ運用しようと思います。なんだかなぁ(笑)

それから、Xcode 4 になって、リポジトリ管理しないファイルが増えたので .gitignore ファイルに記述しました。
この辺りの情報は、ググればいっぱい出てきますが、僕の場合はこんな感じにしています。
*.xcodeproj/*.mode1v3
*.xcodeproj/*.pbxuser
*.xcodeproj/project.xcworkspace   <-- これと下のファイルが Xcode 4 で追加された 
*.xcodeproj/xcuserdata                  <-- 
*.xcodeproj/.LSOverride  <-- Xcode 3と4でプロジェクトを開くと生成されるらしい
build  <-- Xcode 3 までの実行ファイルなどビルド生成ファイルの格納先
DerivedData  <-- Xcode 4 からはこっちが生成ファイルの格納先
.DS_Store <-- Finder の例のアレ
xcworkspace ファイルは ignore しちゃいかんという人もいますが、何か知っていましたら教えて下さい。

2011年10月6日木曜日

Xcode4への移行 [駆け出し編]

これまで Xcode 3.2.5 を使ってきましたが、10月12日に iOS5 がリリースされると聞いて、直後に慌てないように今のうちに Xcode 4 へ移行しておこうと考えました。

Xcode 4 は Xcode 3 から比べると、とんでもなく変わっていて、ちゃんと理解できるまで大分時間を要しそうです。

しかし、先人たちのドキュメントも増えてきましたので、それらを参考にしていければと思います。

今回は駆け出し編として、Xcode 3 で作った cocos2d用プロジェクトを Xcode 4で読み込んで、ビルド&実行するまでを解説します。cocos2d用プロジェクトは git のリポジトリに入っているものとします。

1. Xcode 4.1 をダウンロード
僕の環境は Snow Leopardなので、xcode_4.1_for_snow_leopard.dmg というファイルを Dev Center からダウンロードしました。

2. Xcode4移行ガイド日本語訳
http://sazameki.jp/translations/xcode4/IDEs/Conceptual/Xcode4TransitionGuide/
Xcodeのバイナリは4.3GBとデカイので、ダウンロード完了を待つ間、移行ガイドの「既存の Xcode 3 用のプロジェクトの使用」を読んでおきましょう。

3. インストール
dmgファイルの中のパッケージをインストールすると完了と思いきや、新しいXcodeが見当たりません。アプリケーションフォルダに Install Xcode というアイコンが作成されているので、これをダブルクリックして実行させます。
これで、旧 Xcode 3 関連のファイルは、/Developer から /Developer-3.2.5 へ移動され、Xcode 4 関連のファイルが /Developer にインストールされます。

4. Xcode 4 起動
Xcode 4 のアイコンが Xcode 3 のアイコンとまったく同じなのでビビります。

5. git リポジトリの clone 作成
Xcode の Window メニューから Welcome to Xcode を選択して下さい。
そこで、Connect to a repository を選択します。
リポジトリのフォルダ、もしくは URL を指定して下さい。
そして、clone 先のフォルダ名を指定します。この時、フルパスではなく、フォルダ名だけでいいです。どこに保存するかは、この後指定することになります。
Typeタブから、Git か Subversion を選択できます。ここでは Git を選択します。
cloneボタンを押して終わりです。

6. 環境設定のDerived Data生成フォルダを変更
Xcode の環境設定パネルを開き、Locations を選択します。
Derived Data の設定が Default になっていると思います。
このままだと、ビルドして生成される作業ファイルや実行ファイル群が、~/Library/Developer/Xcode/DerivedData/ 以下に生成されてしまいます。
この設定が嫌なので、Relative に設定して、プロジェクトのワークフォルダ以下に生成させるようにします。合わせてGit リポジトリに含ませないように .gitignore ファイルに、DerivedData フォルダを追記しました。
デフォルト設定。これは嫌!
Relativeに変更。なぜこれがRecommendedでないのか?
ちなみに僕の環境下ですと、~/Library/Developer 下のフォルダの所有者がすべて root になっていました。僕自身が変更した覚えはないので、Xcode インストーラーか何かのバグだと思います。しかし、Derived Data の環境設定がデフォルトのままですと、ビルド中に「error : unable to create ファイル名」とかいうエラーで停止してしまいます。
原因が分かれば、なんの事はないのですが、これはちょっとハマリポイントとして挙げておきます。
気持ち悪かったので、chown コマンドで所有権を自分にもどしておきました。
> sudo chown -R username ~/Library/Developer

7. ビルドセッティング
プロジェクトのビルドセッティングを確認している時、以下のようなものが表示されました。
ひとつは、PREBINDING と GCC_ENABLE_FIX_AND_CONTINUE の設定は古いから外すよという確認をせまっているようです。
もうひとつは、プロジェクトフォーマットが Xcode 3.1コンパチなので、Xcode 3.2コンパチへアップグレードするよという確認です。
両方ともOKなので、Perform Changes ボタンを押します。

すると今度は、こんなメッセージが現れました。(笑)
プロジェクトに大きな変更をした時に、自動的にスナップショットをとるかどうかの確認のようです。これは git リポジトリへコミットするということなのでしょうか? まだよく分からないのと、実際、自動でバックアップをとられるのは好きではないので、Disable を選択しました。

8. ビルド&実行 
ワークスペースウィンドウの左上の Run ボタンをクリックして、ビルド&実行します。

とりあえず今回はこれまで!

2011年10月5日水曜日

UIパーツ作成に便利なArt Text 2

ロゴ、アイコンやボタンなど、リッチなグラフィックパーツを簡単に作成できてしまうツールが Art Text 2 です。

使い方はそれほど難しくなく、豊富なテンプレートギャラリーからファイルを開き、そこから改造するなり、どういう構成要素で出来ているのかひとつずつ確かめていけば、いずれ自分の思い通りのパーツを作る事ができると思います。
豊富なテンプレート (ボタン)
豊富なテンプレート (ロゴ)
テンプレートを選ぶと、エディットウィンドウが開きます。
そこで選んだロゴやボタンなどの構成要素が確認できます。
構成要素は主にベクター形式のシェイプで、それらを組み合わせて複雑な図形を作ります。シェイプのカラーも自由に選択できます。単色、グラデーション、テクスチャーなど豊富なプリセットが用意されています。自分が作成した画像ファイルを読み込ませることもできます。
エディット画面
テキストなどのシェイプを歪ませるのもお手の物。あっという間に動きのあるロゴが作成できてしまいます。
テキスト変形
完成したパーツは画像ファイルとして書きだすことも出来ますが、クリップボードへコピーする事もできます。これは、Photoshop や Pixelmator などの画像ソフトとの連携にすごく便利です。

このツール、Web用素材作りはもちろんのこと、iPhoneアプリのUI素材作りにも重宝しそうです。特にアプリの個人開発をされている方にとっては、UI素材にはいつも頭を悩まされていることと思います。そんな時には、Art Text2 をお試しになってみて下さい。

BeLight Software Art Text 2

Art Text 2 は上記ホームページからデモ版をダウンロードすることができます。
また、MacのApp Storeで購入が可能です。

2011年10月3日月曜日

iTunes Connect with Firefox 7.0.1


iTunes Connect に In App Purchaseのレビューに使う画像を送りたかったんですが、使用ブラウザが FIrefox 7.0.1だと connect errorが出てしまう現象がでました。

最初、Apple側のサーバーのトラブルかと思ってずーっと復旧を待ってたんですが、あまりにも長いので、もしやと思い Safari を使ったら問題なく送れました。
同様のトラブルで悩んでいる方は、一度試して見ることをお勧めします。

Firefox 6の時は、このようなトラブルには見舞われたことはありませんでした。
iTunes Connect には Safari が安全なのかもしれません。

Cocos2dでLandscapeモード

Cocos2dで画面を横向き(Landscape)固定にするには、おおまかに二通りあります。
  1. UIViewController を使う
  2. CCDirector を使う
UIViewController を使う方法は、UIKitおなじみ下記メソッドで目的のOrientationのみ、YESを返します。
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
 return ( interfaceOrientation == UIInterfaceOrientationLandscapeRight );
}

CCDirector を使う方法は、AppDelegateの下記メソッドなどで、目的のOrientationをCCDirectorに設定します。
- (void)applicationDidFinishLaunching:(UIApplication*)application
{
 [[CCDirector sharedDirector] setDeviceOrientation:kCCDeviceOrientationLandscapeLeft];
}
CCDirectorを使って設定する時に注意しなければならないのは、先の shouldAutorotateToInterfaceOrientation メソッドでは、UIInterfaceOrientationPortrait の時にYESを返さなければいけないという事と、CCDirectorに設定している kCCDeviceOrientationLandscapeLeft 値は UIDeviceOrientation と同じで、Landscapeモードの場合は UIInterfaceOrientation のLeftとRightが反対の意味になるということです。(UIDeviceOrientationとUIInterfaceOrientationの関係)

どちらが良い方法かと言えば、それは作るアプリによると思います。
例えば AdMaker などの広告を、自身の作成したViewに重ね合わせる時、広告のOrientation は、その UIViewController に依存します。
GameKit フレームワークのデフォルトViewは、すべて GameKit の UIViewController を使用するので、どちらの方法を使おうとも Orientation は正しく判断されます。

Related Posts Plugin for WordPress, Blogger...