cocos2d-xとLuaでゲームが出来るかな?

cocos2d-xとLuaを覚え書き

Luaをバイトコードに変換してcocos2d-xで読む。

Luaバイトコードにして、cocos2d-xで読んでみた話。

iOSやAndroidのアプリは、簡単にリソース内を覗くことが出来ます。
そうなると、通常Luaスクリプトはただのテキストなので丸見えになってしまいます。
恥ずかしい!

なので、最終的なアプリに入れる場合は、難読化・暗号化などを施します。

独自にこれらの処理を行うのが一番なのでしょうが、出来る限り手抜きでやりたい。


Luaバイトコード

Lua標準のコンパイラにluacがあります。
しかし、WindowsやMac上で、コンパイルしたコードはiPhoneAndroid上で上手く読み込めないそうです。

・Fatal problem: LUA source code encrypt.
http://www.cocos2d-x.org/boards/11/topics/22319

iphone上でLuaバイトコードを生成する
http://p-monster.hatenablog.com/entry/2013/02/12/205003

色々悩んだけれど、これといった解決方法は見つからず。
フォーラムを見てもluacを使って簡単に良い方法は無いようです。


ところが、cocos2d-x 2.1.3からLuajitになりました!

と、いうことはluacで作成したバイトコードは、そもそも読めなくなりました。


Luajitでバイトコード

Luajitでのバイトコード作成はluajit.exe -bを使用します。

・Running LuaJIT
http://luajit.org/running.html

luajit -bs [ input ] [ output ]とやれば、コメントなどのデバッグ情報を削除したバイトコードが作成されます。

関数名や文字列は丸見えですが、なかなか人の目で見るのは難しいですね。


cocos2d-xでバイトコードの読み込み

上で書いたようにluacで生成したバイトコードは、上手いように読めませんでした。

luajitで生成したコードを読んでみます。

    std::string path = CCFileUtils::sharedFileUtils()->fullPathForFilename("hello.lua");
    pEngine->executeScriptFile(path.c_str());

これで終了です。

何も手を加えること無く、普通に読めてしまいました!
luacで悩んでいたのが馬鹿らしいぐらいに。



Luajitコンパイル Windows7 64bit
手持ちのPCと端末 Win32, android( 2.3.5, 4.0.3 ), iPhone4( 6.0.1) では動作確認できました。

全ての環境で上手く行くか分かりませんが、簡単に難読化できました。


次は、簡単に暗号化したいと思います。

cocos2d-xのAdmob表示のバグ

AndroidアプリにAdmobの広告を表示しようとして、謎のバグにハマったのでメモ

Admobが表示されず、cocos2d-xを終了しようとするとアプリがフリーズするという症状です。

