2011年12月27日火曜日

これなら分かるARC日本語解説リンク

Understanding by ransomtech
Understanding, a photo by ransomtech on Flickr.
iOS アプリ開発の効率化のため、ARC はますます重要となってくるでしょう。
数多ある ARC 解説の中で、個人的に分かりやすかったリンク(日本語)を集めました。

基本の基本。ARCとは何なのか?
[iOS5] ARC (Automatic Reference Counting) : Overview / Natsu's note

プロパティに設定する修飾子の解説。
[iOS5] ARC : プロパティ属性と使い方 / Natsu's note

2011年12月25日日曜日

iOS5のビルトインCIFilterリスト

Filtered heart by Sammy Naas
Filtered heart, a photo by Sammy Naas on Flickr.
iOS 5から使用できるようになった CIFilter の名前と、アトリビュートの範囲を調べるための簡単なコードを書きました。
#import <CoreImage/CoreImage.h>  // CoreImage.frameworkはプロジェクトに追加しておくこと

 // どこかのメソッドの中

 // ビルトインフィルター名表示
 NSArray *filters = [CIFilter filterNamesInCategories:[NSArray arrayWithObject:kCICategoryBuiltIn]];
 NSLog(@"kCICategoryBuiltIn = %@",filters);

 // 各フィルターのアトリビュート表示
 for (NSString *filterName in filters) {
  CIFilter *samplef = [CIFilter filterWithName:filterName];
  NSDictionary *attrs = [samplef attributes];
  NSLog(@"Filter <%@> :\n %@",filterName,attrs);
 }

プログラムを実行すると、例えば CIColorControls フィルターの場合、以下のようにログ出力されます。

2011年12月22日木曜日

AdMaker SDK3.6導入テスト

iOS版 AdMaker SDK 3.6を導入してみました。
ここに来るまでの過去数バージョンのSDKには不具合があり、何回も導入テストで時間を無駄にされられたので、今回もドキドキしております。
半日テストしてみた限りでは、今のところ大きな問題は出ておりません。

導入方法に関しましては、公式の導入マニュアルをご覧ください。

注意点としては、今までアプリ毎に指定していた adURL、siteID、zoneID が、adURLひとつに集約されました。ただし、下位互換性を保つとやらで、siteID、zoneIDには、それぞれ @"111"と@"1111"を指定するようになっています。

2011年12月11日日曜日

PhysicsEditorのNext big update

Physics Immersion by buffalostate
Physics Immersion, a photo by buffalostate on Flickr.

先日、PhysicsEditor について、ドキュメントの同時複数エディットができないと書いた後、開発者にお願いしてみたら、次のビッグアップデートで対応するよ!と返事を頂きました。
ついでにバックカラーの変更もできるようにお願いしたのですが、こちらもOKもらいました。
楽しみだなぁ〜。

PhysicsEditor: http://www.physicseditor.de/

Xcode再インストールトラブル

Xcode4.2 でオーガナイザーの Documentation タブを開こうとするとクラッシュするトラブルがでました。
何度やっても直らないので、Xcode を再インストールすることにしました。

Xcode のアンインストールはターミナルで
> sudo /Developer/Library/uninstall-devtools --mode=all
として、開発環境を全て削除。こちらを参考にさせて頂きました。

ところが、Xcode の再インストール後、起動してみると internal error が出てしまい、起動できません。
しかたなく、クラッシュリポートを読んでいくと、

Couldn't load plug-in 'com.apple.dt.IDE.IDEiPhoneSupport' while firing fault for extension 'Xcode.Device.iPhoneSimulator'

と書かれています。
com.apple.dt.IDE.IDEiPhoneSupport というプラグインが見つからないと言っているようです。早速、この全文をコピーしてググッてみると、こちらの情報を発見しました。
どうやらこのプラグインは、iTunes をインストールすると組み込まれるものらしいです。

Xcode の起動に関わるファイルが、iTunes を再インストールしなければ組み込めないなんて!という疑問を飲み込んで、とにかく先へ進みます…。
http://www.apple.com/itunes/download/ から、iTunes をダウンロードしてきて上書き再インストールしました。

これでめでたく Xcode の起動に成功!\(^o^)/

しかし、Documentation タブを開らくとクラッシュ!_| ̄|○

