[Cocos2d-x]Cocos2d-x Ver3.0 Deprecated でハマったらするべき事[iOS][Android]
[2017/05/18 追記] 本記事内の画像で使用している文字列が Deprecated と記載すべき箇所を Duplicated とミスタイプしています。 それぞれ Deprecated に置き換えてご覧ください。
はじめに
こんにちは!あらかわです。
Cocos2d-x3.0リリースから早数週間が経過し、Ver2系からCocos2d-x3.0(FINAL)へ移行されている方が多いと思います。
今回はそんな方を対象に、Deprecatedで悩まされないためのメモを書きます。
ご存知の方も多いですが、Cocos2d-x2系で使えたCCStringやCCArrayなどはCocos2d-x3.0では使用すると警告になります。
今回はわざと2系で使っていたクラスを使用して警告を出し、それを直す手順を紹介します。
プロジェクトの作成
プロジェクト作成の前提条件(筆者ブログ執筆時)
- Mac OS X 10.9.2
- Xcode5.1.1
- Cocos2d-x3.0 FINALインストール済み
ターミナルから以下のコマンドを入力して新規プロジェクトを作成します。
cocos new SampleDeprecatedClass -p jp.classmethod -l cpp -d ~/Desktop/
デスクトップにできたディレクトリをダブルクリックで開き、proj.ios_macディレクトリ内のプロジェクトファイルを開きます。
ClassesグループのHelloWorldScene.cppを開きます。
まず目にかかって来るのがauto型でしょう。
auto型
auto型は型推論といい、C++11から追加されたものの一つです。実行時に代入される値の型を見てその名の通り自動で型を決めてくれます。賢いです。
以下のような特徴があります。
- 右辺に初期値が代入されないと使えない。大体右辺にクラス::create()やクラス::getInstance()や定数が入ると思って良い。
- 右辺の初期値で型が判断されるので、Xcodeでは型チェックがビルド前から行われる。(autoでない型で宣言した時と同等の型チェック)
- TransitionCrossFadeなど長い型名もauto型で宣言できるので簡単にタイピングできる。
- ライブラリなどでクラス名の知らない戻り値の型が複数ある場合も、autoで宣言することで知らなくてもいい型のチェックに悩まされないで済む。(タイピングしづらいクラスプリフィックスとか来た時に効果的です。)
変数宣言時、右辺に初期値がある場合は積極的にauto型を使って行く事をオススメします。
std::string型
auto型を理解した上で、次はCCStringクラスを使ってみましょう。Cocos2d-x2系だとCCLabelの文言を得点によって変更したい場合などに活躍してたクラスです。
initメソッドの74行目(return true;)の一行上から追記していきます。
CCString *labelStr = CCString::createWithFormat("結果は%d点でした。", 10); log("%s", labelStr->getCString());
※Cocos2d-x3.0ではCCLogはlogに変わりました。
Deprecatedが出た時の対処法
- Deprecatedされたクラスの定義を見る。Xcodeの場合、クラス名を Command + クリックで定義へ飛べます。
- 公式ドキュメントを見る。右上のSearchにクラス名を入れる。
- 公式フォーラムを見る。
- 個人のブログやStack Overflowに頼る。
私の場合、1番から順番に試して解決しています。Cocos2d-x3.0のブログや記事はまだまだ多くないので困った時は公式から見ることにしています。
数週間〜数ヶ月後には4番をもっと頼りにしていきたいところです。
クラス名の上にマウスポインターを合わせて、Commandを押すと上図のようにクラス名が青字になりクリックすることで定義が見れます。
CC_DEPRECATED_ATTRIBUTE typedef __String CCString;
CCDeplicated.h(警告するクラスを定義したヘッダークラス)のCCString該当行へ移動しました。
大体はここのtypedef宣言されているクラスに書き換えたものがCocos2d-x3.0でそのまま使えるクラスです。
__Stringと書かれています。修飾子が使われているものはそのまま使うことはほとんどないクラスです。(言語にもよると思いますが、が付いているクラスはいじってはならないクラスの事が多いです。)
実はこの__Stringクラスも別クラスで定義されているので定義へ飛ぶことができます。
CCStringの時と同様にCommand + クリックで飛んでみて下さい。
class CC_DLL __String : public Ref, public Clonable
定義をざっと見るとbyte型の扱いをしています。Ref型とClonable型を継承した__Stringクラスです。
Ref型はCocos2d-x3.0から追加された、retain()やrelease()、autorelease()など参照カウントを管理するクラスです。
Cocos2d-x2系ではほとんどのクラスの親となっていたCCNode型もCocos2d-x3.0ではRef型を継承しています。メニューイベントのSenderなどがCCObject型からRef型になりましたのでかなり汎用的なオブジェクト型のようです。
Clonable型はその名の通りobjectのクローン(複製)を可能にするクラスです。
public修飾子の最下部に記述されている
public: std::string _string;
こちらの
std::string
が今までのCCStringに該当していたクラスです。
こんな感じで元のクラスを探すのも中々大変ですが、大体は一回定義先へ飛びtypedefで新しい型が宣言されているクラスが使えます。
定義先へ飛んだ場合、Xcode画面左側のProject Navigator(出てない時はCommand + 1で出ます。)で戻るのは大変面倒なので、Command + Control + 左キーで戻ることを推奨します。
反対に、Command + Control + 右キーで戻る前に表示していたクラスファイルを再度表示することができます。
HelloWorld.cppまで戻り、CCString型からstd::string型へ変更します。
このstd::というのはnamespaceで、ライブラリなどでクラス名が衝突した時にこの接頭語を見てどのプロジェクトのクラスなのかを識別できるものです。
using namespace句をファイルの頭に追加すればどのライブラリ(プロジェクト)のクラスを使用しているかを明示的に宣言でき、以降省略することができます。
std::stringはC++の標準ライブラリに含まれているstringクラスを使うことを明示的に宣言しています。がしかし、今回は他のライブラリ(プロジェクト)のstringクラスは使わないので省略しましょう。
USING_NS_CC; using namespace std;
cocosのマクロの下に追加しました。そして、CCStringの箇所を修正します。
string labelStr = string::createWithFormat("結果は%d点でした。", 10); log("%s", labelStr->getCString());
右辺のstring::でエラーが出ました。
実はcreateWithFormatはCocos2d-x3.0からはStringUtils::format()になりました。
auto型も踏まえて修正したのが以下になります。
auto labelStr = StringUtils::format("結果は%d点でした。", 10); log("%s", labelStr.c_str());
CCを外しただけじゃダメなクラス
大体のクラスはCCを外せばそのまま使えるのですが、C++の標準ライブラリを踏襲して、StringUtilsのように呼び出し元クラスが変わったメソッドを2つ挙げます。
- CCDictionary::createWithContentsOfFile(const char *pFileName) → FileUtils::getInstance()->getValueMapFromFile(const std::string &filename)
- CCArray::createWithContentsOfFile(const std::string &pFileName) → FileUtils::getInstance()->getValueVectorFromFile(const std::string &filename)
Cocos2d-x2系では、上記メソッドはjsonやplistファイルなどから設定値を読込むときなどに使われていました。(かなり使用頻度は高いと思います。)
- Dictionary型のデータが返って来る場合: ValueMap values = FileUtils::getInstance()->getValueMapFromFile("filename");
- Array型のデータが返って来る場合: ValueVector values = FileUtils::getInstance()->getValueVectorFromFile("filename");
でそれぞれ取得でき、
ValueVector values = FileUtils::getInstance()->getValueVectorFromFile("file.plist"); for (Value value : values) { log("%s", value.asString().c_str()); }
こんな感じで各要素を取り出せます。イテレータなども使えますので、お好きな取り方をして下さい。
まとめ
Cocos2d-xのObjective-C離れ
につきます。今までのCocos2d-x2系がObjective-Cラッパーの様なC++言語だったものからC++の標準ライブラリへシフトしたものだと思って下されば理解しやすいと思います。
オープンソースであるCocosのクラスを知るという観点で、Deprecated になる理由を理解することが解決の糸口になると思います。
同じ手順で解決できる問題が1つでも増えると幸いです。