iOSでCocos2dx

macでの実行ファイルの作成、実行にもなんとか成功したこともあって、ついでにiOSでの実行ファイルの作成と転送にチャレンジしてみる。既にSimulatorでの動作は確認しているので、実機さえ認識してもらえればうまくいきそうではある。
ただ、シミュレータ上で正しく動いていなかったところ(mac版とSpriteBatchの挙動が異なる?)のプログラムの見直しは必要になりそう。実機はこれを使ってる。

iPhone6s OS 9.3

あと、そういえばXCode7からiPhoneへの転送が無料になったらしいんだけど本当かな?

実機に転送、しかし「busy processing symbol files」

iPhoneをmacにつないでみる。xCodeでもデバイスを認識したので早速実行してみるも、あれ?転送に失敗した。よく見ると「Processing symbol files」という表示でプログレスバーが少しづつ伸びている。なんかの作業中かな?

もしかして、お金払ってないから転送してくれないの?できればプロビジョニングファイルとかとの格闘は今は避けたい。

無料じゃないの?

いいえ無料でした。ごめん。プログレスバーが最後まで伸びるのを確認して、再度実行すると、appleIDとの関連を確認されたけど、適当に「OK、OK、fix issue」とかやってたら転送はうまくいった。appleIDが更新されて(fix issueボタンを押す)ちゃんと転送されました。iPhone側でも「設定>一般>プロファイルとデバイス管理」から自作アプリを認証してあげる必要がある。

うまく動かない

しかし、実行するも起動直後にヌルポ参照で落ちた。どうもHelloWorld.pngの読み込みで失敗している。
いろいろ考えられるが、まずはファイル名、大文字小文字の違い、Resourceの当該プロジェクトから除外されてる可能性?などいろいろ探ったが該当なし。
ファイル名についてはBundleフォルダ以下に修正されたパス名で正しく読み込みに行っているのにPNGの解釈で失敗してる。HelloWorld.pngはCocos純正のPNGなので間違いはないはず。申し訳ない気持ちで拙作のPNGに差し替えてみた。

PNG差し替えたら動いた

HelloWorld.pngに問題があった。シミュレーターではうまく読み込めていたのに、実機だと画像ファイルの読み込みでNULLが返されてしまう。

auto sprite = Sprite::create("HelloWorld.png");

ここでNULLが帰ってきちゃう。しかもこの画像ファイルはCocos2Dxの「HelloWorld.png」でmacではうまく動いていたし、iOSシミュレーターでもうまく動いていた。しかし、PNGファイルの解釈のプログラムでASSERTに引っかかってしまう。で、とりあえずの解決方法としては別のpngファイルを読み込ませるとうまく動いた、え!?

macでもiOSシミュレーターでもうまく動いていたのに、iPhone実機だと通らない理由が謎。pngの圧縮率を変えたら動いたという情報もあるが、これと一致しているのかはわからない。とりあえず理由は気になるが先を急ぐ。

どうにか動いた!が・・・

やっぱりシミュレータでの動作と同じく、スプライトがおかしい、macの動作と異なる。具体的にはSpriteのUVWHがiPhoneとMacで異なっている。iPhoneの方の挙動がおかしい。マルチプラットフォームのCocos2dxの一番良いところの恩恵が受けられないのが辛い。だいたい理由は想像できるがSpriteBatchの使い方が好ましくない使い方をしているんだろうと思う。

違いは結構根が深い

setTextureCoords

変数ContentScaleFactorがMacとiPhoneで異なることがわかった。スプライトの画面解像度に対する比率の設定らしい。macだと1、iOSだと0.233333みたいな感じ。バーテックスバッファに保存されているテクスチャのtexCoord(UV値)がこの変数の影響を受けてMacの時のUV値と異なる結果を保持している。何が違いを生んでいるのか?

Cocosの画面解像度

そもそもCocosの画面解像度ってどうやって決めているのか。ContentScaleFactorの値を決定する過程を調べてみた。

ゲームによって理想の解像度は異なるため、ゲームの必要とする解像度を設定しているのがdesigneContentと言われる変数で、ゲームデザイン手動の画面解像度。横幅と縦幅を設定できる。デフォルトは480x320。

static cocos2d::Size designResolutionSize = cocos2d::Size(480, 320);
static cocos2d::Size smallResolutionSize = cocos2d::Size(480, 320);
static cocos2d::Size mediumResolutionSize = cocos2d::Size(1024, 768);
static cocos2d::Size largeResolutionSize = cocos2d::Size(2048, 1536);

対してCocosの解像度はbool AppDelegate::applicationDidFinishLaunching();に記述されている内容を見るとsmall,middle,largeの3種類の解像度を用意している。以降勘で解釈するが、iPhoneや特にAndroidなどの端末では千差万別の解像度になっているため、マシンから得られた実改造度をこの3種類の解像度のどれかに丸めて表示するものらしい。

とはいえピッタリのサイズはなかなかないので、基準となる近い解像度のものを選んで、差分を各端末ごとに画面にピッタリ収まるように処理するための、この時の拡大率がContentScaleFactorとなるっぽい。

macではウィンドウ表示のため、実解像度とは別に480x320のウインドウサイズで初期化すればデザイン解像度とぴったり一致させることができるため、ContentScaleFactorは1.0にできるが、iPhoneではウインドウ表示という概念がないため、iPhoneそのものの固定された解像度になるため、ContentScaleFactorで拡大率を用いて調整することになる。

しかし

しかし、このContentScaleFactorがSpriteの表示時に参照されてUVWHの値の算出にも影響を受けてしまっているのは謎。なぜそんな仕様になっているのかは、もっとCocosの気持ちにならないとわからない。今なんとなくわかるのは、おそらくCocosにおいてはテクスチャからUVWHを設定して切り出して表示する、っていう使われ方そのものが想定外で、Cocosの作法に半数のかもしれない。

対応方法

一応対処方法はある。このContentScaleFactorの値を強制的に1にするか、画面解像度の選定において1になるようなデザイン解像度に設定するか?である。どちらの方法もContentScaleFactorの値を固定することに変わりはないので、Cocosの方針に抗うといった意味ではどちらも罪深い。

その他

その他macとの違いはmacでマウスの対応でタッチの代用をしていたが、iPhoneでは当然何も起こらなかった。タッチの取得とかわかりやすそうなところからやってみるとこれは意外とあっさり出来た。