どうやら、Documentationタブのクラッシュ問題は、原因が違う所にあるようです。なんという、遠回りをしてしまったのでしょう。
気を取りなおして、再調査を開始しました。

きっと、読み込み済みのドキュメントのパスかリンクかファイル自体が壊れているのではないかと推測しました。
Xcode の Preferences パネルを開きます。Downloads タブの Documentation を見てみます。
基本的には以下のようになっていると思います。

人によっては、以前にインストールしたドキュメントが、リストアップされているかもしれません。
今回のクラッシュ問題は、それが原因でした。
過去にインストールしていた iOS 2 のドキュメントのURLなどの情報が null と表示されていて、正常に読み込むことが出来なかったようです。
Preference パネルから削除できれば良かったのですが、(-)ボタンを押しても何も反応しません。
幸いにも、Installed Location 情報は残っていたので、それを頼りに Finder で関連するファイルを削除しました。これに伴い Preference パネルの方でも表示が消えました。
再度、オーガナイザーの Documentation タブをクリックすると、今度はクラッシュせずに正常動作しました。
同様の問題にお悩みでしたら、お試ししてみて下さい。その際は、自己責任でお願いしますね。

2011年12月7日水曜日

PhysicsEditorでシェイプをつくろう

PhysicsEditor(1.0.5)が、同時に1つの書類しか編集できないことが分かりました。(汗)
うーん、今どきすごい仕様です。

こんな出だしでスミマセンが、気を取りなおして、PhysicsEditor でシェイプを作る利点を書いてみたいと思います。

通常、リアルタイム処理を目的とした軽量の物理エンジンの場合、シェイプの形状には制約があります。
Box2D もそうで、Convex (凸)型の形状しかシェイプに登録できません。Concave (凹)型は NG です。
かと言って、それではとても不便なので、Convex 型のシェイプを組み合わせて、あたかも1つのシェイプように Concave 型のシェイプをつくります。

こうなるともう、グラフィックエディタから座標を読み取って…、などという方法ではとてもやっていられません。
PhysicsEditor で、やってしまいましょう。

2011年12月6日火曜日

物理エンジン用シェイプ作りにPhysicsEditor

Box2D アプリを作る時には、b2Body に登録するシェイプデータを作るのがひと仕事です。
丸・三角・四角だったらまだしも、複雑な形状になると、一旦グラフィックエディタなどで図形を描いてから、その座標データを読み取ったりしていました。

PhysicsEditor を使うと、その辺の作業が高速化できます。



2011年11月29日火曜日

NSInvocationでターゲットオブジェクトのメソッドコール

よく、オブジェクトの初期化メソッドで
[[AClass alloc] initWithTarget:self selector:@selector(hello:)];
などとしておいて、後で AClass から [self hello:…]をコールしてもらうということがありますが、これってどうやって実現しているんでしょうか?
方法はいくつかありますが、そのうちの一つが NSInvocation を使うという方法です。
cocos2d の内部でも使われていました。

NSInvocation オブジェクトの作成は以下のようになります。

2011年11月24日木曜日

Dr.Mandala ver.2.1予告

Dr.Mandala ver.2.1 を Apple に申請しました!
遅ればせながら iOS 5 をサポートしました。もうしばらくお待ちください。(公開しました)



Dr.Mandala は僕の最初の iPhone アプリで、ver.1.0 のリリースから、かれこれ2年近く経っております。

Auto モードのヴィジュアルエフェクトが、とても気持ちいいアプリなのですが、スクリーンショット画像だとそれが伝わりません…。

そもそも「回転する模様を眺めて楽しむ」という、ニーズが多くなさそうなものを作ってどうするのだ、という意見もありますが…。
最初の1年半は、酷評されたりもして、かなり落ち込みました。

それから機能を選別していきまして、更に今回はいろいろと決断をしました。

  • 体験時間の制限を削除。
  • サウンドエフェクトの制限を削除。
  • 広告の挿入。(製品版へアップグレードしますと表示されなくなります)

Dr.Mandala は無料でダウンロードできるのですが、製品版へアップグレードして頂きたいために体験時間を制限していました。今回は、それを撤廃しました。サウンドエフェクトの制限も同様です。

