ラベル iPhone OS 3.0 の投稿を表示しています。 すべての投稿を表示
ラベル iPhone OS 3.0 の投稿を表示しています。 すべての投稿を表示

2010年11月16日火曜日

iOS で正規表現を使う (3.0, 3.1, 3.2, 4.0)

iPhone / iPad アプリで正規表現を使いたいときはどうするのか調べてみました。


■iOS 4.0以上

NSRegularExpression があるのでそれを使えば万事解決です。


■iOS 3.2

http://blog.livedoor.jp/pnfhy316/archives/277806.html
こちらのブログでご紹介されているとおり、NSString rangeOfString:options: で NSRegularExpressionSearch を指定するのが楽です。

正規表現のグループ参照はできませんけれど、まぁしょうがないですかね。


■iOS 3.1, 3.0

http://d.hatena.ne.jp/KishikawaKatsumi/20081031/1225463896
こちらのブログでご紹介されているとおり、ライブラリを使うのがよいようです。

または最近調べてわかったのですが、 NSPredicate を以下のように使うと正規表現によるマッチングが可能になる模様です。
NSString *path = @"/path/to/img/img10001.png";
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ matches '.*/img[0-9]{5}\\.png'", path];
BOOL matched = [predicate evaluateWithObject:nil];
キモは NSPredicate の format として matches という構文が使えることと、その中で正規表現が使用できるところですかね。実機の iOS 3.X で試したわけではないのですが、 NSPredicate 自体が iOS3.0 から使用可能になっていることと、 NSPredicate format 関連のドキュメントを見ても特に利用制限とかは書いていなかったので、普通に3.0から使えるんじゃないかと思ってます。もしよろしければ動作報告いただけるとうれしいです。


■iOS 2.X

iOS界のIEだと思って忘れましょう。お客さんに「頼むから対応してくれ」と言われても突っぱねる(すでに全体の1%以下ぐらいのはず)のがベストです。

2009年9月1日火曜日

多少マシになったかな




相変わらずこんなしょぼっちい物を作っていたりします。


■前回からちょっとだけ改善
敵が出るようになりました(キャラの向き滅茶苦茶ですが)。
弾が出るようになりました(タダの自機狙い3Wayですが)。
ちょっとずつデザエモン程度には近づいてきたかなぁと。
あとは自機が動いて弾が出れば一応STGっぽくはなります。

今回作ったところまでの経過をgithubにうpしてみました。
http://github.com/akisute/MyShooting/tree/338e164b4716ec27a6a9b8d384f7714243f53d2e

最新のソースはこちら。
http://github.com/akisute/MyShooting/tree/master


■今回の気づき
弾の発射とか敵の出現タイミングをフレーム単位で計測して実行しているのですが、cocos2dのフレーム時間はかなりばらつくようです。弾が200発程度出ただけでフレーム時間が2倍になったりします。要するに弾の発射間隔が2倍になります。ちょっとこれはいただけない感じ。毎フレームごとに前フレームから何秒経過したかが取得できるので、それを元に時間単位で発射タイミングを測った方がいいかもしれません。

あとはActionがやたら重いです。何が重いってActionの動き自体よりもActionオブジェクトの作成と解放に時間がかかっている感じです。頻繁に起動が変わったり単純直線移動しかしない動きを再現するとき(たとえば高速弾や自機コントロール)は、昔ながらのvx, vyを使った方法のほうが軽くて良さそうです。

2009年8月29日土曜日

どのクラスのインスタンスを作っているかに注意しましょう・・・

突然ですが問題です。以下のソースはコンパイルできますがバグがあります。
http://github.com/akisute/MyShooting/blob/4570825e7384157d3572b689cc23f9bb3acc0b82/Classes/MSTGObjectFactory.m

さて、どこが間違っているでしょうか?




■なんて突然言ってもわからないですが
正解はこちら。
+ (MSTGObject *)cannon
{
// ここが間違い。MSTGObjectではなくてMSTGCannonのインスタンスを生成する必要がある
MSTGObject *object = [MSTGObject spriteWithFile:@"cannon.png"];
return object;
}
で、MSTGObjectはライフがデフォルトゼロなので、生成された瞬間に即死して画面から退場してくださるという悲しいバグが。うーむJavaではこんな情けないバグ作ったりはしないのですが・・・ともかくこれで一歩前進。

