2013年8月31日土曜日

iPhone で Box2D を使う

iPhone で、物理演算を行うためのライブラリ(?) Box2D を動かす所までを調査してみた。
前回は、ソースの取得で苦労した訳だけど、要る物さえ取れてしまえば後は楽か・・・と思ったが甘かった。

今回は、以下のサイトを参考にさせて頂いた。

UIKit で物理演算エンジンを使用する
http://qiita.com/shu223/items/8a1d064411a2a122983d

ここでは、自分が行った点を補足として残す。

まず、1.のファイルの取得については、前回の記事で書いたとおり SVN にプロキシ設定を行って取得した。

次に、4.の必要ファイルの拡張子を cpp から mm に変更する所。
これは、ターミナルを使い対象(Box2D/Box2Dの中)の各フォルダごとに変換を行った。
for nm in *.cpp; do[Enter]
> mv $nm ${nm%.cpp}.mm;[Enter]
> done[Enter]
上記が、フォルダの中の cpp ファイルを取得して mm に置き換えるスクリプトである。
フォルダ数も知れていたので、手作業で行ったが、結構神経使ったので、もう少し改良した物を使った方が良いかもしれない(シェルに明るければ簡単にできるんだろうな)。

作業が前後するかも知れないが、拡張子を変換してから XCode に放り込んだ方が良さそうだ。
放り込んだ時点で XCode では、警告が沢山出た。

warning: Missing file:/job/hoge/trunk/hoge/Classes/Generic/UI/UICustom/NSCViewVisitor.h is missing from working copy

(警告メッセージが違うかもしれん。。。)
このメッセージは何かというと、SVN 管理のファイルが変更になっているぞ!と言う物らしい。

内容的に、無視しても良いけど、気になるので(目障り)、監視ファイルを削除することにした。

コピーしたフォルダ内に「.svn」という隠しファイルがあるので、削除すればこの警告は消える。

これで、中間地点となる5.のヘッダーファイルをインポートしてビルドをする部分までは行ける。
と、思ったがエラーが出る。

「Lexical or Preprocessor ‘cassert’ file not found」

い、意味不明だ・・・
調べたところ、関係する拡張子が「mm」でないとダメらしい。

上記で変換してるんだけど・・・と思ったが、これの影響範囲は関連するプロジェクト全てのファイルが対象の事だった。
つまり、「ViewController.m」 も 「ViewController.mm」 にしなくては行けなかった。
これで、ビルドまで通るようになる。

ここまで来ると、あとはそのまま実装していけば完了するはず。
本当にコピペとなるためにコードは省くが、ビルドして実行すると、箱がポロポロ落ちてくるサンプルが作れる。

今の段階では、これで終了したが、ゲームなりなんなりに使うためには、もう少し使い方を勉強する必要がある。
先が長そうだったので、余裕が出たときに再度取りかかろうと思う。

Mac SVN でのプロキシ設定