また、ヴィジュアルエフェクトとサウンドエフェクトも若干変更して、ちょっとずつパワーアップしています。
以下のスクリーンショットは、Dr.Mandala ver.2.1 の Auto モードプレイ中をスクリーンショットしたものです。
未体験の方は、ぜひ一度 Auto モードをお試し下さいませ!

Dr.Mandala スクリーンショット



2011年11月16日水曜日

cocos2dでテクスチャーループ

cocos2d(1.0.1)でテクスチャーループを使ってみました。
テクスチャーループとは、1つの大きなスプライトの中に、スプライトより小さなテクスチャーをループして貼り付ける方法です。
背景画像に使ったり、色々なところで重宝します。

やり方は、OpenGL ES の知識がちょっと必要ですが、難しくはありません。
今回は、cocos2d テンプレートプロジェクトの Hello World を改造して取り組みました。

テクスチャは以下の4種類の画像を用意します。
左から64×64、64×128、64×100、50×128のサイズです。
4つのサイズのテクスチャを用意
テンプレートプロジェクトを作成したら、CCSprite のサブクラスをひとつ作ります。
ファイル名は Background.m と Background.h にしました。
// Background.h
#import <foundation/foundation.h>
#import "cocos2d.h"

@interface Background : CCSprite {
}
+ (id)backgroundWithRect:(CGRect)rect;
- (id)initWithRect:(CGRect)rect;
@end
// Background.m
#import "Background.h"

@implementation Background

+ (id)backgroundWithRect:(CGRect)rect
{
 return [[[self alloc] initWithRect:rect] autorelease];
}

- (id)initWithRect:(CGRect)rect
{
 if ((self = [super initWithFile:@"worm64x64.png" rect:rect])) {
  ccTexParams params = {GL_LINEAR,GL_LINEAR,GL_REPEAT,GL_REPEAT};
  [texture_ setTexParameters:&params];
  
  self.anchorPoint = CGPointZero;
 }
 return self;
}
backgroundWithRect: メソッドでスプライトのインスタンス化を行います。その際にrect パラメータで、スプライトのサイズとポジションを指定します。

initWithRect: メソッドで、ループテクスチャーの設定を行います。
ccTexParams 変数に、テクスチャーの縦横伸縮方法(サンプルではLinear)、縦横ループ方法(サンプルではRepeat)を格納し、CCSprite の texture_ メンバ変数にテクスチャーパラメータとして設定してやります。これで完了です。

早速表示してみましょう。HelloWorldLayer クラスの init メソッドを以下のように修正します。
// HelloWorldLayer.m
#import "Background.h"
-(id) init
{
 if( (self=[super init])) {
  CGSize size = [[CCDirector sharedDirector] winSize];
  Background *bg = [Background backgroundWithRect:CGRectMake(0,0,size.width,size.height)];
  [self addChild:bg];
 }
 return self;
}
結果は以下のようになりました。テクスチャーのサイズは64×64です。
ちなみにこれをループさせないと、以下のようになります。
テクスチャーの貼付け位置は、スプライト内の左上が起点となっています。
64×128のテクスチャーの場合は以下のようになります。
テクスチャーサイズが2のn乗でない場合はどうでしょうか。
64×100のテクスチャーで試してみました。
縦のループが128(2の7乗)に補正されていますが、テクスチャーは元のままです。

同じく50×128のテクスチャーで試してみます。
横のループが64(2の6乗)に補正されているのが分かります。

このように OpenGL ES では、テクスチャーのループが2のn乗サイズで行われることが分かって頂けたかと思います。

2011年11月14日月曜日

「はっぱ」が42位に!

App Store にて、「はっぱ」が教育・無料部門で42位に入りました。


本当に嬉しいです。ありがとうございます。

記念に、主なレビュー記事をまとめてみました。

iPhone Painters Japan
http://necojarashi.blogspot.com/2011/09/iphone_28.html

Touch Lab - タッチ ラボ
http://ipodtouchlab.com/2011/10/happa-iphone.html

meet-i
http://web.meet-i.com/?type=iphone&q=%E3%81%AF%E3%81%A3%E3%81%B1&x=0&y=0

『iPhone』オススメ情報-.JP-
http://ameblo.jp/shoukai-jp/entry-11035267561.html