ネットで調べても同じようなバグ報告が見つからなかったので、機種等の環境に依存するのかもです。
なかなか解決できなくて大分時間がかかっちゃいました('A';;

OSは2.3.5、4.0.3の両方で確認。
信ON/OFF関係無く発生します。
再現性としては2.3.5のほうは10%ぐらい、 4.0.3のほうが20%ぐらい。なんででしょうね?

Admobの表示

まず、Admobの基本的な表示方法

    // OnCreate ///////////////////////////////

    // adView を作成する
    adView = new AdView(this, AdSize.BANNER, MY_AD_UNIT_ID);

    // 属性 android:id="@+id/mainLayout" が与えられているものとして
    // LinearLayout をルックアップする
    LinearLayout layout = (LinearLayout)findViewById(R.id.mainLayout);

    // adView を追加
    layout.addView(adView);

    // 一般的なリクエストを行って広告を読み込む
    adView.loadAd(new AdRequest());

logcatは次のようになっています。
DexOpt: --- BEGIN 'ads-XXXXXXXXXX.jar' (bootstrap=0) ---
DexOpt: --- END 'ads-XXXXXXXXXX.jar' (success) ---
adRequestUrlHtml:
Received ad url:

loadADの後、ads.jarを読み込み開始 → 終了 → urlリクエストを投げる → url受け取り
みたいな流れだと思います。


バグが発生時ですが、DexOptが失敗しBEGINの後にENDが送られて来ません。
スレッドを見てみると、失敗時はスレッド1個が終了出来ていないのが見れます。

このスレッドがcocos2d-x終了後も残ってしまうので、アプリが正常に終了出来ずフリーズしてしまいます。

AdView.stoploadingでも止まらず、再びloadAdしても「読み込み中です」エラーが返るだけ。

jarの読み込みで停止してしまうので、リクエストエラーも返ってこない、例外も投げられないという
非常に分かりにくい!


解決方法

いろいろ試した結果、自分なりの解決方法
AdViewの作成と、リクエストの送信を分けるだけです。

    // OnCreate ///////////////////////////////

    // adView を作成する
    adView = new AdView(this, AdSize.BANNER, MY_AD_UNIT_ID);

    // 属性 android:id="@+id/mainLayout" が与えられているものとして
    // LinearLayout をルックアップする
    LinearLayout layout = (LinearLayout)findViewById(R.id.mainLayout);

    // adView を追加
    layout.addView(adView);

その後、処理が軽い所でリクエストを投げます。

   me.runOnUiThread( new Runnable(){
      public void run(){  
         // 一般的なリクエストを行って広告を読み込む
         adView.loadAd(new AdRequest());
      }
   });

ただこれだけです。

どうもadView後loadAdをすぐに投げると失敗するようです。
なのでAdViewの作成と、リクエストの送信分けました。

同じ症状ではないですが、処理が重い時にリクエストを投げると、うまく表示されないという
情報があったので、リクエストはロードなどの処理の後に投げるようにしました。

これが正解かどうかは分かりませんが、今のところバグは発生していません。


機種依存だったり、通信関係のバグは情報も少なくて困っちゃいますね。

Luaでのモーダルレイヤー

先日待望のCococs2d-xの日本語の書籍が発売されました。

その中でモーダルレイヤーの作り方が書いてあり、本のままLuaでやろうとしてちょっと悩んだので書いておきます。

モーダルレイヤー

ゲーム画面の上に確認画面等を表示したとき、確認画面のボタンやタッチだけ反応して、ゲーム画面は反応してほしくない! とうことがよくあると思います。

cocos2d-xでどやってそれを実装するかですが、

  1. ゲーム画面のボタンやタッチの反応をON/OFFする。
  2. ゲーム画面にボタンやタッチのイベントを通知しない。

等があると思います。
1.は簡単に思いつく方法でメニューボタンやタッチをEnabled(false)するだけでOKですが、メニューの数だけ用意する必要があり大変です。
2.は"タッチの優先度"を利用した方法です。メニューよりもタッチ優先度の高いレイヤーを用意して全てのタッチイベントを拾い、それ以下にはタッチ処理をさせないようにします。

いままで1.の方法でやっていたのですが、2.の方法を本で知ったので試してみました。

Luaでのモーダルレイヤーの作り方

まず、本で紹介されているC++での実装方法
CCLayerを継承したModalLayerクラス内のinit()で優先度を設定し、ccTouchBeganでtrueを返すことで、それ以下へタッチイベントを渡さないようにしています。

//kCCMenuHandlerPriority = -128 CCMenuのデフォルトの優先度

bool ModalLayer::init()
{
    ~省略~

    this->setTouchPriority( kCCMenuHandlerPriority - 1)   //優先度をCCMenuより高く
    this->setTouchEnabled(true);                          //タッチを有効化
    this->setTouchMode(kCCTouchesOneByOne);               //シングルタッチで拾う

    ~~~~
}
bool ModalLayer::ccTouchBegan( CCTouch* touch, CCEvent* event)
{
    //モーダルレイヤーですべてのタッチイベントを拾う
    return true;
}


これをLuaでも作ってみますが注意点が2点。
LuaからもCCLayer:setTouchPriority()を使用できますが、これは動作しません!?
setTouchPriority() で優先度を設定しても実際に反映されず、デフォルトの優先度0になってしまいます。
ついでにsetTouchMode()ですが、これも動作しません!?


では、どうするかというと、
CCLayer:registerScriptTouchHandler(nHandler, bIsMultiTouches, nPriority, bSwallowsTouches)
を使用します。

Luaでのモーダルレイヤーは以下のようになります。

function ModalLayer:init()
    ~省略~

    self:registerScriptTouchHandler( 
                    function(...) return self:onTouch(...) end, --タッチイベントハンドラーを指定
                    false,                               --シングルタッチで拾う
                    kCCMenuHandlerPriority -1,           --優先度をCCMenuより高く
                    true                                 --タッチイベントを止めるか
                    )

    ~~~~
end

function ModalLayer:onTouch( eventType, x, y)
    --モーダルレイヤーですべてのタッチイベントを拾う
    return true
end

Luaではハンドラーを登録すると共に、シングルorマルチタッチ、タッチの優先度、イベントを止めるかの設定をします。
注意するのは、4番目の引数 SwallowsTouches = trueにしただけでは、イベントを止められないというところです。C++と同じようにタッチイベントを処理するonTouch内で return trueをする必要があります。



私的cocos2d-xとLuaの開発環境

cocos2d-xとLuaで使っているソフトを紹介。

それなりに歴史のあるLuaですが、これといったIDE・エディタがありません。
とくにデバッグに関してはまともなものが無いんじゃないかな?
いいやつがあったらぜひ教えて貰いたいです。

Luaデバッグで有名なDecodaが最近オープンソースになったそうですが、
cocos2d-xのデバッグが出来るのかな? 今のところ情報は見つけれてないです。


とりあえず、デバッグは置いておいて自分が使ってるソフトの紹介。
・LuaDevelopmentTools
・ZeroBraneStudio

LuaDevelopmentTools

http://www.eclipse.org/koneki/ldt/
eclipseLuaを扱えるようにするプラグインです。

良いところ
Androidと相性がいいです。
eclipseなので、エディタは使いやすい。
・細かい設定も出来る。
・コンテンツ・アシスト、コード補完もあり。
・プラグインを追加すればいろいろできる。
・困ったときの情報が多い。

悪いところ
eclipseが重い。
・グローバルしかアウトラインが表示されない。
・コンテンツ・アシスト機能は一応動く程度。
・コンテンツ・アシスト用のライブラリーは自作する必要がある。
・折りたたみ等、挙動いまいち良くない。
・cocos2d-xのデバッグは不明

eclipseベースなので、汎用エディタとしてはとても良いです。
Luaエディタとしてみるとすこし不満。
デフォルトだと "."で補完候補が表示されますが、":"では出て来ません。
不便なので一部プラグインを修正して使っています。

ZeroBraneStudio

http://studio.zerobrane.com/
cocos2d-xのフォーラムでおすすめされていたIDE

良いところ
・コンテンツ・アシストが出来る。(とても重要)
・軽い
デバッグが出来る(出来た?将来的に出来るようになる?)
・テーブルデータをしっかり解読してくれるので、関数ジャンプ等ができる
・本体自体もLuaで書かれており、なんでも機能追加が出来る。

悪いところ
・設定等が不親切、キーバインドがいまいち
・落ちる。
・コード補完のほうは微妙。
・コンテンツ・アシスト用のファイルは自作する必要がある。
・日本語入力ができない?(なにかいじってしまったせいかも)

コンテンツ・アシストは自分が試したLuaエディタのなかでは一番優秀です。
エディタ自体は不満点がいくつがありますが、本体もLuaで書かれているので、
気合があれば修正できるようです。

デバッグですが、luasocketを使用すればリモートデバッグができるようです。
以前のcocos2d-xバージョンではluasocketを使用する情報があったのですが、
現在のバージョンで使用する方法は見つけれませんでした。
時間があればそのうちチャレンジしたいですが・・・

そのうちcocos2d-xにluasocketが組み込まれるのでは?といった情報もあるので
そちらに期待です。


コンテンツ・アシスト

どちらのエディタもcocos2d-xのコンテンツ・アシストを使用するにはcocos2d-x
luaに公開しているデータが必要です。
LuaCocos2d.cppファイルに書かれているグルーコードから作るのが確実ですが、
フォーラムにアップしてくれている方がいるのでそちらを使用しています。
http://www.cocos2d-x.org/boards/11/topics/23384
※真ん中あたりのcocos2dx.lua.zip ファイル

ZeroBraneStudioはこのファイルを指定のフォルダに入れればOKです。
LuaDevelopmentToolsはLuaDocの形式に加工する必要があります。
http://keplerproject.github.io/luadoc/manual.html



2つ紹介しましたが、自分はLuaDevelopmentToolsを主に使っています。
Androidのビルド、転送がオールインワンで出来ることと、ZeroBraneが
よく落ちるので。(環境によるかもしれません)


ZeroBraneがデバッグできるようになったらそちらに移るかな〜

Lua用プロジェクト

cocos2d-xでLuaを使うためのプロジェクト作成、SDcardからLuaファイルの読み込みについて

現在最新Verのcocos2d-x v2.1rc0-x-2.1.3での説明をしていきます

cocos2d-x Luaプロジェクト

旧バージョンではAndroid,iOS共有プロジェクトを作る場合色々と面倒でしたが、最新バージョンでは簡単に作ることができます。やり方は多所で書かれているので簡単説明。


1.create_project.pyでプロジェクトを作る

create_project.pyを使うことで、マルチプラットフォーム共有プロジェクトを自動で作ってくれます。
ファイルの場所:

  [cocos2d-xのフォルダ]/tools/project-creattor/create_project.py

  ※実行にはPythonが必要です。WIndowsは先にインストールの必要があります。

  Macは最初から入っていた気がする?

コマンドプロンプトで上記のフォルダまで移動したら、以下のコマンドでプロジェクトを作成します。

 python create_project.py -project <プロジェクト名> -package <パッケージ名> -language lua

最後の-languageをluaに指定することでLua用のプロジェクトが作成されます。

成功すれば [cocos2d-xのフォルダ]/projects/プロジェクト名 に作らているはずです。

Android,iOS以外はいらいないので、その他のproj.~を削除

androidADK、iosはXcodeでそれぞれのproj.にあるプロジェクトファイルを使いサンプルが動作することを確認しましょう。


2.Android SDcardからLuaを読み込む

通常はbuild_native.shを実行し、プロジェクトをビルドします。

その際に必要なリソースファイルはassetsにコピーされます。アプリケーションはassetsの中のリソースを読み込むわけです。

これでは、Luaファイルの変更→毎回ビルド(時間がかかる)→apkの転送 となり、Luaスクリプトの強みを活かせません。

そこで、LuaファイルはAndroid端末のSDcardへ直接転送し、アプリケーションにはそこからLuaファイルを読みこんでもらうように変更します。 Luaファイルの変更→SDCardへコピー(即終わる)→即反映

詳しい説明は前回同様にこちらの方が書いてくれていますので参考にします。


#20 まさか、Cocos2d-x 使っているのに C++ 書いてるわけないよね?

http://tech.kayac.com/archive/20_snmp.html


・AppDelegate.cppを変更する

上記のHPを参考にして書き換えます。
HPの説明では、最初のLuaファイルは指定しているので読み込まれるのですが、requireの検索パスが設定されていないので、他のファイルを読み込むことができません。
パス指定を追加します。

const char *luaSearchPath = "/mnt/sdcard/Resources";    // 環境に合わせてLuaファイルの検索パスを指定
pEngine->addSearchPath( luaSearchPath );

また、Luaファイルだけでなく、他のリソースもSDcardから読み込みたい!というときはそちらの指定もする。

std::vector< std::string> resourcesPaths;
resourcesPaths.push_back( "/mnt/sdcard/Resources");      // 環境に合わせてリソースファイルのパスを指定
CCFileUtils::sharedFileUtils()->setSearchPaths( resourcesPaths );
LuaファイルをAndroidへコピーする

Androidへのコピーはadbを使用します。ディレクトリをまるごとコピーするのが簡単です。

adb push <コピーするディレクトリ> <コピー先のAndroidディレクトリ>


これで、アプリケーションはSDcardからLuaファイルを読んでくれるようになります。
なお、ビルド時にassetsへLuaファイルを入れる必要が無くなるので、build_native.shを修正しassetsへLuaファイルをコピーを無効にしておきましょう。
(デフォルトのassetsが先に参照される?ため)



・iOSは?

iOSは自分はやっていません・・・
上記のHPでも説明されていますが、iPhoneにはSDCardがなく面倒くさいようです。
また、Androidと違い、シミュレータの動作もしっかりしているのでMac上でテスト→実機で確認という方法を取っています。
やってみたいという方は、上記HPを参考にお試しあれ



大分長くなったけれど、これでLuaファイルの読み込み!実行!修正!が高速で出来るようになりました。

次回は、Luaの開発環境について紹介したいと思います。

なぜLuaなのか?

なぜCocos2d-xをLuaでヤルのか?

答えはほとんどこちらの方が書かれています!

 

#20 まさか、Cocos2d-x 使っているのに C++ 書いてるわけないよね?

http://tech.kayac.com/archive/20_snmp.html

 

・型とか気にせずにガシガシ書ける。

・書いて、テストしてが即出来る。

・そこそこ速度が早い。

・速度で困ったらC++に頼れる。

・自分がLuaC++もそれとなく使えた。

自分の理由を並べるこんな感じです。

C++も良いのですが、Luaの手軽さを取りました。

 

スキルが無いので、とりあえず書いて!動かして!! という勢いでしかコーディングが出来ないのが最大の理由ですが・・・

 

それでも、ゲームはやってみないと実際のところ分からない点が多いので、Luaは悪く無いと思っています。

 

次は、Cocos2d-xでのLuaの準備について書きたいと思います。

このブログについて

 

アプリ作成で最近人気のあるCocos2d-x

そんなCocos2d-xを使って、ゲームを作っていく上で気がついた事を忘れないように書いていく大分個人的なブログの予定です。

 

Cocos2d-xといえばC++, JavaScript, Luaと好きな言語で書けるのも特徴です。

しかし、多くの人は C++を使って書いているようです。情報もC++は沢山!

そんな中、Luaでやっていく行く人へ向けた内容です。

 

なお、プロでも無ければ歴戦のプログラマでもないので、大分間違いを書くと思いますが、お手柔らかにお願いします。