iPhone で、物理演算を行うライブラリ Box2D が使えるとの事で、早速調査して見た。
その際に、Box2D は、SVN で管理されているようで、ターミナルで以下のコマンドを発行すれば取得出来るとの事。
svn checkout http://box2d.googlecode.com/svn/trunk/ box2d-read-only
意味としては、「指定したURLからデータを取得し、指定場所へ格納せよ」となる。
これを実行したところ
svn: OPTIONS of 'http://box2d.googlecode.com/svn/trunk': could not connect to server (http://box2d.googlecode.com)
と、エラーが出る。

「う~ん。URL の先がないのか?」
と思ってブラウザでアクセスすると、普通に表示される。

頭が固いのか?
ブラウザで一個ずつファイルを落とすのか?とかバカな事を考えたり、
時間を置いて見たらどうだ?とか意味不明な事をしばらく繰り返していた。
若干あきらめかけた時、エラーメッセージで調べると答えが有った(はじめからすれば。。。)

どうやら「プロキシ」の設定がいるらしい。
確かに、今の環境は Web に繋ぐのもプロキシがいる事を忘れていた

正直なところ SVN はローカルで少し使ったぐらいで、プロキシの設定と言われても困ったが、それ程難しくなかった。
以下のようにすればよい。
vi ~/.subversion/servers
これで、設定ファイルを開く事ができる。
その設定ファイルの 「global」 の項目にプロキシ設定がある。

...
[global]
# http-proxy-exceptions = *.exception.com, www.internal-site.org
# http-proxy-host = defaultproxy.whatever.com ← コピーする(または削除)
http-proxy-host = xxx.xxx.xxx.xxx             ← ここに自分の環境を設定
# http-proxy-port = 7000                      ← コピーする(または削除)
http-proxy-port = xxxx                        ← ここに自分の環境を設定
# http-proxy-username = defaultusername
# http-proxy-password = defaultpassword
...
これで、先ほどの取得コマンドを発行すると、上手くいった。
さて、ここからが大事だがそれは次の記事とする。

2013年8月29日木曜日

Firefox OS(シミュレータ) で Hello World

Web OS ってやつですか。

流行ってるんですかね?いかんせん端末も普及していないので、なんとも言えませんが、今後の主流になるとか何とか。

今のところ情報が少ないのですが、Hello World は一瞬ということなので、試してみました。

Firefox OS が載っている端末は所持していないので、ブラウザ上のシミュレータでやってみたいと思います。

準備する物は、
  • Firefox
  • エディタ
のみ。

俗に言う IDE と言う物が今の所無いようです。
なくても Firefox にアドオンでデバッグ環境は整うので大した問題じゃないと言う事みたいです。

実際に環境を作るに当たっては、アドオンをインストールする必要があります。
Firefox OS Simulator と言うアドオンをインストールします。
あと、Firebug や、その他 Web 開発に便利な物が有ればいいのかな?

インストールすると、Simulator Dashbord というタブが開き、左のペインの Simulator のスイッチをクリックし Running とすることにより、シミュレータが起動します。

これで、環境が整ったので、実際に動かす Hello World の作成へ入ります。

まずは、デスクトップに適当なフォルダを用意します(今回は、FirefoxOS と言う名前で作成)
その中に、動かすプログラムのフォルダを作ります(今回は、HelloWorldと言う名前で作成)
このフォルダ内に実際のプログラムを入れていきます。

プログラムと言っても、iPhone や Android などの堅苦しい言語を使用する訳ではなく、HTML5 と JavaScript を使用して作っていきます。

フォルダ内に index.html を作成します。
<!DOCTYPE html>
<html lang="ja">
 <head>
  <meta charset="UTF-8" />
  <title>Hello World!</title>
</head>
 <body>
  <h1>Hello World!</h1>
 </body>
</html>
以上です。

え?と思うでしょ。
自分もそうでした。単なる HTML のファイルじゃないかと。
だけど、これで良いらしいです。

あとは、OS で動かすに当たって、設定を行う manifest.webapp を記述します。
{
    "version": "0.1",
    "name": "Hello World!",
    "description": "first firefox app.",
    "launch_path": "/index.html",
    "icons": {
    },
    "developer": {
        "name": "Hello",
        "url": "http://yourawesomeapp.com"
    },
    "default_locale": "ja"
}
ここには、プログラム起動に際してのエントリポイントとなるファイルの指定等を行います。

以上で、準備か完了となります。
再度、 Simulator Dashbordに戻り、Add Directory のボタンを押してください。
ファイルの読み込み指定のダイアログが開くので、上記で作成した manifest.webapp を選択する事により、プログラムが起動します。
(icon のところで指定がないので、警告が出ます。)
以上で、Hello World の実行は完了です。
お疲れ様でした。。。って

拍子抜け名感じしますが、今の段階ではこれで良いとします。
実際には、HTML5 や jQuery などを駆使して高度な事や、マニュアル読めばもっと細かな事が可能だとは思うんですが、今のところは深追いしないと言う事で。


2013年8月28日水曜日

Mac の Markdown エディタ

iPhone のパーソナル Wiki の TrunkNotes が便利と思っている。

現在の編集方法は、ローカルで、エディタを使って Markdown を書き、iPhone で閲覧すると言う方法を取っている。
これは、TrunkNotes のデータをクラウド上に置く事によって実現している。

編集は、iPhone では厳しく、ローカルだと快適だからこの方法をとっているが、これで完璧か?と言うとちょっと違う。
現状の欠点としては、書いた後の見た目を iPhone 側で見ないと行けないため、記法が有ってるかの確認に手間がかかっている。

この部分については、以前から合った物の「しょうがない」と決め込んでいた。
しかし、今回ツールアプリの調査をしているときに、Markdown Editor を見つけてしまった。
これは、Mac 上でしかもリアルタイムプレビューが可能という物だ。

Windows には、「ひとりWiki」やその他ローカル上で Wiki を作成できる物を知っていたが、Mac でもあることに驚いた。

今回見つけて、使いやすそうだと思った物は、

Kobito
Mou

の 2 つ。
どちらもフリーとなっている(ただし、Mac 専用)。

Kobito については、Qiita との連携が取れるようになっているため(Qiita が作ってるから)、ユーザ登録を行わないと使用が出来ないが、登録は無料のため気兼ねなく使える(データ登録が面倒と思わなければ)。
また、国産だ。

Mou についても、外部との連携が取れるようになっているが、こちらはユーザ登録なしで、使用ができる。

どちらも専用のエディタというか編集領域があり、編集するとリアルタイムで結果が見える。

ただエディタは、これまで Vim を使っていたので、エディタはそのままで、結果のみ確認する使い方にした。
この使い方だと、Mou の読み込みが上手くいかなかった。

何はともあれ TrunkNotes に反映する前に確認出来るのは嬉しい。

また Markdown が好きになってしまった。

CoreData で "An instance of NSFetchedResultsController requires a fetch request with sort descriptors"

CoreData を使用するアプリを作成しているが、表題通り
An instance of NSFetchedResultsController requires a fetch request with sort descriptors
エラーが出てアプリが落ちる。

原因は、メッセージにある通りではあるが、sort descriptors が絡んでいる。
今回、動作を早く見たいが為に、ソートの設定まだイランだろと思ってソート部の実装を省いたのがまずかった。

以下のように、ちゃんと実装すれば良かった。
// ソートを設定
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO];
NSArray *sortDescriptors = @[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
しかし、ビルドは通って、実行で落ちるこの手のエラーは、出たときに途方に暮れそうになるな。。。
(落ち着いてかつ、ポイント押さえてない自分が悪いとも言う。)

ホームアイコンバッチの文字列表示は出来なくなった

アイコン右上に表示されるバッチ(アプリケーションバッチ)について、数値ではなく、文字を表示したかった。

最近簡単なサンプルを作っている時に、文字が表示できたと思っていたが、どうやら現行の XCode(4.6) では、機能削除された様だ。

以前は、
[[UIApplication sharedApplication] setapplicationBadge:@"ON"]
と書けば良かったらしい。

現状は、文字列ではなく数値のみが表示可能で、
// バッチに 5 を表示
[UIApplication sharedApplication].applicationIconBadgeNumber = 5;
// バッチのクリア [UIApplication sharedApplication].applicationIconBadgeNumber = 0;
とすれば良い。
しかし、この間文字列が表示できたと思ったんだけどな。。。

ついでに、バッチはタブバーにも表示でき、こちらは文字列も表示できる。
ひょっとすると、こっちの印象が残っていて、ホームでも出来たとか思っていたのだろうか?

調べて分かったけど、あんまりバッチは好まれていないような気がする。
確かに、自分も使っていて便利な時と表示が汚いと思うときがある。
適切な使い方を考えた方が良いかもしれないな。

2013年8月26日月曜日

ループする(している様に見せる) PickerView の作り方

今頃のアプリの項目選択では、余り使われなくなった PickerView。
時代に反しながら、それでも利便性がある所があるため使い方を学習した。

通常に実装しただけでは、ループが施される処理にはならず、ドラムの先頭には空白が表示される。









↑こんなかんじ

これをループ形式にする。








↑こんなかんじに

ちなみに画像が違うのは、Blog を書いている PC は開発機ではないため、実際の画像が取得できないので、イメージを取ってきているから(実装内容はポイント押さえてるからご勘弁を)。

要するに、数値で行けば、0 ~ 9 をループさせると言うようにする。

この実装をどのようにするかというと、プロパティ等ではなくカラクリがある。
実際にはこれループをしているわけではない。
ループさせたい番号を一定数繰り返している長~いビューを見せているだけなのである。

最初は、「そんなバカなプロパティあるんだろ?」と思っていたが、当然無くまたどのソースをみてもこの方法を採用している(当たり前か)。

当然ただただ長いビューを表示させているので、先頭と終端があり、何も考えずに表示すると最初に載せている通り先頭が空白の状態が表示される。
これでは、ループに見えないので、最初に表示する際には、中間地点の先頭(この場合は 0)を表示するようにする。

なので、ループに見えるようなPickerViewの場合、頑張って先頭もしくは終点までクルクル回すと先頭(終点)が表示される。

また、終点近くまでクルクル回してそれで終わりとすると、次回のクルクルの時に直ぐに終わりが来てしまうとまずいので、選択が確定したら、中心部近くへ移動させる処理も入れないと行けない。

これらを実装した内容は以下の通りになる。
今回のソースは、時・分をループさせるような物を作成している。
(上にも書いたけど画像と実装内容が微妙にずれていて申し訳ない)

ポイントは上に書いたから良いか。。。
    #import <UIKit/UIKit.h>

    @interface ViewController : UIViewController<UIPickerViewDelegate, UIPickerViewDataSource> {
        NSTimer *timer_1sec;
    }

    @property (strong, nonatomic) NSMutableArray *hours;
    @property (strong, nonatomic) NSMutableArray *minutes;

    @property (weak, nonatomic) IBOutlet UILabel *timer_count;
    @property (weak, nonatomic) IBOutlet UIPickerView *pvTime;

    @end
    #import "ViewController.h"

    #define MAX_HOURS 24
    #define MAX_MINUTES 60

    #define LOOP_COUNT 6

    @interface ViewController ()

    @end

    @implementation ViewController

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.

        _pvTime.delegate = self;
        
        // データ準備
        // 時
        _hours = [NSMutableArray arrayWithCapacity:MAX_HOURS * LOOP_COUNT];
        for(int i=0; i<LOOP_COUNT; i++) {
            for(int j=0; j<MAX_HOURS; j++) {
                [_hours addObject:[NSString stringWithFormat:@"%02d",j]];
            }
        }
        
        // 分
        _minutes = [NSMutableArray arrayWithCapacity:MAX_MINUTES * LOOP_COUNT];
        for(int i=0; i<LOOP_COUNT; i++) {
            for(int j=0; j<MAX_MINUTES; j++) {
                [_minutes addObject:[NSString stringWithFormat:@"%02d",j]];
            }
        }
        
        // 中央に移動(先頭番号)をセット
        [_pvTime selectRow:MAX_HOURS * (LOOP_COUNT * 0.5f)
               inComponent:0
                  animated:NO];
        [_pvTime selectRow:MAX_MINUTES * (LOOP_COUNT * 0.5f)
               inComponent:1
                  animated:NO];

    }

    - (void)didReceiveMemoryWarning
    {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }

    // ドラムの数(本数)を指定
    - (NSInteger) numberOfComponentsInPickerView:(UIPickerView *)pickerView {

        return 2;

    }

    // 各ドラムのセル数をセット
    - (NSInteger) pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
        
        NSInteger count = 0;
        
        switch (component) {
            case 0 :
                count = [_hours count];
                break;
            case 1 :
                count = [_minutes count];
                break;
            default:
                break;
        }

        return count;
        
    }

    // ドラムのセルに表示するタイトルをセット
    - (NSString *)pickerView:(UIPickerView *)pickerView
                 titleForRow:(NSInteger)row forComponent:(NSInteger)component {
        
        NSString *title;
        switch (component) {
            case 0 :
                title = [_hours objectAtIndex:row];
                break;
            case 1 :
                title = [_minutes objectAtIndex:row];
                break;
            default:
                break;
        }
        
        return title;
        
    }

    // 選択後に呼ばれる
    - (void)pickerView:(UIPickerView *)pickerView 
          didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
        
        // 時
        NSInteger index = [_pvTime selectedRowInComponent:0];
        // 中央付近に戻す
        [_pvTime selectRow:(index % MAX_HOURS) + (MAX_HOURS * (LOOP_COUNT * 0.5f))
               inComponent:0 animated:NO];
        // 値の確認
        NSLog(@"%d, %@", index, _hours[index]);

        // 分
        index = [_pvTime selectedRowInComponent:1];
        // 中央付近に戻す
        [_pvTime selectRow:(index % MAX_MINUTES) + (MAX_MINUTES * (LOOP_COUNT * 0.5f))
               inComponent:1 animated:NO];
        // 値の確認
        NSLog(@"%d、%@", index, _minutes[index]);
        
    }

    @end