夏までにiPhone アプリつくってみっか!

趣味でiPhone/Androidアプリを開発し、日々勉強した事を書いています。オープンワールド系レースゲームをUnityで開発中です。

【スペースハリアー】Core Graphicsでシマシマの地面を描いてみた

スペースハリアーに欠かせないのがチェッカー模様の地面。
これを再現するのが一番難しそうなので最初にクリアしたいと思います。
スプライトをSprite KitのEffectNodeで台形に変換して奥行きをつけてみたりしましたが、スペハリのような広大な地面を作るには巨大なスプライトを変形させる必要がある事がわかりとりあえずこの方法は断念。
なにかいい情報はないかとネットをググってみると引っかかったのがこの「Lou's Pseudo 3D Page」というサイト。
ほとんどが擬似3Dのレースゲームの地面の実現方法についての解説ですが、スペハリの地面がどのようなデータになっているかの説明もあったりします。
なるほど、とりあえず縦縞のデータが必要なんですね。

早速お絵描きソフトを引っ張り出して描き始めましたが、縞を扇状に10数本引いたところで断念。
いつまでたっても扇形のままでいつまでたっても両端が水平線まで上がってきません。

これは手描きでは無理だぞということで、プログラムで絵を描いてみる事にしました。
これまでcocos2dしか使ってこなかったのですが、UIViewとかCore Graphicsとか慣れないことにチャレンジしなくてはならないようです。

そこで参考にしたのが以前から度々参考にさせてもらっている「Ray Wenderich」のサイト。

とりあえず見よう見まねでプログラムを組んでみました。UIViewのサブクラスを作ってdrawRect:メソッドをオーバーライドすれば自動的に絵が表示されるのですが、知らずにUIViewControllerのサブクラスに適当なメソッドをつくっていたためinvalid context 0x0というエラーが大量に発生してしばらくハマりました。

で、完成したのがこのプログラムです。

- (void)drawRect: (CGRect) rect
{
    
    int bandWidth = 48;
    int bandCount = 1000;
    
    CGFloat screenWidth = self.bounds.size.width;
    CGFloat screenHeight = self.bounds.size.height;
    CGFloat centerOffset = screenWidth/2.0;
    CGPoint topCenter = CGPointMake(screenWidth/2.0, 0);
    CGPoint startPoint = topCenter;
    UIColor* dark = [UIColor colorWithRed:108/255.0 green:186/255.0 blue:113/255.0 alpha:1];
    UIColor* light = [UIColor colorWithRed:160/255.0 green:234/255.0 blue:162/255.0 alpha:1];
    CGContextRef context = UIGraphicsGetCurrentContext();
    UIColor* currentColor = dark;

    for (int i = -bandCount; i < bandCount; i++) {
        currentColor = (currentColor == dark) ? light : dark;

        for (int j = 0; j < bandWidth * 2; j++) {
            CGPoint endPoint = CGPointMake(i * bandWidth + j/2.0 + centerOffset, screenHeight);
            draw1PxStroke(context, startPoint, endPoint, currentColor.CGColor);
        }
    }
}


void draw1PxStroke(CGContextRef context, CGPoint startPoint, CGPoint endPoint, CGColorRef color)
{
    CGContextSaveGState(context);
    CGContextSetLineCap(context, kCGLineCapSquare);
    CGContextSetStrokeColorWithColor(context, color);
    CGContextSetLineWidth(context, 1.0);
    CGContextMoveToPoint(context, startPoint.x + 0.5, startPoint.y + 0.5);
    CGContextAddLineToPoint(context, endPoint.x + 0.5, endPoint.y + 0.5);
    CGContextStrokePath(context);
    CGContextRestoreGState(context);
}

draw1PxStroke関数は上記のサイトからそのままお借りしました。

とにかく画面上部中央から画面下部に向かって少しずつ下端の位置をズラしながら線を描いていき、ある程度描いたら色を入れ替えるという作業をひたすら繰り返すプログラムです。

実行結果はこうなります。

f:id:takujidev:20131030224558p:plain:w600

なかなかいい縞模様だと思います。扇形がほぼ水平にまで広がっています。
理論上、上端は完全には一直線にはならないので少しカットして使用します。

ちなみに、これをiPhoneで実行すると計算にものすごい時間が掛かります。
上記のプログラムは縞を2000本描く設定になっているのですが、4 core Core i7 2.3GHzのMaciOSシミュレーター上で実行すると約33秒かかりました。アクティビティモニターで見る限り使っているコアは1個だけです。
一方iPhone4Sだと1/10の200本の縞を描くのに約1分かかりました。
単純計算でMacより20倍遅いことになります。

このプログラムでは線幅の半分だけズラした位置に次の線を描いていくようにしています。
単純に線幅ずつズラしていくと画面中央下部にモアレが出てしまうからです。
参考までに、モアレありバージョンのスクリーンショットです。
f:id:takujidev:20131030230608p:plain:w600

なお、この奇麗なグリーンはスペースハリアースクリーンショットからRGBの値をゲットしました。
画面からRGBの値を読み取りにはMacのアプリケーション->ユーティリティに入っているDigitalColor Meterを使いました。

f:id:takujidev:20131030231317j:plain:w600

こんな感じにカーソル位置のRGB値を知る事ができます。
なお、UIColorオブジェクトは0.0-1.0の値を取るので255.0で割った値を使用します。

次回は作成したデータをファイルに保存する方法を考えます。
横スクロール用に画面外のデータも少し必要なのでスクリーンショットは使えませんから。
https://itunes.apple.com/jp/app/travelshooting-jp-toraberushutingu/id917570972?mt=8&uo=4&at=10laCt