イニシャライザとかコンポジットとか考えた結果がこれだよ!

前回の記事から引き続きCocos2Dでちょこちょこアプリを書いてます。
ソースコードも公開してみました。
http://github.com/akisute/MyShooting/tree/master


■で、あれからどうなった
全面作り直しorz

コーディングを進めていくうちに次々と問題点が噴出してきたのですが、その中でもこれはまずいと思ったのが2点。


■問題点その1.オブジェクトリストの管理が面倒
画面への描画を担当してくれるCocosNodeにSpriteを登録するのとは別にオブジェクトを管理するためのマネージャクラスを持っていたため、オブジェクトを追加するたびにこんなコードを書かなくちゃいけなかったんです。
// MSObjectの親ノード(Layer)の中で・・・
MSObject *object = [[[MSBullet alloc] initWithPosition:ccp(100,100)
angle:0.0
speed:200.0] autorelease];
[self addChild:object.node];
[[MSObjectManager sharedInstance] addObject:object
forOwner:kMSObjectOwnerEnemy];
これだけならまだしも、今度は消すのが面倒。
[self removeChild:object.node];
[[MSObjectManager sharedInstance] removeObject:object];
どちらかを消し忘れるとエラーになりますし、ループ中にMSObjectManagerからインスタンスを消すとエラーになったりと結構散々です。この問題点1はまだCocosNodeにaddMSObject:forOwner:とかremoveMSObject:みたいなメソッドを追加すればなんとか対応できたのですが、もっと深刻な問題点2が。


■問題点その2.オブジェクトクラスの中でscheduleが使えない
これで完全に積みました。要するにこんなコードが書けないわけです。
// MSObjectの中で
// 毎フレームごとに呼び出されるメソッドを定義する
- (void)onFrame:(ccTime)dt
{
// 死亡判定してみたり
// 衝突判定してみたり
// ライフを回復したり無敵フレームを設定してみたり
}

- (id)init
{
if(self = [super initWithFile:@"ship.png"])
{
// sceduleメソッドはCocosNodeのメソッドなのでこうやって登録する
// しかしschedule出来るのはCocosNode自身が持っているメソッドのみ
// そのためこの方法でメソッドを登録しても
// 実行時に「spriteにはonFrameなんてメソッドがありません」とエラーになる
[self.sprite schedule:@selector(onFrame:)];
}
return self;
}
これを回避するにはMSObjectにスケジューラを自分で実装する必要が出てきて・・・ってそれもうCocos2dでやる意味なくね?ということで結論。Cocos2dはCocosNodeを継承して使え。


■作り直しの結果
MSObjectをコンポジットではなくSpriteのサブクラスにして解決。ついでに名前をMSTGObjectに改名しました。イニシャライザが漏れてややこしいと言う問題が発生してしまいますが、そこは以下のようにファクトリクラスを使って解決。
MSTGObject *ship = [MSTGObjectFactory ship];
MSTGObject *bullet = [MSTGObjectFactory bullet];

2009年8月8日土曜日

cocos2d細かいところメモ

cocos2dを使っていて適当に気づいたところとかメモしてみます。

■ログ出力
CCLOGというマクロccMacros.hで定義されています。
#ifdef DEBUG
#define CCLOG(...) NSLog(__VA_ARGS__)
#else
#define CCLOG(...) do {} while (0)
#endif
注意点としてDEBUGがdefineされていないと使えません。


■ベクトル演算とか角度変換とか
CGPointを拡張してベクトル演算をするためのメソッドが追加されています。なかなか便利です。たとえばこんな感じ。
    // 二つのベクトルのdot(内積)とlength(ベクトルの長さ)を計算してcosθを求める
