2015/05/07

東京インディーフェス2015に出展します

明日の5/8〜5/10まで、秋葉原UDXで行われる東京インディーフェスに開発中のオナラアクションローグライクの「Bad Smell Queue」をプレイアブル出展します。

5/8はビジネスデーなので、一般の方で遊びに来たい方は5/9か5/10にお越しください。僕のブースはB-02です。他にも色々な開発者の方々が出展していますので、珍しいゲームでいっぱい遊べると思います。ぜひどうぞ。
インディー界で有名な稲船さんの「Mighty No.9」もプレイアブル出展だそうですよ。
ではー。


Bad Smell Queueについてはこちら:http://xionchannel.no-ip.org/badsmell/

2014/11/21

デジゲー博 ありがとうございました!

先日、11/16にデジゲー博に出展いたしました。
すごいイベントですね。とても大勢のお客さんに遊んでいただくことができました。是非プレイの感想を聞きたいと思って、紙のアンケート用紙も20枚ほど用意してたのですが、2時間くらいで全て終わってしまいました。

スペースの来られたお客さんはほんと途切れること無く、おかげ様で他のスペースに遊びにいく暇がありませんでした。知り合いのところ回りたかったのですが、イベント時間が4時間じゃ無理ですね。
来られた方の中には、ブロガーさんや、ソニーの方、アップルの方などすごい方々もこられて、アドバイスなどいただくことができました。また、女性や外国人も思ったより多く来て頂いて、色々な意見が聞けて良かったです。