iPhone女史
http://www.iphone-girl.jp/application-review/141341.html

AppBank
http://www.appbank.net/2011/11/12/iphone-application/320502.php

ワオっち!
http://waochi.wao.ne.jp/appli/4552.html

2011年11月10日木曜日

iOS5のpresentingViewController

photo-12 by tubes.
photo-12, a photo by tubes. on Flickr.
iOS 5 で UIImagePickerController を使用して、カメラUIを表示させるまでは良かったのですが、これが消せないという問題に遭遇しました。

「iOS カメラプログラミングトピックス」という Apple のドキュメントを参考にしていたのですが、例えば、カメラUIをキャンセルした時のコードは以下のような感じになっています。
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
 [[picker parentViewController] dismissModalViewControllerAnimated:YES];
 [picker release];
}
しかし iOS 5 では、これだとうまく動きません。
[picker parentViewController] からは nil が返ってくるからです。

2011年11月8日火曜日

AdWhirlのCustom Eventを使う

Custom Car June 2011 by NickGrant.co.uk
Custom Car June 2011, a photo by NickGrant.co.uk on Flickr.
AdWhirl で標準対応していない広告サービスを使うには、Custom Event という仕組みを使います。
使い方について需要がありそうなので、書いてみます。

実はめちゃくちゃ簡単です。
ただし、AdMaker なら AdMaker SDK を、AdLantis なら AdLantis SDK を単独で組み込んで使用できるスキルは必要です。(笑)

AdWhirl の管理画面で、Custom Event を登録します。
Apps の Ad Network Settings で、Add Custom Event というボタンを押します。

AdWhirlとAdStirを比べる

Advertising by Wrote
Advertising, a photo by Wrote on Flickr.
先日リリースしたばかりの iPhone アプリには、AdMaker(mediba ad) を組み込んでいたのですが、これがほとんど広告を配信してくれない状態が続いています。
こういう事があるかもしれないと前々から思ってはいたのですが、最悪のタイミングで起こってしまいました。

それで広告切り替えシステムを導入する気になりました。
試したのは2つ。AdWhirl(3.0.0) と AdStir(1.0.2) です。
AdWhirl は Google 傘下の AdMob のサービスで、SDK はオープンソースになっています。
AdStir は ngi group のサービスで、最近始まったばかりのようです。SDK はフレームワークのみの提供です。

2011年11月5日土曜日

ショートゲーム Worrrm

iPhone/iPod touch 用ゲームの Worrrm をリリースしました。



プレイヤーが標的の中に居続けると、得点が加算されるというシンプルなゲームです。
1回のプレイ時間が短く、ほんのちょっとした時間や気晴らしにプレイしていただくことができます。

遊び方

デバイスを傾けた方向にプレイヤーが移動します。敵ワームに触れないように標的の中に入って下さい。ゲームスタートです!

標的の中心に近いほど、得られる得点が高くなります。ですが、敵ワームに囲まれてしまうリスクも高くなります。

時々現れる緑色のワームがアイテムを置いて行きます。
アイテムを取ると、短い時間、チョウチョに変身することができます。
その間に敵ワームに触れると、高得点ゲットです!

Game Center に対応していますので、他のプレイヤーと得点を比較することができます。

アプリ基本情報

アプリ名:Worrrm
価格:無料
ダウンロードURL:http://itunes.apple.com/jp/app/worrrm/id468015254?mt=8



2011年11月2日水曜日

はっぱ、バージョン1.3

葉っぱが舞い落ちる、お絵かきアプリ「はっぱ」のバージョン1.3がリリースされました。
今回は蝶が追加されて、更に画面が華やかになりました。

更新内容

  • 新しいキャラクター「蝶」が追加されました。
  • iOS5に正式に対応しました。

アプリの基本情報

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

アプリの概要

描いた絵の上に、葉っぱが舞い落ちるお絵かきアプリです。
お絵かきした線の上に、舞い落ちる葉っぱが積もり、キャンバスをカラフルに飾ってくれます。
お子様はもちろん、絵の苦手な大人の皆様もぜひお絵かきを楽しんでみて下さい。

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 モデルのテクスチャーを作成したり、画像処理プログラムの勉強をしたりと、いろいろな使い道がありそうです。

Related Posts Plugin for WordPress, Blogger...