float dot = ccpDot(lastAccerelometerVector, convertedVector);
float a = ccpLength(lastAccerelometerVector);
float b = ccpLength(convertedVector);
float cosTheta = dot / (a*b); // 注意:a*bが0だとゼロ除算で死にます。真似しないでね><
// 角度→ラジアン変換用のマクロ。逆ももちろんあります
float threshold = cos(CC_DEGREES_TO_RADIANS(120.0));
Chipmunkにも同様のメソッドがあります。お好きな方をご利用いただけますが、個人的には全部cocos2dでやる方が好きです。唯一の問題は、こんなメソッドを使っているとベクトル内積の計算式を忘れます。


■音を出力する方法
最初からCocoa Touchで用意されているAudio Queue Service, OpenAL, AVAudioPlayer, Audio Unitに加えて、さらにcocos2dについてくる音再生用ライブラリとして
  • CocosDenshionとSimpleAudioEngine
  • sound-engine
これだけたくさんの中から選択できます。今回は一番簡単なSimpleAudioEngineという奴を使ってみました。
        // prefetch sound resources
SimpleAudioEngine *audioEngine = [SimpleAudioEngine sharedEngine];
[audioEngine preloadEffect:@"bell.aif"];
[audioEngine preloadEffect:@"gong.aif"];
[audioEngine playEffect:@"gong.aif"];
たったのこれだけです。playEffectでサウンドエフェクトが、playBGMでBGMが再生できます。同時再生数とか使用可能なファイルタイプとか細かいところは不明ですが、そこそこの量が同時に出せるみたいです。
注意点としてSimpleAudioEngineはCocosDenshionを使用しています。CocosDenshionのライセンスはcocos2dと独立しており、こちらは年間2500ドル以上の利益が出ている場合には500ドルのライセンス料を徴収するとかなんとかそういう条項があります。(http://www.cocos2d-iphone.org/wiki/doku.php/cocosdenshion:licenseを参照)


今はこのcocos2dの上にどのような構造でアプリを組んでやろうかとか考えてます。・・・あー、こんなこと考えてるからいつまで経っても本題のアプリが完成しないんだー ><

2009年8月7日金曜日

cocos2d for iPhone Project Template v0.8 真似して作ってみました



cocos2d導入キット(http://d.hatena.ne.jp/Seasons/20090511/1241990196)としてid:Seasonsさんが公開されているキットを元に、私もcocos2d用のプロジェクトテンプレートを作ってみることにしました。id:Seasonsさん、すばらしいキットをありがとうございます!

今回の作業にあたり参考にしたページはこちら。
http://d.hatena.ne.jp/griffin-stewie/20090315/p1

作成したテンプレートはgitに公開いたしましたので、よろしければ使ってみてください。
http://github.com/akisute/cocos2d-xcode-template/tree/master


■インストール方法
downloadタブから0.8をダウンロードしてきて、解凍したファイルを以下のパスに配置してください。
~/Library/Application Support/Developer/Shared/Xcode/Project Templates
配置後、.gitと.gitignoreファイルを削除してください。削除しないと後からテンプレを使って新しいプロジェクトを作ったときに、これらのファイルがコピーされてしまいgitを使おうとしたときに問題が発生する可能性があります。


■テンプレートの内容
id:Seasonsさんが公開されている0.8beta用のテンプレートを元に、一部自分の気になった箇所を自分好みに修正しました。また、使用するcocos2dのバージョンを0.8betaから0.8リリース版に変更しました。一応ビルドして画面が出るところまでは確認しています。ただし、全機能をテストしたわけではないので、一部不具合があるかもしれません!
また注意点として、簡素化のため元のcocos2dや外部ライブラリに付随していたREADMEやドキュメント、LICENSEファイルなどをすべて削除してしまっています。おそらくは大丈夫だと思いますが、ライセンス的に問題が発生するかもしれません。大変申し訳ありませんが、このテンプレートを利用した際に生じる一切の不具合について当方では責任を負いきれませんのでご了承ください。


■id:Seasonsさんのテンプレートから変更したところ一覧
  1. 使用するcocos2dのバージョンを0.8リリース版にした。
  2. プロジェクトのグループ構成およびディレクトリ構成を変更した。この変更により、本来システムが利用する/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Project Templates/Application以下ではなく、ユーザーのライブラリである~/Library/Application Support/Developer/Shared/Xcode/Project Templates以下にテンプレートを配置しても動作するようになった。
  3. MenuSceneとGameSceneが最初のテンプレとして用意されていたのを統廃合し、MainSceneひとつだけにした。
  4. ビルドの設定を必要に応じて変更し、またGCC_PREPROCESSOR_DEFINITIONSにDEBUGを追加した。これにより最初からデバッグビルド時にCCLOGマクロが正しく機能する。
  5. ソースコードのインデントがまちまちになっていたのを、すべてApple式のインデントに修正。
  6. ソースコード中から日本語コメントをすべて除去し英語コメントに置換。
  7. 一部使用されていたdepreciatedメソッドを除去。[[Director sharedDirector ] setDeviceOrientation:CCDeviceOrientationLandscapeRight];を使うようにした。


■思わぬ収穫
この作業によってXcodeの仕組みとテンプレートの作り方、ビルドの仕方などにかなり詳しくなることができました。たとえば・・・
  • Xcodeのテンプレートファイルは単なる普通のXcodeプロジェクトと何ら変わらない。そのままビルドして実行することもできる。
    唯一異なる点が、xcodeprojバンドルの中に、TemplateIcon.icnsとTemplateInfo.plistというファイルが存在し、これらを変更することでテンプレートとしての設定を変更することが出来る点。
  • libhogehoge.a(静的ライブラリ?)の作り方。ターゲットに新規ターゲットを追加して、必要なソースをぽいぽい投げ込むだけ。antでbuild.xmlを書くより簡単。
  • ___PROJECTNAMEASIDENTIFIER___.pchの配置パスを変更したときは、ターゲットのビルド設定のPrefix Headerの値を書き換える必要がある
  • 同様に、___PROJECTNAMEASIDENTIFIER___-Info.plistのパスを変更したときも、ターゲットのビルド設定のInfo.plistファイルの値を書き換える必要がある
  • main.mは何処に置いておいても大丈夫らしい

ということで、Xcodeプロジェクトのテンプレートを作成するのは凄く勉強になります。皆さんも一度自分好みのテンプレートを作成してみてはいかがでしょうか?

2009年8月4日火曜日

cocos2d for iPhoneをためしてみました



YourTurnが一段落したので、今度は前々から一度やってみたかったゲームにチャレンジしてみようと思い、cocos2d for iPhone(http://code.google.com/p/cocos2d-iphone/)を使い始めました。cocos2d for iPhoneとは、iPhone上で簡単に2Dのゲームを作成するためのフレームワークです。FPS管理、アニメーション、パーティクル、シーンの管理およびアニメーション付きの切り替え、メニュー、タッチおよび加速度イベントの管理など、ゲームに必要な要素をほぼすべて網羅してくれています。さらにオプションとしてBGMやサウンドエフェクトの管理と、2D物理エンジンも用意されており、まさに至れり尽くせりといった感じです。

まずは手始めにチュートリアルをやってから、Tic Tac Toe(いわゆるタダのマルバツゲーム)を作ってみました。
ソース全体 http://github.com/akisute/Cocos2DTest/tree/master
ヘッダ部分のみ http://gist.github.com/160244
実装部分のみ http://gist.github.com/160246

この程度の内容のソースでパーティクルも作れるし音も鳴らせるしで、本当に簡単です。ここまで簡単だとRPGツクールより簡単かもしれませんw
今までゲームプログラミングの経験が全然無い人のとっかかり用としては最適ではないでしょうか?

2009年7月19日日曜日

YourTurn 1.1 サブミットしました

YourTurn 1.1をApp Storeにサブミットしました。現在レビュー待ちです。
http://wiki.github.com/akisute/YourTurn


■そもそもこのアプリ何?
学会タイマーアプリといえばわかりやすいと思います。要するに参加者ごとに時間を決めて割り振り、時間が来たらタイマー音がなる、それだけのアプリです。





2分程度のビデオデモを作ってみましたので、よろしければご覧ください(無音ですけど・・・)。


順調にレビューが進めば2週間後の8月上旬にはストアに並ぶと思います。無料、というかソースも全部公開してます。気が向いたらプレゼンしたりするときにでも使ってやってください。

iPhone OS 3.0のTableViewでscrollEnabledをNOにするとtableView:didSelectRowAtIndexPath:が機能しない

http://forums.macrumors.com/showthread.php?t=470266

OS2.2のころはscrollEnabledをNOにしても正常にdidSelectRowAtIndexPathが動いていたので、これは正直結構困ります。仕方がないのでscrollEnabledを使うのを回避して、スクロールするように戻しました。OS 3.1では直っているとよいのですが。

iPhone付属のPhotos(写真)アプリのような、回転可能な全画面表示ビューを作る方法



Photos(写真)アプリに使われている、全画面ビューを真似して作ってみました。具体的には以下のような仕様になります。
  • ステータスバーの後ろも含め、ビューの内容が全画面(320x480)で表示される
  • 画面をタップするとステータスバーとナビゲーションバーが消える
  • もう一度タップすると再度表示される
  • iPhoneを傾けると画面が回転する
参考にしたページは以下のとおり。
http://life.ponpoko.tv/?p=533

また、サンプルソースも公開してます。こちらのソースの、- (void)fullScreenMode:(BOOL)mode animated:(BOOL)animatedがフルスクリーン表示を実現しているメソッドになります。


■注意
iPhone OS 3.0以上専用です。2.2以下では別の方法を取る必要があります。


■まずはStatus BarとNavigation Barを消す
http://life.ponpoko.tv/?p=533 こちらのページに書いてある内容を100%そのままパク参考にさせていただければ何も問題ありません。iPhone OS 2.2までは記載されている通りのworkaroundが必要になりますが、3.0以降ではバグが修正されているため、単にStatus BarとNavigation Barを消せばOKです。

ただし一点、[self.navigationController setNavigationBarHidden:YES animated:YES];を使うと、ナビゲーションバーが上方向に対してぴょこんと引っ込むようなアニメーションになります。ステータスバーはその場でフェードアウトして消えるので、なんだか不自然な感じに見えます。Photos.app(写真アプリ)など、iPhone標準のアプリと同様にナビゲーションバーをフェードアウトさせて消したい場合には、UIViewのアニメーション機能とalpha値を利用して以下のようにします。
    if (animated)
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
}
self.navigationController.navigationBar.alpha = mode ? 0 : 1;
if (animated)
{
[UIView commitAnimations];
}
Three20プロジェクトのソースから拝借してきました><


■Viewを全画面配置する(Status BarとNavigation Barのあった場所にも描画させたい)
さんざん悩んだあげく、実はUIViewControllerにそのためのプロパティがあったと知り、ものすごく簡単に解決しました。
- (void)viewWillAppear:(BOOL)animated
{
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleBlackTranslucent;
self.navigationController.navigationBar.barStyle = UIBarStyleBlackTranslucent;
// このUIViewController.wantsFullScreenLayoutをYESにするとフルスクリーン表示になる
self.wantsFullScreenLayout = YES;
}
ついでにステータスバーとナビゲーションバーを透明にして裏が透過するようにしてみました。


■ビューの回転に対応したい
何も考えずにshouldAutorotateToInterfaceOrientationすればいいだろう常考
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
とおもったら思わぬ落とし穴が。確かに全画面表示にした状態でビューを回転させたり、逆にナビゲーションバーが出ている状態でビューを回転させたりする分にはこれだけで全く問題ないのですが、
  1. ナビゲーションバーを消す(全画面表示にする)
  2. iPhoneを傾けてビューを回転させる
  3. 再度ナビゲーションバーを表示する
という手順を踏むと、再度表示したときにナビゲーションバーが崩れる問題が発生。結局ナビゲーションバーを消したり再表示したりする際に、以下のようなworkaroundをするハメに。
    // Force set the frame of the navigation bar
CGRect frame = self.navigationController.navigationBar.frame;
frame.origin.y = 20.0;
self.navigationController.navigationBar.frame = frame;


■全画面表示をしている画面から前の画面に戻るとバグる問題
これで完成かと思えば、最後にもう一つ落とし穴が。
  1. iPhoneを横向きに傾けて、Landscape表示にする
  2. ナビゲーションバーを表示する
  3. そのまま横向きになっている状態で、ナビゲーションバーの戻るボタンを押して、前の画面に戻る
  4. 前の画面に戻る際に、wantsFullScreenLayoutをNOにして通常画面モードにし、ステータスバーとナビゲーションバーのスタイルもDefaultに戻す
という風な操作を行うと、なぜかナビゲーションバーのスタイルだけがDefaultに戻らず、表示も崩れるという問題が発生しました。たぶんOS 3.0のバグだと思います。
いろいろ試してみた結果、全画面表示をしている画面ではなく、前の画面のviewWillAppearのタイミングで以下のコードを呼びだせば問題が解決しました。
- (void)viewWillAppear:(BOOL)animated
{
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleDefault;
self.navigationController.navigationBar.barStyle = UIBarStyleDefault;
}

CGGradientを用いてUITableViewCellを描画し、テーブルをカッコよく見せる方法

デフォルトのUITableViewCellの背景が白くてのっぺりでいまいち味気ないと思い、背景にグラデーションを付けてかっこよく見せる方法を調べてみました。単純に別途用意した背景画像をbackgroundViewに表示してもよいのですが、Cocoa Touchの2Dグラフィックスライブラリにはグラデーションを描画するためのCGGradientというクラスが最初から用意されています。さっそく私もパクってインスパイアされてやってみました。

参考にしたページはこちら。
http://developer.apple.com/documentation/graphicsimaging/conceptual/drawingwithquartz2d/dq_shadings/dq_shadings.html#//apple_ref/doc/uid/TP30001066-CH207-TPXREF101


■どこで描画するか
  • UITableViewCellのdrawRectで直接描画。
    少しでも高速に描画したい場合にはこの方法
  • 新規にUIViewを継承した背景用Viewを作成しセルのbackgroundViewに設定。そのViewのdrawRectで描画
    複数のUITableViewCellで同じ背景を適用したいときはこの方法が便利

■まずは実際に描画してみる
drawRectの中でCGContextを作成して、続いてCGGradientを生成。CGGradientを作るためにはCGColorSpaceとか色を指定する配列とかが必要になるのでそれらも生成。最後に生成したCGGradientオブジェクトを描画するという流れになります。ということでソースを見てみましょう。
- (void)drawRect:(CGRect)rect
{
// CGContextを用意する
CGContextRef context = UIGraphicsGetCurrentContext();

// CGGradientを生成する
// 生成するためにCGColorSpaceと色データの配列が必要になるので
// 適当に用意する
CGGradientRef gradient;
CGColorSpaceRef colorSpace;
size_t num_locations = 2;
CGFloat locations[2] = { 0.0, 1.0 };
CGFloat components[8] = { 1.0, 1.0, 1.0, 1.0, // Start color
0.79, 0.79, 0.79, 1.0 }; // End color
colorSpace = CGColorSpaceCreateDeviceRGB();
gradient = CGGradientCreateWithColorComponents(colorSpace, components,
locations, num_locations);

// 生成したCGGradientを描画する
// 始点と終点を指定してやると、その間に直線的なグラデーションが描画される。
// (横幅は無限大)
CGPoint startPoint = CGPointMake(self.frame.size.width/2, 0.0);
CGPoint endPoint = CGPointMake(self.frame.size.width/2, self.frame.size.height);
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);

// GradientとColorSpaceを開放する
CGColorSpaceRelease(colorSpace);
CGGradientRelease(gradient);
}
このコードを実行すると・・・


はい!もうグラデーションができました。嘘みたいに簡単です。

2010/05/24追記:注意点として、CGGradientとCGColorRefのオブジェクトは手動でリリースしないとメモリリークが発生します!


■UIColorを元にグラデーションを作る
先ほどの例では配列にRGBA要素を渡してグラデーションを作りましたが、UIColorが使えるともっとお手軽で、しかもRGBAだけではなくてHSBAで色が指定できて何かと便利です。ということで、次はUIColorからグラデーションを作ってみます。

UIColorから直接CGGradientを作ることは出来ないので、途中でCGColorとCFArrayRefを作り、それを元にCGGradientを生成してみます。コードはこちら。
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
UIColor *currentColor = [UIColor colorWithHue:currentBackgroundColorHSBA[0]
saturation:currentBackgroundColorHSBA[1]
brightness:currentBackgroundColorHSBA[2]
alpha:currentBackgroundColorHSBA[3]];
// gradient background
CGGradientRef grad;
CGColorSpaceRef colorSpace;

// UIColorからCGColorを取り出すのはとっても簡単
CGColorRef currentColorRef = [currentColor CGColor];
CGColorRef voidColorRef = [[UIColor colorWithHue:0.0
saturation:0.0
brightness:0.17
alpha:1.0] CGColor];
CGColorRef colorArray[2] = {voidColorRef, currentColorRef};
CGFloat locations[2] = { 0.0, 1.0 };

// CFArrayRefを作る。
// もっと簡単に作りたければ、NSArrayを作ってからCFArrayRefにキャストするだけでもよい。(未確認)
CFArrayRef colors = CFArrayCreate(kCFAllocatorDefault, (const void **)colorArray, 2, &kCFTypeArrayCallBacks);

colorSpace = CGColorSpaceCreateDeviceRGB();
// Gradientを生成する
grad = CGGradientCreateWithColors(colorSpace, colors, locations);

// 描画する
CGFloat progress = currentTime / allottedTime;
CGFloat height = self.frame.size.height + HEIGHT_GRADIENT;
CGPoint startPoint = CGPointMake(self.frame.size.width/2, progress * height - HEIGHT_GRADIENT);
CGPoint endPoint = CGPointMake(self.frame.size.width/2, progress * height);
CGContextDrawLinearGradient(context,
grad,
startPoint,
endPoint,
kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
// GradientとColorSpaceを開放する
CGColorSpaceRelease(colorSpace);
CGGradientRelease(grad);
}
CGContextDrawLinearGradientの第4引数にkCGGradientDrawsBeforeStartLocationフラグとkCGGradientDrawsAfterEndLocationフラグを設定しています。これらのフラグを指定すると、startPoint以前、およびendPoint以降をグラデーションの開始色と終了色でべた塗りしてくれます。実行結果は以下の通り。


こんな感じで、画面中央から下が全部べた塗りになっているのが分かると思います。

他にも、CGGradientを作る際にlocationsを増やせば2色だけではなくて多色のグラデーションを作成することが出来たりします。このあたり、Appleが公開しているドキュメントだけでも相当詳しく紹介されているので、そちらを見ればほぼ間違いないかと。


■サンプルソース
今回のサンプルはgithubですべてソースを公開して居ますので、より詳しく学びたい方はそちらも併せてご参照ください。
http://github.com/akisute/YourTurn/tree/master
これとかこれを見るとよいかと思います。

2009年7月5日日曜日

iMacとiPhoneでBluetoothのヘッドセットを使ってみた

以前からずっと気になっていたBluetoothヘッドセットをこのたび購入したので、iPhone 3GやiMacとつないでみました。
使用感などをレポートしてみたいと思います。


■購入したヘッドセット
某所で紹介されていた、「Bluetoothステレオヘッドセット ELANVITAL EVSH-2882」という奴です。ネックバンド型でA2DPに対応していて通話ももちろん出来ちゃう、その上8000円未満となかなかお買い得。(ソニーとかモトローラのやつは1万6000円とかしますからね><)


■iMacで使う
まずは「環境設定」から「Bluetooth」を開いてヘッドセットとペアリングさせます。ペアリングさせると、「ヘッドセットを有効にする」と「ヘッドフォンを有効にする」の二つが選択できる様になります。

こんな感じ。

実はここに落とし穴があって、
  • ヘッドセットを有効にすると、HSPを用いてiMacとヘッドフォンが通信を行う。
    そのため音楽等を聞くとモノラルで電話並みのひどい音質になる。
  • 逆にヘッドフォンを有効にすると、A2DPを用いて通信が行われるため音質は最高になるが、
    ヘッドセットとして認識されない(要するにヘッドセット内蔵のマイクが使えない)
何この残念な状況orz

MacのBluetoothの扱いが悪いのか、このヘッドセット自体に問題があるのか、それともBluetooth2.1の規格がそもそもそんな使い方を想定していないのかは知りませんが、とにかく「Mac OS X 10.5.7上でELANVITAL EVSH-2882を使う場合、そのままではステレオ音質でヘッドセットとしてマイクを自由に使うことはできない」というのは間違いなさそうです。

それでもいろいろ試して見た結果、以下の手順を取ればステレオの音質でマイクが使えるようになりました。
  1. 「ヘッドフォンを有効」にする。
  2. 「環境設定」の「サウンド」を開き、出力と入力を以下のように設定する。

    出力は、「Bluetooth ヘッドフォン」を選択。


    入力は、「Bluetooth ヘッドセット」を選択。
この設定を行った状態でSkypeを開き、オーディオデバイスを適当に設定して友人とSkypeで通話してみましたが、無事ステレオ音質でマイクが使えました。しかしこの方法は何かしらの無理をしているようで、突然音質がモノラルになったり、突然マイクが使えなくなったりします。その上バックグラウンドで動かしていたiTunesの音が全く聞こえませんでした。ということで、おすすめできません。


■iPhoneで使う
設定方法とかはAppBankさんのこちらの記事が詳しいのでそちらをご参照をば。
http://www.appbank.net/2009/06/19/iphone-news/32183.php
http://www.appbank.net/2009/06/21/iphone-application/32492.php

使ってみた感想ですが、A2DPのおかげで音質はかなり良いです。私はオーディオマニアではないので参考にはならないかもしれませんが、有線のヘッドフォンと全く違いが分かりません。普段から1万円以上のイヤフォンやヘッドフォンを使われるような方でなければ問題ないと思います。

着信があったときの移行もスムーズです。また、着信を受けてからBluetoothヘッドセットの電源を入れて、オーディオデバイスをそちらにに切り替えても問題なく通話が出来ました(ただし友人曰く、ちょっと音が遠いとのことなので、ひょっとしたらiPhone本体のマイクで音を拾っていたのかも)

Macでは音声出力先・入力先ごとに音量を保持してくれていたのですが、iPhoneではどうやらそのような機能がないようです(すべての音量が共通)。そのため、Bluetoothヘッドセットで小さいと思った音がiPhone本体のスピーカーでは大音量と言うことがあります。ちょっと不便。

最大の問題が、「一時停止」「次の曲」「前の曲」ボタンがすべて利用できないことです。私は結構頻繁に曲を次に送ったり一時停止したりする使い方をするので、これらのボタンが使えないのが苦痛になっています。
ひょっとしたら使える機種もあるかもしれませんが、少なくとも今回私が購入したものではダメでした。気になる方はご自身で確認されることをお勧めします。

電池の消耗については、有線よりは確実に減ります。どれぐらいがっつり減るかは未確認ですが、1時間程度付けっぱなしにしていると10%ほど減っていました。(別の要因もあるので一概には決められませんが・・・)

なお、音声コントロールはBluetoothヘッドセットが使用できませんでした。ヘッドセットをペアリングさせて使える状態にしていても、本体のマイクとスピーカーが使用されてしまいました。

結論としては、聞くだけ・通話するだけ専門としてはほとんど申し分ありません。ケーブルが邪魔になるジムでの運動時などでは最大限に威力を発揮すると思います。逆に普段使いでは電池の消耗・iPodコントロール不可などのデメリットが目立つかと思います。


■iMacとiPhoneで併用
ペアリング自体は全く何の問題もありません。iPhoneとiMacの両方に同じヘッドセットをペアリングさせることが出来ました。
しかし当然ですが同時に使うことは出来ません。ヘッドセットの電源を入れた後、「先に認識を完了した」デバイスが独占的にヘッドセットを使います。認識が遅れた方はエラーになりヘッドセットを使うことが出来ません。
iPhoneとiMacの両方のBluetoothがオンになっていると、どちらが先に認識するか安定しないため、
  • iPhoneで使うときは、iMac側のBluetooth自体をオフにする
  • iMacで使うときは、逆にiPhone側のBluetoothをオフにする
という面倒な作業が必要になります。結局、私はiPhone専用としてヘッドセットを使うことにしました。