動物を主人公にしたゲームって他にあまりなかったようなので、ある意味目立って良かったです。
バッドスメルキューは、3月のBitSummitでみた版画で作られた猫のフラッシュゲームしっぽねこと消えたエビフライ」の疾走感があまりにも良かったので、そういう疾走感が楽しめるゲームを作りたいなと思って作り始めたものでした。(記事はこちら
次はBitSummitの出展を目標にがんばりますー!



バッドスメルキューについては、記事を2つほど書いて頂けました。

オナラで動物を気絶させる可愛くもクサいアクション、バッド・スメル・キュー(仮称) - デジゲー博レポ

[日刊アプリゲット]デジゲー博で見つけた「バッドスメルキュー(仮)」の完成が楽しみ!

2014/11/09

デジゲー博の準備大詰め

いよいよ、来週日曜のデジゲー博に向けて、試作ゲーム「Bad Smell Queue」のテスト版の仕上げ状態。展示に向けて、マニュアルとか用意してみた。


これを展示スペースに置いとく予定です。
あとはiPadにプレイ動画とか流しとこうかなと思ってます。
あと、チラシを配ります。
チラシにはBad Smell Queue試作版のapkのダウンロード用QRコードも載せてますので、チラシもらって後からプレイも可ですー。

http://xionchannel.no-ip.org/~xionchannel/badsmell/

2014/10/21

デジゲー博 2014 参加します!

告知ですー。2014.11.16に秋葉原のUDXで開催される「デジゲー博 2014」にインディーゲーム開発者として参加します!
今回は、Unityを使ったiOS, Android用のアクションゲームの試作版の展示となります。当日はiOSの実機(iPadとか)でのプレイができますし、Android向けにはapkファイルをQRコードからダウンロード可能にしますので、たぶんお手持ちの端末で試すことができますよ。

サークル名「xionchannel software」
スペースナンバー「D-16b」

展示タイトルは新作のオナラアクション「BAD SMELL QUEUE(仮題)」がメインですけど、既出の「Hungry Master」や「Electro Master」も持ってくのでプレイできますよ。
以下の目印で展示してる予定なので、ぜひ来てください。

BAD SMELL QUEUEの情報はこちら

2014/04/06

cocos2d-x andoroidエミュレータのエラー回避

RC0で一度動いたーと思って、その後RC1環境にしてみて、それがだめだったので、やっぱりとRC0環境に戻してといろいろやってたらRC0自体もandroidエミュレータで実行できなくなってしまったので、どうしたものかと思ってたのですが、なんとかなったので、メモ。

起きていたエラーは以下のものでした。

dvmFindClassByName rejecting ‘org/cocos2dx/lib/Cocos2dxHelper’

です。
何が起きてるのかと思ったのですが、ライブラリ側がうまく連結されて無くてそちらを参照できないために動作してなかったみたいです。
で、このフォーラムのやりとりを見て対応。ライブラリ側のjavaファイルをコピーしてアプリのプロジェクトに追加するという力技でした。

というわけで、
HAXMのエミュレータでも、実機でも動くようになりました。

本来ならlibcocos2dxのプロジェクトをインポートしているので、そっちが参照されてうまいこといくはずなんだろうなーと思いつつ、それがうまくいかない場合は直接コピーして対処するしかないのかなーという印象です。

cocos2d-x ver3.0 RC0, RC1の環境構築メモ

cocos2d-x ver3.0 RC0, RC1の環境構築をしてみたので、そのメモをしておきます。あとでもう1回やることになっても思い出せるように。ビルドターゲットはiOSとAndroidが動くようにという感じでやりました。
結論から言うと、RC1はAndroidエミュレータで落ちるのでまだ使わないほうが良いみたいですね。RC0は大丈夫です。

さて、
まず最初にcocos2d-xの公式サイトから最新のパッケージをダウンロードします。(最初RC0しか無かったので、それでやりましたけど、その後RC1が公開されましたので、再度ダウンロードしました)

次にAndroid用の環境の用意で、いろいろ見つかるブログを参考にセットアップを行いました。こことか。

NDKのセットアップ(下記のを使いました)
https://dl.google.com/android/ndk/android-ndk-r9d-darwin-x86_64.tar.bz2

AndroidSDKのセットアップ(下記のを使いました)
これはSDKとEclipseがセットになってます。
https://dl.google.com/android/adt/22.6.2/adt-bundle-mac-x86_64-20140321.zip

上記のを解凍した場所をcocos2d-xのセットアップの際に設定する必要があります。
cocos2d-xを解凍した場所で、./setup.pyを実行し、上記のSDK等のパスを設定します。
これで必要な環境変数が設定されます。(shellを立ち上げ直すと環境変数がセットされた状態でshellが起動します)

これで一旦完了。

cocosコマンドを使ってプロジェクトが作成できます。
こちらを参考に。作ってみます。

cocos new SampleProj -p com.xionchannel.sample -l cpp -d ~/Documents
みたいな感じにするとHello Worldのプロジェクトが作られます。
共通ソース部分と各環境別にフォルダ分けされて置かれてます。

proj.ios_macの中にはXcodeのプロジェクトがあります。
開いてビルドするとワーニングは多数出ますが問題なく動きます。

proj.androidにはAndroid向けの環境があります。
ビルドするにはbuild_native.pyを実行すればできます。
Eclipseで実行確認するには多少手順が必要で、こちらの設定の説明が有用でした。
とはいえ、結局RC1ではAndroidのエミュレータでの起動直後に落ちてしまってどうにもならない感じで、本家のフォーラムでも落ちるって話題になってました。今のところ対処方法無いっぽい。実機だと大丈夫なのかな?試してないのでわからないっす。
RC0では動きます。
さて、Androidのエミュレータは起動が遅くて、動作も遅くて確認が大変なので、x86の高速エミュレータ(HAXM)を使ったほうが確認が楽です。それを設定してみました。
このブログを参考にインストールします。
ただ、OSがMarvericksになっている場合は、この高速エミュレータがOSを巻き込んで落ちてしまうので、このブログの対応が必要になります。要するに修正バージョンを入れないとだめってことですね。
さらに、Android側のmakeファイルを修正して、x86向けのコンパイル模するようにしないといけません。これが参考になります。

APP_ABI := armeabi armeabi-v7a x86

みたいな感じです。
これで高速動作するエミュレータでらくらく確認できるって感じみたいです。

とりあえず以上です。RC1早く治ってほしいですね。
さて、RC0に戻さなくちゃな。


2013/04/29

さいたま勉強会 vol7 エフェクトの作り方

 4/28にさいたま勉強会に参加してきました。今回は「エフェクトの作り方」として手描きアニメーションによるエフェクト作成の基礎みたいなものを発表させてもらいました。参加者はプログラマばかりなのであまり目に触れる機会のないものだったかと思いますが、単純に見て楽しい内容に映ったようなので良かったかなと思います。

 さいたま勉強会はどんな内容でも発表して良いよというものなので、何が発表されるか毎回ドキドキなのですが、今回もかなりバリエーションに富んだ内容でした。
 今回のおしながきは以下のとおり。

@dsgarageさん Arduino×Unity
 ArduinoをUnityからコントロールしようというお話。シリアル通信で制御可能なはずとのことですが、一からコードを作るのがなかなか困難とのことで、uniduinoというアセット($30)の紹介とともに制御の方法について解説しておりました。Arduino Esploraというゲームパッド型のArduinoがあるそうで、それも気になります。

@k_katsumiさん SECoreTextView
 SECoreTextViewという岸川さんの自作ライブラリの紹介。URLやTwitterのユーザー名、ハッシュタグ、画像などが含まれたリッチテキストの表示を簡易に行うためのライブラリです。かなり応用範囲が広そうな便利なものと感じました。また発表ではどうやって実装しているかの細かい話まで聞けてものすごく濃かったです。

@ajinotataki(私です) エフェクトの作り方
 ゲームにおけるエフェクトはどういうものがあって、どうやって作られているかを軽くおさらいした後に手描きエフェクトの基礎の紹介。その後はリクエストに従って手描きをライブでやりました。細かくは後述します。

@akisutesamaさん Parseでガチャ回すお
 Parseという簡易サーバーサービスを使っていわゆるガチャをどうやるかについてを例に使用方法を紹介。データベースとしては設計を先にしなくてもデータ優先で作っても自動的にサーバーができていく点は良い。しかしサーバー側で実行するコードがJSでデバッグがしづらいなど、そちらの面は難ありとのこと。小規模開発やプロトタイプで使用する分にはいいのではとのことでした。

@yoichinejiさん Logic Proの活用と耳コピについて
 Logic Proを買ったもののどう使って良いかという状況に光を当てる発表。耳コピについてはWaveToneというアプリが非常に便利そうでした。

 さて、エフェクトの発表についてですが、当日のスライドはこちら。PDFはこちら

 で、手描きの基礎に入るわけなのですが、以下の2つがわかればなんとかなるかなと思って紹介しました。
 1つは、「移動の錯覚」で、以下のような点の後に線が表示されることで点が移動して見えるというもの。

 それをディテールを整えると以下のようになります。


 もう1つは「送り」で自然現象は繰り返し出てくるパターンが移動していくように見えることが多いのですが、それを意識して絵を描くということ。

 こんなのも「送り」に含まれると思います。

 で、ディテールを整えると、以下のようになります。

 という感じに紹介しました。
 その後は会場のみなさんにリクエストを貰ってその場で描きました。

 以下のは電撃での攻撃。パズドラみたいに手前から奥に攻撃するイメージ。
 これ描いてる時の録画を岸川さんがやってくれたのですが。それはこちら

 以下のは水属性の攻撃。これも同様に手前から奥に攻撃するイメージ。

 以下のは召喚的なイメージ。何かが現れた感じですね。
これ描いてるのを、これまた岸川さんに録画していただいたのですが、それはこちら

 以上でした。
 ここで紹介したアニメ素材については自由にしてもらって構いません。
 ではではー。

 あ、当日のツイートまとめはこちら

2013/01/15

ライトマップのシェーダーをMac, iOSで両立するには

 先日の記事でライトマップを別シーンで使うために、自前のライトマップシェーダーを用意して行うようにしてましたが、いざそれをiOSへ持っていくとそのままだと問題がでることがわかりました。
 ま、絵が出ないってことなんですけどね。
 Unity標準のライトマップシェーダーもそうなっているそうですが、LightModeの設定にライトマップ方式がいくつかあるのには理由があるらしく、環境によって有効な設定が違うようです。LightMode=VertexLMRGBMはMacならばOKですが、iOSでは何も出ません。逆にLightMode=VertexLMの場合はiOSはOKですが、Macでは何も出ません。という感じです。

 てなわけで、どうするか。
 UnityのShaderLabにはSubShaderというタグがありまして、それを使うと有効なシェーダーが実行時に選ばれるというものがあります。これを使えばいいんじゃない?とすると、以下のようになります。

SubShader {
  Tag { "LightMode" = "VertexLMRGBM" }
}
SubShader {
  Tag { "LightMode" = "VertexLM" }
}

 ところがこれではダメで、LightModeでどうなっててもどうやらSubShaderレベルでは有効なシェーダーとして判別されてしまうようです。なので、やるなら以下のように1つのSubShaderの中に2つをいれてしまいます。

SubShader {
  Pass {
    Tag { "LightMode" = "VertexLMRGBM" }
  }
  Pass {
    Tag { "LightMode" = "VertexLM" }
  }
}

 こうすると片方は何も描画しないのでうまくいくようです。たしかに実際にうまく表示できました。使わない方のPassのオーバーヘッドが無いのかどうかについては調べきれてないです。GLSL化された後のコードを見れば分かるとは思うんですけどね。

参考1:Alpha-VertexLit.shader
http://dorumon.googlecode.com/svn/trunk/pwars/Assets/Standard%20Assets/DefaultResources/Alpha-VertexLit.shader
参考2:Multiple target platform lightmap shader
http://forum.unity3d.com/threads/75487-Multiple-target-platform-lightmap-shader

2013/01/12

Unityのライトマップを別のシーンで使う

 Unityではライトマップはシーンデータに固定されて、他のシーンで使用することが出来ないというのが仕様ですが、他で使用するための方法を考案しました。最初は適当にライトマップを貼れるシェーダー書けばいいんでしょって思ってましたが、どのあたりに制約があるのかわかりました。

 てなわけで、まずはモデルへの焼付けを行います。通常通りモデルやライトをstaticにしてLightmapウィンドウにて焼き付けます。今回はキャラにトップライトを設定して、主にオクルージョンの焼付けをメインにしたような調整で行いました。こうするとある程度キャラに動きが入ってもそこそこ見た目が破綻しないので。


 こんなかんじになります。わかりにくいですが、エプロンの下に影が落ちています。トップライトの影とオクルージョンが入っている感じです。エプロンを腰の後ろで縛っている部分にもオクルージョンが入っていますね。
 試しにこれで動かしてみたら、以下のようになります。(なんか色が違ってますけど、細かいことは気にしないでください。テストレンダリングなので微妙に設定違いだったりするからです)


 で、これを別のシーンにもっていくわけですが、普通だとunity_LightmapSTという値がシーンで管理されているために、正しい描画結果になりません。ライトマップがずれて表示されたりします。このunity_LightmapSTっていう値はどこにどう設定されているのかということですが、LightmapウィンドウのObjectタブを見ると焼き付け後には値が入っていることが分かります。


 下の方にある、Tiling X, Tiling Y, Offset X, Offset Yがそれです。このそれぞれの値がunity_LightmapST.xyzwに入るようにUnityのシステムからシェーダー側へuniform変数として受け渡されます。これを元にシェーダー側ではUV2のスケーリングとオフセットを行なってズレを修正するので、ライトマップが正しく貼れることになります。
 さて、では自前のシェーダーでライトマップ用UV2のズレを修正するには上記の値を何らかの状態で保持しておいてシェーダーに伝達すればいいので、マテリアルのプロパティとして値を追加して持って行く事にしました。以下の用に設定します。(注、自前シェーダーのプロパティなので、普通のシェーダーにはありません)


 ちょっと図が細かいですけど、先ほどの値をマテリアルのLightmapSTのVectorへセットしてあります。ちなみにシェーダーコードは以下の様な感じです。HalfLambert + Lightmap + Ambientにしてあります。


 描画結果はどうなるかというと、以下の様な感じです。ライトマップ焼付けが白のトップライトだったのですが、下の図では横からの赤いライティングでベースに緑が入っているのがわかります。横からの赤いライトがDirectionalLightでHalfLambertになってます。ベースの緑はAmbientですね。ライトマップの影響はほんのりと分かる感じになってます。


 シェーダーコードのことで注意ですが、LightModeはVertexLMRGBMを選択しないと正しくライティングできません。LMRGBMじゃないとライトマップのデコードが出来ないみたいです。とはいってもシェーダーコードの中では特にライトマップとして扱ってるわけでもないんですけどね。謎です。なお、今回はライトマップを1枚で焼き付けましたけど、Dualの場合にはVertexLMを選択することになるみたいです。細かいことは以下を参考に。
 http://docs.unity3d.com/Documentation/Components/SL-PassTags.html

 とはいえ、unity_LightmapSTに縛られたくない場合には、他の方法も考えられます。
 1,ライトマップの焼付けを3dsmaxやSoftimageなどのDCCツール側で行う。UV2も自前で展開してね。
 2,Unityでライトマップ焼付けた後にunity_LightmapSTの補正をスクリプトで計算して、UV2へ書き戻してしまう。これならunity_LightmapSTの値がシーン違いで失われても問題ありません。マテリアルもモデル毎に増えないしいいかもしれない。
 以上です。

2012/07/01

さいたま開発勉強会4回目で発表しました。シェーダーについてGLSL, cocos2d, UnityのShaderLabまで

6月30日に久しぶりにさいたま開発勉強会が行われましたので、蕨の商工会議所まで行って来ました。遅刻遅刻〜ってタクシーで急いで会場まで乗り付けたんですけど、どうやら会場があいて無くて入れない様子。会場代払ってるのに管理者が来なかったみたいです。なんということ!
急遽隣の区民会館に部屋を借りて勉強会を行うことになりました。プロジェクターが無くてスライド共有に苦労しましたけど結果的にはうまくいってよかったです。

さて、当日の発表内容は以下のとおり。

@ajinotataki:シェーダーしよっ☆ GLSL, cocos2d, UnityのShaderLab
 スライド:http://www.slideshare.net/xionchannel/lets-play-shaders
 サンプルコード:http://xionchannel.no-ip.org/cocos2d_shaderTest.zip

@tmokitaさん:iPhone開発 超超初心者Tips50 (寝たければ寝てね!)
 スライド:http://www.slideshare.net/tmokita/saitama-beginner-tips50

@f_megmeg5さん:Androidクラスタの私がcocos2d-xでマルチディスプレイ対応してみた
 スライド:http://www.slideshare.net/megmegfive/vol4-13446073

@shun_nakaharaさん:iOSのGCDについて
 スライド:http://www.slideshare.net/shunnakahara/gcd-13499796


@nun_さん:10時間で!無料で!アプリプロモーション
 スライド:http://www.slideshare.net/AsamiNagasaki/10-13490251

@yoichinejiさん:cocos2d-x + ○○でどうにかしてこれしたい
 スライド:なし、tweetで発表でした。

私は、先週のcocos2d勉強会に続き、シェーダーを掘り下げた内容の発表を行いました。
おおまかな流れとしては、
・シェーダーとは何か、概念的な説明
・シェーダーを使って何ができるかの実例
・OpenGLESでのシェーダーを使うための流れ
・それを踏まえてcocos2dでシェーダー使用の流れ
・cocos2dでオリジナルシェーダーを使うための改造ポイント
・cocos2dでのシェーダーデモ
・Unityでシェーダーを使うために
・ShaderLabの基本的な説明
・VertexShader, FragmentShaderを内包する場合の例
・SurfaceShaderを使う場合の例
という感じです。

前の発表会ではそもそもシェーダーに対しての事前知識が無い受講者に対していきなりコア部分の話をしてしまってよくわからないという事態を引き起こしてしまったので、反省して、今回はシェーダーとは何かという部分にページを割いて概念としての説明を丁寧にやってみたつもりです。

他の発表者さんでは@tmokitaさんの発表内容が良かったです。1問1答形式でTIPSをバンバン紹介していく感じで、あまり知られていない内容のものもちょこちょこあり非常に為になりました。

@nun_さんの発表は時間の都合で割愛されてしまったようで残念でした。

で、我らが@yoichinejiさんのネタですけど、今回はプロジェクターが無いことでいつものようにネタを展開することができなかったようで、ネタバレにならないようにtweetを駆使してやる感じでしたね。
開発言語がC++であるということが共通項となるので、Kinect + cocos2d-xというのを組んでみたという発表でした。まだ未完成ではあるそうですけど、Kinect関連で画面描画するのはなかなかとっつきにくいらしいので、cocos2d-xを使用出来ればいいんじゃないかとのことです。

以上でした。

2012/06/22

cocos2d勉強会2回目で発表しました


今日はcocos2d勉強会2回目がGREEさんの会場にて行われました。そこで、cocos2d(ver2.0系列)でオリジナルシェーダーを使うための方法について発表したので、以下に資料とか置いておきます。

●スライドはこちら
●サンプルソースはこちら

内容としては以下の様な感じです
・シェーダーとは何か
・OpenGLでのシェーダーを使うための基本的な流れの紹介
・それを踏まえてcocos2dではどの部分を修正すれば良いかを紹介
・実際のデモ
という感じでやりました。
twitterの発言の様子を後から見るとついてこれなかった方多数のようでしたので、申し訳ないです。先にデモをしたほうが良かったのでは?とおっしゃってくれた方もいらっしゃいました。

来週末のさいたま勉強会vol4でも同じ内容を多少詳しくしたものと、Unityでのシェーダーの記述方法について発表しますので、質問がある方はそちらに来てもらえると良いかと思います。また、twitterメールでもご連絡いただければ答えさせて頂きます。

とはいえ確かにシェーダーを記述したり利用するのはハードルが高いし、そもそもcocos2dのような2Dライブラリで本当に必要なのかといえば、まず必要にならないので技術デモのような感じだったかと思います。逆にUnityではシェーダーは無くてはならない要素だったりするのでそちらの方が親和性が高いかもしれないなと思った次第です。

他の方の発表は以下のとおりでした。
@Seasonsさんがこれまでのcocos2dの流れや海外の様子の紹介
Walzer WangさんがSkypeを使用してのcocos2d-xの紹介
@torotitiさんがOpenOfficeをレベルエディタとして使用してcocos2dで絵本を作ったという事例紹介
@splhackさんがOpenfeintをアップグレードするとGREEになるよという話

OpenOfficeをレベルエディタに使用した話は非常に興味深かったです。OpenOfficeの保存ファイルはxmlと画像リソース等をzipで固めた形式なので、Rubyで展開してパースするのに便利だったということで、コンバートしたデータをcocos2dで再生するように環境を整えたとのことです。これは何かに使えそう!

そういやPSSSDKは地雷という話を懇親会で聞きました。まあiOSが並行開発出来ない時点でフレームワークとしてどうなのよと言われたら言い返せませんよね。

短かったですが、なかなか興味深い勉強会でした。
ではまたー。

2012/05/13

cocos2dでパレットっぽいことをしてみたい その2

以前書いていたエントリーの続きです。その時はシェーダープログラムが全くわからない状態だったので試さなかったのですが、cocos2d 2.0がRC1になったこともありますので、シェーダーに手を出してみました。ここ最近やってるUnityでのシェーダー入門がうまく役に立っているってのもありますね。ちなみにシェーダー入門にUnityは向いているような気がします。すぐに絵を出せますからシェーダーコードに注力できます。とはいえShaderLabという特殊な記述方法となるのでOpenGL ESでGLSLを使うのとは若干違いますけど。まぁ考え方は一緒です。

●以前のアイデア
1,テクスチャ書き換え
2,白い素材を用意し、マテリアル色指定
3,シェーダーでリアルタイム書き換え

 というアイデアを出していました。2までは前回達成しています。1はバッチが効かず、メモリ圧迫も多いため事実上使わないほうがよい手法でした。2については前回のレベルでは限界値で、ぎりぎり実用レベルだった感じです。
 さて、今回は3を実装しました。

●前回の評価を考えなおす
 前回は以下のように3を評価していました。

 利点:テクスチャが共通なので省メモリ
 欠点:僕はシェーダー作ったこと無い。OpenGLES2に対応したcocos2d2.0のベータ版を使わないとならない。描画が遅いかもしれない。シェーダーに渡すパラメータが色ごとに変化するので、CCSpriteBatchNodeで処理させるには難しいかも?

 最初の欠点は今では問題ないです。個人的に。
 2番目はcocos2d 2.0 RC1となったので問題ないでしょう。
 3番目はやってみないとわからない。
 4番目はバッチ処理するためのいい方法を考えましたので、次項で説明します。

●バッチ処理のために
 cocos2d 2.0のソースを見ていると、cocos2dの描画に必要な基本的なシェーダーが全部用意されています。その中身を読んでいくとスプライトの色を設定してもバッチ描画を可能にするための技を確認できました。
 通常OpenGL等では、いわゆるマテリアル(テクスチャ)が違うものはバッチ処理ができないものですが、頂点座標、頂点カラー、頂点UV値、オブジェクトのTransform情報については違っていても1つのDrawコマンドで描画することができます。Transform情報についてはcocos2dの内部で1オブジェクトとなるようにバッチノードに登録されたすべてのスプライトを1つのバッファに入れ込んで参照しているからですね。その他のパラメータは頂点に備わっている情報なので、1つながりのバッファにしてしまえば1度に送れます。
 というわけで、cocos2dでは1.0の時からそうだったようですが、スプライトの色は頂点カラーへセットすることでバッチ描画を可能にしてたんですね。これを使わない手はありません。
 シェーダーでパレットチェンジするための色変化情報は頂点カラーへもたせましょう。頂点カラーはRGBAの4チャンネルしかないので1枚のスプライトのなかに持てるパレットインデックスは4種類しか持てなさそうです。その中でアルファにはスプライトの透明度が入っているので使えないでしょう。ということでRGBのチャンネルに色変化情報を与えてやって1枚のテクスチャにつき3色変更可能というようにしました。

●パレット処理の仕様
・テクスチャのRGBをそれぞれ好きな色に変更可能
・変更後の色は頂点カラーRGBへ3色セットする(Rが1色目、Gが2色目、Bが3色目)
・セットする色はビット圧縮をかけて入れる(RGB=3:3:2bit)
 ビット圧縮で8bitに収めたのはccColor3BでCCSpriteを継承したクラスに色をセットしたり、cocos2d通常の描画ルーチンを流用するためです。ビット圧縮はHSVとかYUVを使った方が色の再現性がいいかもです。そのうちやってみます。

●パフォーマンスはどうかな?
上:cocos2d 1.0 白テクスチャに色設定して重ねたもの


左:cocos2d 2.0 白テクスチャに色設定して重ねたもの
右:cocos2d 2.0 でシェーダーで色変化させたもの

 3つテストしました。
 前回作成した上のものは、アイドル時には60FPSが出ることもありましたが、画面がスクロールすると描画される頂点数が変化して頂点バッファを作り直すのに時間がかかるようで、最低では25FPSまで下がるようです。
 左のものは、同じソースをcocos2d 2.0で動作させたものです。60FPSから落ちることなくスイスイ動いています。これでいいんじゃないかという気もします。cocos2d 2.0からはフレームレート表示に上から、ドローコマンド数、処理時間、FPSの3つが表示されます。バッチが効いているのでドローは3(背景、キャラたち、ラスター線)です。処理時間はアイドル時0.007で、画面スクロール時には0.017までかかります。
 右のものは、パレットチェンジシェーダーで表示したものです。これも60FPSから落ちることはありません。バッチ描画ができているのでドロー数もかわりません。処理時間はアイドル時0.004で、画面スクロール時には0.017までかかります。
 というわけで、なかなかの好成績です。結論から言うと通常描画が速いのでシェーダーでやらなくてもいいかもという気もしなくもないですw cocos2d 2.0は描画が速いですね。

 今回は以上です。
 今度6/21に行われる予定のcocos2d bootcampでcocos2d 2.0のシェーダーについてのLTをたぶんやるとおもいますので、もしよろしければ聞いてください。

2012/05/12

cocos2d 2.0への移行


●ライブラリの移行
 既存のcocos2d1.0系プロジェクトを2.0へ移行するのはとても簡単です。基本はlibsグループ以下に登録されている各種cocos2dのソースを一旦削除して、2.0系のものを入れなおすだけです。これは今までのcocos2dのバージョンアップでも同じですね。
 
 1.0系ですと、大体以下のものが置かれてます。
 ・cocos2d
 ・CocosDenshion
 ・cocoslive
 ・FontLabel
 ・TouchJSON
 
 これを2.0にすると以下のようになります。
 ・cocos2d
 ・CocosDenshion
 ・kazmath

 kazmathは見慣れないものですけど、中身をみるとベクトル・マトリクス演算系のライブラリだということが分かります。OpenGLES2.0ではマトリクス演算が弱くなるので、そのあたりを補完するためにはいっているのだろうと予想します。
 
●その他の設定
 さて、上記入れ替えと、いくつかProjectのBuild Settingsに値を変更しないといけません。
 
・Header Search Pathsの指定
 なぜかkazmathへのパスが通らないことがありますので、以下のような感じで設定しておきます。
 $(SRCROOT)/../cocos2d-iphone-2.0-rc1/external/kazmath/include/
 
・コンパイラの変更
 Apple LLVM compiler 3.1 にします。

・PreprocessingへDefineを追加
 Apple LLVM compiler 3.1 - PreprocessingのReleaseへ値を追加します。
 NDEBUG
 NS_BLOCK_ASSERTIONS=1
 以上の2つです。別にこれがなくても困りませんが、cocos2d 2.0のテンプレートでプロジェクトを生成すると上記の設定が行われるので、やっておいたほうがいいでしょう。

●初期化周りのソースの修正
 cocos2d 2.0では1.0系と違ってglViewの生成に関する部分が大きく変更されていますので、AppDelegate.mとかを2.0で生成したプロジェクトのソースをコピペして少し整える必要があります。あと、いままで使用していたRootViewController.mは必要なくなるので、削除します。cocos2dのフレームバッファ初期化部分をカスタムしていない人はそれほど大変な作業ではありません。カスタムしてトリッキーなことをさせていた場合には何がどうなっているか理解してから修正しましょう。
 あんまり簡単に修正できたので、僕の認識では、libsだけ入れ替えたら普通にビルドできましたよーとか勘違いしてましたが、初期化部分はソースの修正必要です。


 以上で移行完了です。普通にビルドも通るとおもいます。ひょっとしたらフレームワークが足りないことがあるかもしれませんが、怪しいところはGameKitくらいですかね。
 公式の移行ガイドがここにありますので、参考にしてみてください。変更になったAPIなども書かれていますので、複雑なプロジェクトの場合には影響があるかと思います。
 
 ではー。

2012/05/10

Unity ShaderLabでのあれこれ

 最近Unityを触っています。ShaderLabでiOS向けシェーダーを書いているのですが、その中で気づいたことなど書いてみます。

●モバイル向けシェーダーの心得

 各種Unity勉強会に出ましたけど、iOS等モバイル向けシェーダーではリアルタイムライティングはご法度ということ。普通はバーテックスシェーダー内でライトマップを参照したり、ライトプローブを参照して色を取ってきてフラグメントシェーダーで色を載せるということをやるようです。
 いい例となるのがShadowGunのシェーダーサンプル。このアプリではリアルタイムライティングは完全に行っていませんでした。背景など動かないものはライトマップの参照がメイン。カメラからの相対ベクトルでの擬似スペキュラーライティングを若干与えているので、これについては平行光源での頂点ライティングと同じくらいの処理負荷はかかるものと思われます。キャラでは、ライトプローブ参照と擬似スペキュラーライティングでした。
  iOSでのシェーダーサイクルは以下の数値。(PVRUniSCoEditor調べ)

背景用シェーダー
 vert cycles: 72
 frag cycles: 6

キャラ用シェーダー
 vert cycles: 86
 frag cycles: 28

 となっています。背景のフラグメントシェーダーが軽いことがよく分かります。6cyclesという数値はUnity標準のMobile/Unlit(Support Lightmap)での7cyclesよりも軽い数値なのが驚きです。

 速いシェーダーを書くには、なるべく演算精度を下げるのが良いです。floatなんで言語道断。halfもかなり絞りたいです。fixedがほとんどになると思います。あとは、あたりまえですけど、演算コード量を減らすことですね。
 さらに、コツとしてフラグメントシェーダーは描画面積に対して掛け算で重くなっていくので、バーテックスシェーダーで事前に処理出来る部分はなるべくそっちで計算させておくというのもいいです。ShadowGunのシェーダーでバーテックスシェーダーのサイクル数が多めなのはそのせいですね。
 ちなみに、バーテックスシェーダーで事前計算した値はもちろん頂点毎の情報となりますが、フラグメントシェーダーへ値が受け渡された時点で頂点間で値が補間されますので、まあまあ良い感じになりますよ。


●Unityでのバッドノウハウ

 ライティングがご法度ということでなるべくライティング計算を行いたくないのですが、ライトカラーだけは参照してどうにかしたいとか、ライトベクトルだけ参照して良い感じに使いたいとかあると思います。
 で、普通はSurfaceシェーダーを使ってライティング計算を書くわけなんですけど、Unity内部で何が行われているかわからないので、無駄を省くのが難しいとかありますよね。

ライトカラー参照
 てなわけで、以下のようなコードで直接参照したくなります。

SubShader {
  Pass {
    Lighting Off
  }
  CGPROGRAM
  #pragma vertex vert
  #pragma fragment frag
  #include "UnityCG.cginc"
  struct v2f {
    float4 pos : SV_POSITION;
    fixed2 uv : TEXCOORD0;
    fixed3 col : COLOR;
  }
  v2f vert(appdata_base v) {
    v2f o;
    頂点演算は省きます。
    o.col.rgb = _LightColor0.rgb;
  }
  フラグメントシェーダーは省きます。
  ENDCG
}

 でも、なんかライトの色が取得できないことがありますね。Lighting Offが効いているみたいです。 というわけで、一応ライティングしますよって宣言してやらないと色が取れないみたいです。Passの前にTagで指定します。
Tag {
  "LightMode" = "Vertex"
}

 みたいにやります。すると色がちゃんと取れます。ちなみに、unity_LightColor[0].rgbでも取得できます。cgincを読むとこっちのほうが後々まで使用できる宣言みたいですお。

ライトベクトルが取りたい
 はい。これなんですけど、公式ドキュメントに書かれている_ObjectSpaceLightPosが宣言されていないらしく、一見取得出来ないように見えます。なので、しかたなくSurfaceシェーダーでやるしかないかなぁとか思うのですけど、unity_LightPosition[0].xyzで行けます。

実機でGLSLコンパイルが通らない!
 なんかね、Unity上では正しくシェーダーがコンパイルできて表示も問題なくても、XCodeで実機ビルドすると実機での実行時にGLSLが正しくコンパイルされないことがあるんですよ。原因はバーテックスシェーダーでのVaryingパラメータの命名が同名にされてしまうことがあるということ。
 現象としては、このコミュニティでの記事と同じです。xlv_という変数が大量に宣言されちゃうみたいなんですね。
 これを解消するには、struct v2fでの中身にsemantic nameで何らかの指定をつけておくと良いというもの。
struct v2f {
  float4 pos : SV_POSITION;
  fixed2 uv : TEXCOORD0; <これとか
  fixed custom : TEXCOORD1; <これとか
  half4 custom2; <これはだめ
}

 みたいな感じです。カッコ悪いしなんだかなぁと思いますけど、これで実機実行時には、xlv_TEXCOORD1とかで参照されることになって名前がかぶらなくなるわけです。…ひどい。ひどすぎるぜUnity!

実機だと描画結果が違う!
 MacとかでUnity Editor上でみてる描画結果と実機での描画結果が違うことがあります。特にfixed変数を使っているところで。まあ、これは普通にGLSLを書いてOpenGLESでやってるときにも起こることですけど。
 この場合は、fixedの部分をhalfに置き換えていくと描画結果が一致します。どこを戻さないといけないのか試して少しづつやるといいです。なるべくfixedを使ったほうが軽いですから。
 ちなみに、GLSLベタ書きの場合は、lowpが問題になってます。mediumpに変えていくと直ります。同じ事ですね。


 以上です。最近苦労してたことが大体わかってきたので、まとめておきました。Have a nice shader life!

2012/01/14

cocos2dでパレットっぽいことをしてみたい



 こんな感じに同じキャラなんだけど色違いのキャラをいっぱい出したい場合、2D表現が主流だった頃はパレットの色を変更することで実現するということがメジャーでした。しかし今の主流はテクスチャを使用した画像処理なためパレットの効果を入れるにはちょっと大変です。cocos2dもOpenGLベースなので条件は同じですね。ということで実現するには…と考えてみた。

●アイデア
1,テクスチャ書き換え
2,白い素材を用意し、マテリアル色指定
3,シェーダーでリアルタイム書き換え

 1と2の方法は「cocos2d for iPhone 1 Game Developer Cookbook」に処理の紹介がありますので、そちらがわかりやすいです。



1,テクスチャ書き換え
 肝はCCTexture2DMultableというテクスチャ書き換え機能のついたCCTexture2Dクラスを使うことですね。これはOpenGL側で管理しているテクスチャメモリを書き換える為のメソッドが追加されたCCTexture2Dだと思えば良いです。

 利点:元になるテクスチャが何色でもOK。

 欠点:色変更後のテクスチャはそれぞれ別にメモリに載るので不経済。テクスチャが共通じゃないのでCCSpriteBatchNodeが使えない。そのため描画が多少遅い。



2,白い素材を用意し、マテリアル色指定
 これは元の絵のなかの色を変えたい部分だけを切り分けた別レイヤーとして素材を用意し、白で描いておく。これを使ったCCSpriteを色指定することで色を載せるというもの。cocos2dの基本機能だけで実現できます。

 利点:テクスチャが共通化するのでCCSpriteBatchNodeが使え、描画が高速なはず。省メモリ。

 欠点:色を変えたい部分を分けた素材を用意するのが手間。



3,シェーダーでリアルタイム書き換え
 OpenGLES2以降であればピクセルシェーダが使えるので、描画時にリアルタイムに色変更して表示することができるはず。

 利点:テクスチャが共通なので省メモリ。

 欠点:僕はシェーダー作ったこと無い。OpenGLES2に対応したcocos2d2.0のベータ版を使わないとならない。描画が遅いかもしれない。シェーダーに渡すパラメータが色ごとに変化するので、CCSpriteBatchNodeで処理させるには難しいかも?



 以上が考えられるなぁと思いました。

 まずはお手軽なところからと、1の案をテストしたのが冒頭の画像です。iPhone4での表示ですが、1キャラあたり6レイヤーつかっていて、50体表示という状態です。これで60FPS出ているので処理能力的には十分かもなぁという印象。ただし、MacBookPro上でシミュレーターでの実行結果は30FPSしか出ませんでした。CCSpriteBatchNodeは使ってません。
 cocos2dのcookbookで紹介されているままの方法では、色変更後のテクスチャはCCTextureCacheへ乗らず、同じ色変更を何度も指定すると同じテクスチャが量産されてしまうコードだったため、その部分は自前でキャッシュへ載せて共用するように直しました。

 次は2のアイデアを試そうと思ってます。そのためには絵素材を色別に分離して別々のテクスチャに分配する必要がありますが、手作業でやるのめんどい…。2の方が処理が速いのであれば、最終的には絵素材の分離ツールを作らないとだなぁとか思ったり。

●追記
 2のアイデアを実装して試したところ、CCSpriteBatchNodeの威力もあるだろうが100人出しても60FPSラクラク回ってました。1レイヤー(というかCCSprite)には1色しか指定できないので、色分だけ枚数が増えてますがそんなの全く影響が見られないですね。下図は1キャラ11レイヤー使ってます。ちなみに、CCSpriteのcolorプロパティで色を設定するとその色が乗算されるので黒の輪郭を色変更せずにそのまま残したいという場合は、どこかのカラーのスプライトに入れ込んでしまってもOKです。黒なので他の色を重ねても黒以外になりませんから。

2011/12/25

coco2d Advent Calendar 2011 24日目: オーバートップレイヤーのすすめ

あいかわらず後からの投稿になっちゃいますが、めげずにcocos2d Advent Calendar投稿します。
 今回は、以前さいたまiPhone勉強会で発表した内容からのピックアップで、オーバートップレイヤーというものを実装してみたという例のご紹介です。

<中級編>オーバートップレイヤーのすすめ

●まえがき
 ゲームでは、よく画面を覆う枠があったり、画面上部に体力やスコア表示などが分けて表示されていたりするような画面配置がありますよね。たとえば、以下のような感じです。
 Ysなどの往年の国産PCゲームでは、画面を覆う枠があり枠内がスクロールするゲーム画面。枠の下には体力などのパラメータ表示となっていました。たとえばこんな感じで検索するとヒットしますよ。
 また、ファミコンのゲームではよく画面を上下に区切って画面上部にアイテムやパラメータ表示をして、画面下部はスクロールするゲーム画面として使用するようなものもメジャーでした。たとえばグーニーズとか。ほら検索すると画面がヒットしますよ。

 こんな感じの表示を行う場合に、CCLayerを2つ用意して、1つは枠や画面上部のパラメータ表示用に使用し、もう1つはゲーム画面として使用するように作ると思います。しかし、ゲームの進行で別のマップへ移ったりする際にフェードアウト・フェードインして画面切り替えを行おうとするとcocos2dで普通に推奨されている方法で組むと、パラメータ表示用も含めてフェードがかかってしまい、あまりかっこ良く有りません。ちなみに、このcocos2dで推奨されている組み方はCCSceneにCCLayerを2つ子供にして別のCCSceneへ切り替えるというものです。
 そこで、今回ご紹介する方法が出てくるわけです。


●オーバートップレイヤーの仕組み

 仕組みはこんな感じとなります。通常cocos2dで用意されている画面遷移関数はCCSceneを入れ替えるものですが、この方法ではCCScene自体は基本的に入れ替わらず、ゲーム画面として使用しているCCLayerだけが入れ替わるという構造になります。もちろん、そんな便利なメソッドは用意されていないので、レイヤー入れ替え部分は自作しなければなりません。

 CCSceneの定義自体はこんな感じとなります。
+(MainScene*)sceneWithLayerTop:(CCLayer*)layerTop
           layerBelow:(CCLayer*)layerBelow
{
  MainScene *scene = [MainScene node];

  layerTop.tag = kLayerTop;
  layerBelow.tag = kLayerBelow;

  [scene addChild:layerBelow z:0];
  [scene addChild:layerTop z:2];

  return scene;
}


 で、画面遷移用にとりあえずこの2つのメソッドを用意してみました。メソッドの中のコードはサンプルコードを参照してください。transitionFadeWithLayerは黒フェードを用いてレイヤーの入れ替えを行うものです。transitionFlowerWithLayerはオマケ的なものですが、花で画面が埋め尽くされて花が画面上から去るとレイヤーが入れ替わっているというものです。
- (BOOL)transitionFadeWithLayer:(CCLayer*)layer
           duration:(ccTime)d;

- (BOOL)transitionFlowerWithLayer:(CCLayer*)layer
           duration:(ccTime)d;



●実装の注意点
 実装にあたって、以下の部分に注意しました。
・関係するレイヤーのタッチイベントの無効化だけでは余り効果を発揮しなかったので、画面遷移の重複が起きないように注意した。
・トランジション中フラグを用意して、トランジションメソッドが複数動作しないように制御した。
 と、こんな感じですが、荒削りなので実際に使用するにはもう少し改善の余地はありそうです。でも、夢が広がると思います。


●夢がひろがりんぐ
 もともとオーバートップレイヤーが欲しいなと思ったのは、GameCenterでのアチーブメント解除表示を行う際に画面上部などにアチーブメント情報を表示し始めたとしても、その後すぐに画面遷移が行われてしまった場合にアチーブメント表示が消えてしまうのをなんとかしたいと思ったところから発想しました。もちろん、ネットをググれば同様の問題の対処としてcocos2dのCCSpriteオブジェクトなどをcocos2d外のものに適用して表示させる方法があったりしましたが、なんだかスマートじゃないなと思っていたのです。どうせなら表示はすべてcocos2dの中で完結させたいと。
 オーバートップレイヤーがうまく動いてしまえば、往年のゲーム的な画面表示が簡単に行えるので、これはいいんじゃないでしょうか。おすすめですよ。

 ではでは、今回はこのへんで。

2011/12/22

coco2d Advent Calendar 2011 22日目: Tiledの使い方講座

cocos2d Advent Calendar 22日目としてさくっと書きました。
 cocos2dとはちょっとズレた話かもしれませんが、CCTMXTiledMapを活かそうと思ったら使うことになるTiledというマップ作成ツールの使い方について解説します。

<初級編>Tiledの使い方講座

●基本機能
 Tiledは正方形(トップビュー、サイドビュー)、平行四辺形(クォータービュー)、六角形(ヘックス)のスクロール可能なマップを作成するのに非常に便利なツールです。もともとはPythonのゲーム用に作られたこのツールですが、cocos2dのCCTMXTiledMapクラスでサポートされた形式tmx形式を出力可能なツールとなっています。というか、cocos2dの方がtmxに対応させたということになりますか。

 以下のスクリーンショットを見てもらえれば、どんなマップが作れるのかよく分かると思います。





 一番上のマップはHungryMasterで使用したもので、トップビューとなっています。真ん中はTiledのサンプルに用意されているもので、トップビューなのですが、左上に向かって上層がつながって表示されているややパースペクティブなもの。一番下の例はクォータービューとなっております。クォータービューのタイルチップはこちらのものを使用させてもらいました。
 マップはレイヤー構造を持てますので、それを利用することで、地面、建物、2階部分などをレイヤー分けして載せていくことができます。クォータービューのものを例に取りますと、地面レイヤーだけでは以下のようになります。



 これに建物レイヤーを重ねることで、次のようになります。




 さらに、オブジェクトレイヤーという範囲指定が行えるレイヤーを設定し、範囲指定を行うと以下のように設定が行えます。この範囲を何に使うかはゲーム部分で自由に利用すれば良いのですが、得られるの矩形の4点となります。




●実践してみましょう

 まず「ファイル>新規ファイル」コマンドにて、どんな形状のマップを作成するか設定します。ここでは、平行四辺形の並んだクォータービューマップにしてみましょう。これって結構たいへんなんですよね。ここで指定する数値は平行四辺形の地面部分が収まるサイズのドットを指定します。使用するマップチップの画像はこれを使用してみましょう。



 こんな感じに空のマップが用意されます。さらに、使用するマップチップを登録して配置可能にしましょう。
 「マップ>新しいタイルセット」コマンドを実行し、タイルセットとして登録します。



 この時に、指定するタイルの高さは透明部分も含めたサイズになりますので、地面部分の高さしないように注意しましょう。
 正しく登録できると、こんなふうにタイルを配置可能です。



 次は、壁を配置しましょう。新しいレイヤーが必要になります。「レイヤー>タイル・レイヤーの追加」を実行すると、タイルレイヤーが1つ増えます。そのレイヤーをアクティブにしてタイルを配置すれば上のレイヤーに物が置けます。



 どんどん上層レイヤーを増やしていけば、2階、3階などどんどん増築できます。



 再度に範囲指定用のオブジェクトレイヤーを追加してみましょうか。
 「レイヤー>オブジェクト・レイヤーの追加」を実行すると範囲指定用のレイヤーが生成されます。これをアクティブにして、「オブジェクトを追加」アイコンをアクティブにすると、範囲指定ができます。



 以上です。
 駆け足ですが、ざっと使い方についてのみ解説してみました。

2011/12/10

coco2d Advent Calendar 2011 10日目: cocos2dキャラクタークラス設計の考察

cocos2d Advent Calendar 10日目、2回目の投稿なのですけどすみません。まだまだ空きはあるので我こそはという方はどしどし参加してみてください。前回、12/9日目の記事は@mybさんのこの記事でした。「CCMenuでラベル付きボタン、長押しボタン

 さて、今回はcocos2dでゲームを作る上で考え方の参考になるような記事を書ければと思ってこれを書いています。


<初級編>cocos2dキャラクタークラス設計の考察

●ゲームにおけるキャラクターに必要な要素
 ゲームには多くの場合キャラクターが登場します。プレイヤーキャラクター、エネミーキャラクター、アイテムなんかもキャラクターに含まれるかもしれません。落ち物パズルなどでも落ちてくるブロックのパーツはキャラクターのようなものと考えられます。ゲームキャラクターにはどんな要素が必要なのか、まずは考えてみましょう。

・横スクロールのジャンプアクションの場合(スーパーマリオブラザーズなど)
 A・キャラクターの絵
 B・動作によって変化する絵のパターン、場合によっては動く
 C・画面上の障害物によって移動制限がある
 D・何らかのアイテム効果によって変化することがある
 E・やられると画面から消える
 F・武器を発射することがある
 G・別のキャラクターを発生させることがある

・トップビューのアクションRPGの場合(イースなど)
 A・キャラクターの絵
 B・動作によって変化する絵のパターン、場合によっては動く
 C・画面上の障害物によって移動制限がある
 C2・画面上の建造物などによって一時的に隠れて見えなくなることがある
 E・やられると画面から消える

・シューティングゲームの場合(ゼビウスなど)
 A・キャラクターの絵
 B・動作によって変化する絵のパターン、場合によっては動く
 E・やられると画面から消える
 F・武器を発射することがある
 G・別のキャラクターを発生させることがある

 ざっと思いつく限り書いてみましたが、よくあるゲームジャンルではこんなところが求められるでしょうか。パズルゲームは特殊なのでここでは省きますね。他のゲームよりも求められる要素は少ないはずです。あとで復習がてら考えてみてくださいね。
 では、代表的な要素であるA, B, Eを考えてみましょう。


●キャラクターの絵を扱う
 基本的なCCSpriteの扱いとそれほど変わりません。そのままCCSprite型をキャラクターとして扱って構わないことも多いでしょう。キャラクターの絵が変化する場合には少し要素が増えます。たとえば、絵が変わるたびに新たにCCSpriteを生成して割り当てるとすると以下のようになります。gmCharaという名前でクラス定義したとして仮に書いています。

@interface gmChara : CCNode(あるいはNSObject) {
  CCSprite *sprite; //キャラクター用スプライト
  CGPoint accel; //加速度
  int vital; //体力
  ...などなど
}


 あるいは、CCSpriteを継承して絵のフレーム自体を変更する場合は以下のようになるでしょう。

@interface gmChara : CCSprite {
  CGPoint accel; //加速度
  int vital; //体力
  ...などなど
}


 絵のフレームを変更するには、以下のようなコードで行うことができます。CCSpriteで使用しているテクスチャに必要な絵が全て入っていて、その一部を切り出して使用しているという状況が前提となりますが簡単に絵を変更できます。CCSpriteを再生成して割り当て直すよりも高速なはずです。CGRECT_FOR_NEWは新たに指定したい切り出し用のCGRectです。spriteはCCSprite型とします。

[sprite setTextureRect:CGRECT_FOR_NEW];


 さて、これらの例に移動用の加速度パラメータはあるのに、移動後の位置を格納するプロパティは用意されていません。そうです。CCSprite自体に位置を扱うプロパティが存在するので、そちらを使う方が無駄が少ないからですね。ゲームの要求として表示位置とは別に位置情報が必要になる場合は別途プロパティを用意するべきでしょう。サイズ用のプロパティが存在しないのも同様の理由ですが、サイズの場合は当たり判定の大きさが絵の大きさとは異なることも多いので、別途プロパティを用意したほうがいいかもしれません。

 実は、ElectroMasterやHungryMasterでは上記の方法でキャラクタークラスが構築されています。おそらく無駄が多いと思います。やっちまいましたね。


●やられると画面から消えるために
 敵などをやっつけると、画面上から姿が消えるのがゲームのお約束であることはみなさんもお分かりのはず。
 消すだけなら簡単です。絵を取り扱うCCSpriteを表示しなければ良いのですから。しかし、表示を消しただけではキャラクターの存在自体はメモリ上に残り続けてしまいます。ですから絵を消す代わりにキャラクターのオブジェクトを解放しちゃってもいいかもです。いいかもしれません。ゲームに登場するキャラが有限である場合はこれで十分でしょう。
 では、ゲームに登場するキャラが無限に発生する場合は困ったことになります。敵が発生するたびにキャラオブジェクトを生成してゲームへ追加していくのも手です。一度倒してしまったキャラオブジェクトを再利用して追加のキャラとして復活させることもいいかもです。

 A・キャラが有限の場合
  ゲーム開始時にキャラオブジェクトを全て生成しておいて、倒すたびにクラス解放でいいでしょう。
  倒せば倒すほど処理も軽くなりますね。

 B・キャラが無限に発生する場合
  1. キャラが発生するたびにキャラオブジェクトを生成してゲームへ追加とする。普通にやるとオブジェクト生成時のオーバーヘッドが大きいかもです。次善の策としてはひな形オブジェクトを用意しておいて、そのコピーを追加する。(newChara = [[chara copy] autorelease];とすればコピーが生成できますね)

  2. キャラオブジェクトを再利用する場合。一度に画面に登場するキャラ数は事前に生成しておいたキャラオブジェクト数に制限されますが、キャラを初期値に最設定するメソッドを用意しておけば高速に復活させられそうです。また、一度に取り扱うキャラ数限界も管理しやすいので、メモリや処理能力的にも優しいかもです。手前味噌ですが、ElectroMaster, HungryMasterではこの方法を取っています。


●動作する絵のパターンを取り扱う(パターンアニメーション)
 キャラクターが移動すると、歩いたり走ったり、ジャンプボタンでジャンプしたり。敵に触ったら痛がったりというようなキャラクターの動きに関するものです。これが無いと物足りないですよね。多くはキャラクターの絵を複数用意しておいて、場合によって絵を切り替えて動いているかのように見せています。パターンアニメーションですね。
 cocos2dの入門書ではパターンアニメーションについて扱っていて、そういう動作を割りと簡単に与えることができるように説明されています。単一のパターンアニメーションを指定するだけなら難しくありません。よく悩んでる人が多いのは、右に移動すると右向きの絵で歩く、左に移動すれば左向きで、上なら上向き、下なら下向きといったように場合に応じて使用されるパターンアニメーションが変化するときにどうするのがよいかということでしょう。

 パターンアニメーションの定義は以下のようにできます。これは入門書にも書かれていますね。下記の例では、512x64のテクスチャを用意してあって、横方向に8枚の絵が並んでいる状態を元に、64x64の絵が0.3秒毎に変化していくというパターンアニメーションとなります。生成されたパターンアニメーションはCCRepeatForever型(CCAction系列として取り扱える)として最終的に定義されます。TEXTURE_FILE_NAMEはスプライトに使用するテクスチャのファイル名です。


CCTexture2D *texture =
  [[CCTextureCache sharedTextureCache]
    addImage:TEXTURE_FILE_NAME];
NSMutableArray *animFrames = [NSMutableArray array];

for (int x=0; x<8; x++) {
  CCSpriteFrame *frame =
    [CCSpriteFrame frameWithTexture:texture
      rectInPixels:CGRectMake(64*x, 0, 64, 64)
      rotated:FALSE
      offset:CGPointZero
      originalSize:CGSizeMake(64, 64)];
  [animFrames addObject:frame];
}

CCAnimation *animation =
  [CCAnimation animationWithFrames:animFrames
    delay:0.3f];
CCRepeatForever *action =
  [CCRepeatForever actionWithAction:
    [CCAnimate actionWithAnimation:animation
      restoreOriginalFrame:NO]];


 これをCCSpriteへ適用してパターンアニメーションを実行するには、通常以下のようにします。

[sprite stopAllActions];
[sprite runAction:action];


 こんな手順を適用するパターンアニメーションが変化するたびに行えば、パターンアニメーションを変更することが可能ですが、stopAllActionsを実行するとそれまで適用されていたアクションが解放されてしまうので、次に同じアクションを最適用する場合でも、新たに定義しなおしたアクションを与えなければなりません。となると定義に必要な処理が無駄になってしまいます。(stopAllActionsではなくstopActionを使用して名指しで停止するアクションを指定してあげても良いですが、同じことです)

 では、パターンアニメーションのアクション再定義に必要な処理を軽くするためにはどうすれば良いでしょうか。キャラクターの絵について説明した部分でも書きましたが、1つの方法としては事前に定義しておいたアクションオブジェクトをコピーして与えてあげれば良いです。あらたにオブジェクトを初期化するよりも処理は軽いはずです。もう1つ考えられるとすれば、stopActionなどを行なっても解放されないようにretainしておくのも良いです。retainしておくにはCCArrayやNSArrayなどに格納しておくのが呼び出すのも楽なのでいいでしょう。

 コピーして与える場合は、こんな感じでしょうか。事前にactionとして定義しておいて、使用する際に随時コピーします。コピーして与える場合は、キャラ毎にパターンアニメーションを保持するのではなく、キャラの種類毎に保持するような運用の場合に活きてきます。というのも単一のアクションオブジェクトを複数のスプライトに対して適用するとタイマーがそれぞれのスプライトから更新されて動作がおかしくなるため、各スプライトへ別々のオブジェクトを与えなければならないためです。個々のスプライトでアクションを保持していた場合はその点ではコピーする必要が無いと思われます。

[sprite runAction:[[action copy] autorelease]];


 でも、actionをまとめて管理しておくにはやはりNSArrayやCCArrayに入れておくと思いますので、以下のようにしますよね。そうすると、CCArrayなどに格納した時点でretainされますので、アクション終了後に解放されなくなります。というわけで、メモリの占有率が変化しないこっちのほうがおすすめです。

@interface gmChara : CCSprite {
  CCArray *actions; //パターンアニメーション保持用
  ...などなど
}


 実際の適用はこちら。0番目に登録されているパターンアニメーションを適用させています。spriteはCCSprite型とします。

[sprite runAction:[actions objectAtIndex:0]];


 以上のような取り扱いでうまく制御ができると思います。

 ところで、ElectroMaster, HungryMasterでは、かなり無駄な組み方をしていたことに、この記事を書きながら気づいてしまいました。上記のようなCCArrayなどに予めパターンアニメーションを格納して管理していたところまでは一緒なのですが、CCRepeatForeverを噛まさずに入れておいてアニメーション適用時にいちいちCCRepeatForeverを生成して適用しておりました。なので、その部分がオーバーヘッドになってました。これには理由があって、毎回新たにCCRepeatForeverを生成しないと再生開始フレームが最初からにならないかもしれないって思っていたからです。しかし、実際にはそんなことはなく過去に再生済みのアクションであっても最適用すれば最初から再生されますので、要らぬ心配でした。それから、アクションをコピーしてから適用しておりました。これも無駄で、CCArrayなどに格納した時点でretainされいるためにアクション終了時に解放されないわけです。なので、解放を恐れてコピーすることも無かったということになります。以下はそのダメコードの例です。

id action = [[[actions objectAtIndex:0] copy] autorelease];
[sprite runAction:[CCRepeatForever actionWithAction:action]];



 今回の記事はここまでにしておきます。今回解説しなかった障害物判定については機会がありましたらまた説明できればと思っています。次は@Seasonsさんの予定と聞いております。楽しみー。ではー。

 追記:11日目の記事はこちら:@Seasonsさん「cocos2dパフォーマンスチューニングTips

2011/12/02

coco2d Advent Calendar 2011 2日目: cocos2dでオリジナルフォントクラスを作ろうぜ!

@yoichinejiさんから連絡があって、なんでもcocos2dのAdvent Calendarをやるとのこと!なんだか楽しそうなので、1も2も無く参加してみましたー。というわけで、以前さいたまiPhone勉強会で発表した内容と同内容になりますが、詳しい解説を入れて再録させてもらいました。ネタはフォントクラスについて。一応中級編とさせてもらいます。内容的にはcocos2dを踏まえていますが、基本的なビットマップフォントの考え方みたいな感じになりますでしょうか。僕が勝手にこうだと思い込んでいるものなので、いろいろ間違ってたり、効率悪い点などご指摘いただけると幸いです。
 cocos2d advent calendarのatnd:http://atnd.org/events/22814
 以下本文ですー。


<中級編>cocos2dでオリジナルフォントクラスを作ろうぜ!

●フォントについてあれこれ

 ゲームではよく変わったフォントで文字が表示されることが多いですよね。特に昔のゲームなんかはゲームの世界観を表す上でかなり重要なポジションを担っていたと思います。iOSデバイスが出てからはそのスペックに適したゲームとしてレトロ風味のものも多数出ていますが、やはり時代性を表す上でドットで作られたフォントは美味しい要素です。
 以下の図は拙作のアプリからですが、上図が等幅フォント、下図はプロポーショナルフォントで日本語を取り扱っています。



 cocos2dにはTrueTypeフォントを直接展開してテクスチャ化し使用する文字スプライトクラス(CCLabelTTF)や、ツール(Hiero Bitmap Font ToolやBMFontなど)で事前にテクスチャ化して準備しておき、それを切り出して使用するためのフォントクラス(CCLabelAtlas, CCLabelBMFont)が元々用意されていますが、今のゲームのテイストの場合は良いですが、レトロ風味ということでいうと向いていません。
 なぜなら、公開されているツールを使用する場合だと、テクスチャにフォントがアンチエイリアス有りで展開されてしまったりするからですね。まあ、テクスチャの作り方にも依るので一概に向いていないとは言えませんが…。なお、上記の上画像の下の方に表示されている日本語の文章はCCLabelTTFを使用したもので、それ以外の文字はテクスチャからの切り出しです。
 あと、日本語のことを考えるともっと向いてない要素があります。基本的にキャラクターコードに従ってテクスチャへ展開するので、無駄な領域が多く必要になってしまうわけです。その点、自前でクラスから作る場合は、トリッキーなことも含めて自分の管理下にあるわけで、仕様から好きに決められて良いというところがあります。
 正攻法でいけば、cocos2dのフォントクラスに合うフォントファイルとテクスチャを用意すれば良いですが、それは別の機会にとっておきましょう。正直言うと調べて対応するより自分で作ってしまったほうが早いってのもあります。あと、どうせ作るならcocos2dのフォントプロトコル(CCLabelProtocol)に沿った作りにすればよかったのですが、それに気づいたのは作った後だったので、すみません。以下の例はかなり変則的です。

 と、そんな前提がありまして、自前でフォントクラスを用意してみてはどうですか?という例のご紹介です。
 サンプルコードはこちらからどうぞ:http://xionchannel.no-ip.org/advent_calendar_cocos2d_day2.zip


●等幅フォントクラスを作ってみよう!

 まずは、ドットを打って文字をデザインしていきます。要領はドット絵と同じですね。作りを簡単にするには等幅フォントがいいので、その場合は、格子状に文字を配置しながらデザインしていきます。

 フォント用のテクスチャができたら、それを切り出して使用するということになります。フォントクラスを簡単に設計するためには、文字コード順に文字が並んでいると良いです。途中必要ない部分があれば省いてしまっても良いです。省かれた部分のコードの取り扱いはフォントクラスの方で対処すれば良いです。
//指定のキャラクターのCGRectを返す
- (CGRect)getRectWithASCII:(char)ascii {
  char c;
  if (ascii>='0' && ascii<='9') {
    c = ascii-'0';
  }
  else if (ascii>='A' && ascii<='Z') {
    c = ascii-'A'+10;
  }
  else if (ascii=='!') {  //!
    c = ('Z'-'A'+10)+1;
  }
  <中略>
  else {
    c = ('Z'-'A'+10)+8;  //スペースに置き換える
  }
  return CGRectMake(8*(c%8), 8*(c/8), 8, 8);
}

 こんな風に文字コードを元に切り出しCGRectを生成して、CCSpriteを作成するようにします。あとはスプライトを並べていけば文字列の表示が達成できるというものです。等幅フォントの肝はこんな感じになります。簡単ですね。切り出しはもっとスマートにやる方法もありそうですね。汚いソースですみません。


●プロポーショナルフォントクラスを作ってみよう!

 レトロ風味から少し高級な印象になりますが、プロポーショナルフォントは見た目に美しいので、それをつくろうとする場合はどうしましょうか。はい。それぞれの文字の幅が異なる可能性があるので、その情報をどこかに持たなければなりません。

 まずはフォント用のテクスチャを用意してみましょう。横書き前提とすると、文字の高さは変化せず幅のみ変化することになります。たとえば以下のような感じでテクスチャを作ることになります。なお、縦書き用プロポーショナルフォントとする場合は、幅が変化せず、高さが変化するような作りが良いでしょう。もちろん高さも幅も変化するように作ることも可能ですが、それについては自分で効率良い配置を考えてみてはどうでしょうか。

 さて、テクスチャは用意できたとして、切り出しサイズをまとめる必要があります。どのような形式でも良いですが、iOSっぽくplistにいれてみた場合、以下のような感じにできます。データの置き方は自分でわかれば良いので、ほかの並びでも良いと思います。

 このデータを元にフォントクラス側でテクスチャを切り出して文字を並べていくことになります。文字コードとの対応は等幅の場合と同じようにしても良いです。以下の例の場合、カタカナも扱いたかったため文字コードを意識しない構造にしました。切り出し情報の中に対応する文字を情報として入れておき、ソース文字列に合致する文字があった場合に切り出して文字を配置するという流れです。処理は文字コードを使用したものよりも重いでしょう。

 以下は切り出しの例ですが、先ほどのplistをcharactersというNSDictionaryオブジェクトに読み込んでおきます。キーに対応する文字情報を入れてあるので、キャラクターが入ったNSStringを与えてやれば切り出しサイズが得られるという寸法です。
//指定のキャラクターのCGRectを返す
- (CGRect)getRectWithString:(NSString*)s {
  NSArray *rect = [characters objectForKey:s];
  if (rect) {
    float x = [[rect objectAtIndex:0] intValue];
    float y = [[rect objectAtIndex:1] intValue];
    float width = [[rect objectAtIndex:2] intValue];
    float height = [[rect objectAtIndex:3] intValue];
    return CGRectMake(x, y, width, height);
  }
  else {
    return CGRectZero;
  }
}

 以上のようにしてオリジナルフォントクラスの基本的な部分が出来ました。切り出し用plistを用意するのは面倒ですけど、その分、切り出し自体は簡単ですね。あとは、サイズに応じて並べるときにずらすドット数もあわせてやれば、プロポーショナルな文字を並べることができますね。

 なお、一番最初の例では、カタカナのみ日本語対応していますが、濁点や半濁点を別の文字としてまとめることでキャラクター数を減らしています。なので、表示する際に、濁点や半濁点がある場合は、内部で分割して2文字分として処理して表示するように調整を入れています。昔のファミコンゲームでよく使用している手法ですね。そのために、濁点・半濁点の対応表を作っておいて照らしあわせて処理するようにしています。
//文字スプライトの初期化(private)
- (CCSprite*) __makeCharacterSpriteFrame:(CCSpriteFrame*)f
                string:(NSString*)c
                position:(CGPoint*)pos
                isShadow:(BOOL)isShadow
{
  CCSprite *s = [CCSprite spriteWithSpriteFrame:f];
  [s.texture setAliasTexParameters];
  s.anchorPoint = ccp(0,0);
  s.scale = 2.0f;
  if (![c compare:@"゛"] || ![c compare:@"゜"]) { //濁点・半濁点
    s.position = ccp(pos->x -4*2, pos->y +8*2);
  }
  else {
    s.position = *pos;
    if (!isShadow) pos->x += s.contentSize.width*2;
  }
  return s;
}

//カタカナの濁点等を分解する(private)
- (NSString*) __correctString:(NSString*)string {
  NSMutableString *ret = [NSMutableString string];
  for (int i=0; i < string.length; i++) {
    NSString *c = [string substringWithRange:
            NSMakeRange(i, 1)];
    NSString *replace;
    if ((replace = [self.dakuten objectForKey:c])) {
      //濁点の差し替えがあれば差し替える
      [ret appendString:replace];
    }
    else {
      [ret appendString:c];
    }
  }
  return ret;
}


●フォントクラス応用編

 さあ、ここからがおいしいところですよ。おまけですけど。ゲームに於いて文字で表示されたメニューをタップしたり、文字の内容を変更したり、文字の色が変わったり、一文を与えただけで途中改行されたりというようなよく使う機能があると思います。そのあたりをメソッドやマクロとして組み込んでおくとゲームに使う場合にかなり便利になります。下記はそんな例です。

 ここから先は僕も作ってはいませんが、こんなネタもあったらいいんではというものをご紹介。文字がバラバラになって飛んでいくとか、文字が一文字づつカードがめくれるように回転するとか、ピカっと光るとか、シャクトリムシのように伸び縮みするとか、どれもcocos2dの基本機能を使ってアニメーション登録すればいけますが、一文字一文字バラバラに制御する必要があるので、フォントクラスの外側から制御するよりも、フォントクラス内でアニメーションを与えるような作りの方がやりやすいと思います。みなさんもそんな効果をつけてみてはいかがですか?ひと味違った文字表現になって楽しいですよ!

 そんなこんなで、フォントについてはここまで!お付き合いありがとうございましたー。

 3日目は、@ZuQ9Nnさんです。「cocos2d Advent Calendar 2011 -3日目 波紋のアニメーションやてみた。オッパイぷるるんもあるんだよ!