連載
» 2013年04月23日 18時00分 UPDATE

C++でクロスプラットフォームを実現するCocos2dx入門(後編):iOS/Androidとの相互API呼び出しを行う際のコツ (1/3)

スマホ向けゲームアプリ開発フレームワークの概要や使い方を解説する特集です。今回は、C++とJava/Objectve-Cとのバインドコードを使って拡張する方法や注意点、メモリ解放機能などを解説します。

[森本数馬,グリー]

実際のゲーム開発では拡張する必要がある

 前編の記事「Cocos2dxでiOS/Androidの2Dゲーム開発を始めるには」では、「Cocos2dx」とは何かや、「なぜiOSでもAndroidでも動くの??」のところで、どのようにクロスプラットフォームを実現しているのか見てきました。また、Cocos2dxでAndroid/iOSアプリを開発する手順を具体的に解説しました。

 必要最低限の部分だけをプラットフォーム依存の言語で記述し、C++で記述されたCocos2dxのコア部分とプラットフォームとのインターフェイスを取っているのでしたね。

cocos2dx8.png Cocos2dxのスタック(再掲)

 さて、実際のゲーム開発では、デフォルトで提供されている機能以外にもプラットフォーム側には備わっているけれど、現状Cocos2dxから利用できないような機能を使用する必要があるかもしれません。

 筆者の場合は、それぞれのプラットフォーム上で動作し、静止画を投稿するようなライブラリをCocos2dxから使う必要に迫られました。ライブラリはそれぞれJavaとObjective-Cで実装されており、それぞれのプラットフォーム上で動作するものでした。

 後編では、それらのCocos2dxへの拡張実装をiOS、Android両者にマッチする形でどのように実装したのかを説明します。

Android/iOS両方にうまく対応するために

 単純にAndroid、iOSのAPIにつなぎ込むためには、それぞれバラバラに、JNIを記述しJavaのコードを呼び出す、Objective-Cのコードを呼び出すといった対応を行えばよいのですが、せっかくクロスプラットフォームをサポートしているエンジンを使っているのですから、その他のAPIと同様に、同じインターフェイスでAndroidでもiOSでも動くように拡張コードを実装していきたいですね。

 そのためには、アプリに公開するヘッダを共通にし、その実装をプラットフォームごとに分けて、C++とJava、C++とObjective-Cのバインドコードを準備するのが良い方法だと思います(この辺りの具体的実装もAudio再生機能を提供している「CocosDenshion」の構造が参考になるかと思います)。

 ソースコードのディレクトリ構成としても、例えばAndroid/iOS両対応の場合、以下のようになるのが良いでしょう。

cocos2dx2_1.jpg

 以降では、実際にどのようなバインドコードを実装すれば良いかを、いくつかのユースケースに分けて説明していきます。

単純なAPI呼び出し

 最初に、単純にAPI呼び出しを行う場合について説明します。ここでは、Javaのstaticメソッド、Objective-Cのクラスメソッドを、それぞれCocos2dxから呼び出す場合について説明します。

cocos2dx2_2.jpg

 Javaのstaticメソッドを呼び出す場合ですと、すでにCocos2dx内でデフォルトでJniHelperクラスが定義されており、このクラスを利用することにより容易に実現できます。

 Objective-Cの場合だと、直接クラスメソッドを呼び出せます。

 筆者の場合はキャプチャしたビットマップデータからJavaの場合はBitmapクラス、Objective-Cの場合はUIImageを生成して渡してやる必要がありました。その際、各プラットフォームの提供しているStaticなAPIを呼び出していますが、その部分が以下になります。

	//generate Bitmap object
	JniMethodInfo b;
	jobject config = NULL, bmpObj = NULL;
	if(JniHelper::getStaticMethodInfo(b, "android.graphics.Bitmap$Config", "nativeToConfig", "(I)Landroid/graphics/Bitmap$Config;")){
		config = b.env->CallStaticObjectMethod(b.classID, b.methodID, (6));
		b.env->DeleteLocalRef(b.classID);
		if(JniHelper::getStaticMethodInfo(b, "android.graphics.Bitmap", "createBitmap", "([IIILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;")){
			jintArray array = b.env->NewIntArray(width * height);
			b.env->SetIntArrayRegion(array, 0, width * height, pSrc);
			bmpObj = b.env->CallStaticObjectMethod(b.classID, b.methodID, array, width, height, config);
			b.env->DeleteLocalRef(array);
			b.env->DeleteLocalRef(b.classID);
		}
	}
Javaの場合

 JniHelperの提供するgetStaticMethodInfoに対し、クラス名、メソッド名、メソッドのSignatureを渡しています。これで、Javaのコードは一切記述せず、Cocos2dxからAndroidのStaticメソッドを利用する拡張機能の実装ができますね。

 ちなみに、Nativeで取得したBitmapデータをJavaで扱う場合はカラーデータを変換する必要がありますが、割愛しています。

	CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, pBuf, width * height * 4, NULL);
	CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
	CGBitmapInfo bmpInfo = kCGBitmapByteOrderDefault;
	CGColorRenderingIntent intent = kCGRenderingIntentDefault;
	CGImageRef imgRef = CGImageCreate(width, height, 8, 32, 4 * width,
										colorSpaceRef, bmpInfo, provider, NULL, NO, intent);
	UIImage *newImg = [UIImage imageWithCGImage:imgRef];
Objective-Cの場合

 Objective-Cの場合もStaticなメソッドコールを行うだけですね。

 これで、Cocos2dx側で保持しているビットマップデータから、それぞれプラットフォーム依存のImageデータ保持用のオブジェクト生成を行い、そのインスタンスをCocos2dx側で保持するところまでできました。

       1|2|3 次のページへ

Copyright© 2017 ITmedia, Inc. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。