cocos2d-xプロジェクト作成+自分用設定など

Cocos2d-xで新しいプロジェクトを作成するときの、自分用メモです。
Androidは、Android Studio用のプロジェクトを使うようにします。

今回使用したバージョン

  • Cocos2d-x 3.9
  • AndroidNDK r10c

プロジェクト作成

 cocos new プロジェクト名 -p パッケージ -l cpp -d プロジェクトディレクトリ --portrait
# cocos new TestCocos2dx3 -p jp.milt.cocos2dx3test -l cpp -d /Users/okahiro/Documents/  --portrait

--portraitオプションは画面を縦にしたい時につけます。

sublime textプロジェクトファイル追加
プロジェクトディレクトリ直下にSublimeText2.sublime-projectファイルを追加

{
	"folders":
	[
		{
			"path": ".",
			"file_exclude_patterns" : ["*.png","*.jpg","*/DelivedData/*"]
		}
	]
}

.gitignoreを追加
プロジェクトディレクトリ直下に.gitignoreファイルを追加

.DS_Store

*.class

*.moved-aside
*.xcworkspace
*.mode1v3
*.mode2v3
*.pbxuser
!default.xcworkspace
!default.mode1v3
!default.mode2v3
!default.pbxuser
*.perspectivev3
profile
xcuserdata
.idea/*
Backup
bin
gen
obj/
cocos2dcpp_shared
libcocos2dcpp.so
proj.android/assets
proj.android-studio/app/assets
TESTFLIGHT_OUTPUT
*.sublime-workspace
com_crashlytics_export_strings.xml
proj.android/libs/armeabi/gdb.setup
proj.android/libs/armeabi/gdbserver
proj.android-studio/app/libs/armeabi/gdb.setup
proj.android-studio/app/libs/armeabi/gdbserver

Android.mkファイル更新
cppファイルの追加をいちいちAndroid.mkに書きたくないので、Android.mkファイルを更新します。
場所:
プロジェクトディレクトリ/proj.android-studio/app/jni/Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

$(call import-add-path,$(LOCAL_PATH)/../../../cocos2d)
$(call import-add-path,$(LOCAL_PATH)/../../../cocos2d/external)
$(call import-add-path,$(LOCAL_PATH)/../../../cocos2d/cocos)

LOCAL_MODULE := cocos2dcpp_shared

LOCAL_MODULE_FILENAME := libcocos2dcpp

FILE_LIST := $(wildcard $(LOCAL_PATH)/../../../Classes/*.cpp)

LOCAL_SRC_FILES := hellocpp/main.cpp
LOCAL_SRC_FILES += $(FILE_LIST:$(LOCAL_PATH)/%=%)

LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../Classes

# _COCOS_HEADER_ANDROID_BEGIN
# _COCOS_HEADER_ANDROID_END


LOCAL_STATIC_LIBRARIES := cocos2dx_static

# _COCOS_LIB_ANDROID_BEGIN
# _COCOS_LIB_ANDROID_END

include $(BUILD_SHARED_LIBRARY)

$(call import-module,.)

# _COCOS_LIB_IMPORT_ANDROID_BEGIN
# _COCOS_LIB_IMPORT_ANDROID_END

Androidコンパイル確認

# cocos compile -p android --android-studio

Android向け設定
AndroidManifest.xmlに下記を追加します。

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Android apk署名設定

# cd proj.android-studio/app
# keytool -genkey -v -keystore 鍵ファイル名 -alias エイリアス名 -keyalg RSA -validity 10000
# cocos compile -p android --android-studio -m release
 初回のみ最後にキーファイル、エイリアス名、キーパスワードを聞かれるので入力

Android Studioでプロジェクトを開く
このサイトを参考にさせていただきました。
http://studio.cretia.net/blog/462

もしくは「File」ー「New」ー「Import Project」から"proj.android-studio"を選択してもOK。

サーバー上においてある画像をダウンロードして使う(TextureAtlas)

サーバー上においてある画像をダウンロードして、それをそのままSpriteに使う例はそれなりにみかけますが、
TextureAtlas(pngとplist)をダウンロードしてSpriteFrameCacheを使う例は見かけなかったので、自分なりに試してみました。

今回使用したバージョン

  • Cocos2d-x 3.2

TextureAtlasを使う場合はpngとplistセットでダウンロードする必要があるので、あらかじめzipファイルに圧縮してサーバーに置いておくようにします。
この方法であれば、一緒に必要なファイルをまとめてダウンロードできるので便利です。

zipファイルの扱いについては、こちらを参考にさせていただきました。

http://qiita.com/kuuki_yomenaio/items/b93b236ed5563adf9c39

// zipファイルから取り出すファイル名一覧
std::string dataNameList[] = {"textureatlas.png","textureatlas.plist"};

...

void HttpAccessManager::downloadFile()
{
	std::string fileUrl = "http://www.example.com/data.zip";
	
	auto request = new HttpRequest();
	request->setUrl(fileUrl.c_str());
	request->setRequestType(HttpRequest::Type::GET);
	request->setResponseCallback([&](HttpClient *sender,HttpResponse * response)
	{
		if(response->isSucceed())
		{
			std::string filePath = FileUtils::getInstance()->getWritablePath() + "data.zip";
			
			std::vector<char>* buffer = response->getResponseData();
			// zipファイルを保存
			FILE* fp = fopen(filePath.c_str() , "wb");
			fwrite(buffer->data() , 1, buffer->size() , fp);
			fclose(fp);
			
			// zipファイルからそれぞれのファイルを取り出し
			for(std::string dataFileName : dataNameList)
			{
				unsigned long size = 0;
				unsigned char* buf = FileUtils::getInstance()->getFileDataFromZip(filePath.c_str(),
																				  dataFileName,
																				  (ssize_t*)&size);
				std::string writeFilePath = FileUtils::getInstance()->getWritablePath() + dataFileName;
				
				FILE* dataFile = fopen(writeFilePath.c_str(),"wb");
				fwrite(buf,1,size,dataFile);
				fclose(dataFile);
			}
			
			// SpriteFrameCache
			SpriteFrameCache::getInstance()->addSpriteFramesWithFile(FileUtils::getInstance()->getWritablePath() +
																	 "textureatlas.plist");
		}
		else
		{
			// 通信失敗した時の処理
			CCLOG("LoadImage failure.");
		}
	});

	HttpClient::getInstance()->send(request);
	request->release();
}

zipファイルをダウンロードしたら保存して、そこから順番にファイルを取り出して保存していきます。

自分用 MacOSXインストールソフトメモ

MacOSXに入れておくといいなと思っているソフトです。
自分用メモです。

ブラウザ

オフィス

SNS

ユーティリティ

サービス

セキュリティ

プログラム開発など

ゲーム開発

  • Texture Packer
  • Physics Editor
  • Particle Designer
  • Glyph Designer

グラフィックス

Cocos2d-xでアプリのバージョンを取得する

Cocos2d-xからiOS,Androidのアプリバージョンを取得する処理を作成したのでメモします。
JNIのクラスなど、ネイティブ連携のクラスはCocos2d-x上からTwitterにツイートする - おかひろの雑記などと同じ形で作成してあるとします。

今回使用したバージョン

  • Cocos2d-x 3.2

NativeCodeLauncher.h

	// バージョン番号取得
	static std::string getVersionNo();

iOS向け
NativeCodeLauncher.mm

// バージョン番号取得
static std::string static_getVersionNo()
{
	NSString *versionNo = [NativeCodeLauncher getVersionNo];
	std::string ret([versionNo UTF8String]);
	return ret;
}

...

namespace Cocos2dExt
{
	// バージョン番号取得
	std::string NativeCodeLauncher::getVersionNo()
	{
		return static_getVersionNo();
	}

NativeCodeLauncher_objc.h

// バージョン番号取得
+(NSString *)getVersionNo;

NativeCodeLauncher_objc.mm

// バージョン番号取得
+(NSString *)getVersionNo
{
	return [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
}

Android向け
NativeCodeLauncher.cpp

	// バージョン番号取得
	std::string NativeCodeLauncher::getVersionNo()
	{
		return getVersionNoJNI();
	}

NativeCodeLauncherJni.h

	// バージョン番号取得
	extern std::string getVersionNoJNI();

NativeCodeLauncherJni.cpp

// バージョン番号取得
std::string getVersionNoJNI()
{
	JniMethodInfoEx methodInfo;
        
        if (!getStaticMethodInfo(methodInfo, "getVersionNo", "()Ljava/lang/String;"))
        {
            return "";
        }
		
	jstring retString = (jstring)methodInfo.env->CallStaticObjectMethod(methodInfo.classID, methodInfo.methodID);
		
        // jstringをstd::stringに変換
        std::string ret = JniHelper::jstring2string(retString);
		
        // 解放処理
        methodInfo.env->DeleteLocalRef(retString);
        methodInfo.env->DeleteLocalRef(methodInfo.classID);
		
        return ret;
}

AppActivity.java

	// バージョン番号取得
	public static String getVersionNo()
	{
		PackageManager pm = me.getContext().getPackageManager();
		String versionName = "";
		try
		{
			PackageInfo packageInfo = pm.getPackageInfo(me.getContext().getPackageName(), 0);
			versionName = packageInfo.versionName;
		}
		catch(NameNotFoundException e)
		{
			e.printStackTrace();
		}
		
		return versionName;
	}

戻り値の型はstd::stringがいいと思います。const char*にすると、Androidでうまくいきませんでした。
(私のやり方が良くなかったのかもしれません。)

JNIでJavaからC++のメソッドを実行

ネイティブ連携はC++Objective-C or Javaの一方向で事足りることが多いですが、
ネイティブ側からC++のメソッドを呼びたいケースもやはりあります。
(課金処理の結果をC++に渡すなど)

JNIは少しややこしいので、メモをしておきます。
JNIのクラスなど、ネイティブ連携のクラスはCocos2d-x上からTwitterにツイートする - おかひろの雑記などと同じ形で作成してあるとします。

今回使用したバージョン

  • Cocos2d-x 3.2


AppActivity.java

public class AppActivity extends Cocos2dxActivity
{
	// C++側のメソッドを呼ぶ宣言
	private static native void sampleNativeMethod(boolean result,String test);
	...


	// 実際の呼び出し
	sampleNativeMethod(true,"Test");

NativeCodeLauncherJni.cpp

	void Java_org_cocos2dx_cpp_AppActivity_sampleNativeMethod(JNIEnv *env,jobject thiz,jboolean success,jstring test)
	{
		const char *testChar = env->GetStringUTFChars(test,0);
		
		// ここで処理を行う
		
		// 解放
		env->ReleaseStringUTFChars(test, testChar);
	}

JavaからStringを引数にして呼び出す場合は、変換が必要のようです。

Actionに他のターゲットのアクションを混ぜて実行する

Cocos2d-xで、例えば
スプライト1を指定の座標に移動

スプライト2を指定の座標に移動

スプライト1をフェードアウト
のように、他のターゲットのActionを混ぜて実行する方法のメモ。

今回使用したバージョン

  • Cocos2d-x 3.2

そんなに難しくなく、runActionを実行する以外のノードではTargetedActionを使うだけでいけそうです。
たとえば上の例であれば

	sprite1->runAction(Sequence::create(MoveTo::create(0.5f,Vec2(100,100)),
										TargetedAction::create(sprite2,MoveTo::create(0.5f,Vec2(200,200))),
										FadeIn::create(0.5f),
										NULL));

CallFuncNなどを使った処理もできます。

	sprite1->runAction(Sequence::create(TargetedAction::create(sprite2, FadeTo::create(0.5f,100)),
									 CallFunc::create([&]()
										{
											// なにか処理を実行
										}),
									 DelayTime::create(1.5f),
									 TargetedAction::create(sprite2, FadeOut::create(0.5f)),
									 TargetedAction::create(sprite2,CallFuncN::create([](Node *n)
										{
											n->removeFromParentAndCleanup(true);
										})),
									 NULL));

Cocos2d-x 3.2でスクリーンショットを撮ってツイート

Cocos2d-x3.2で、スクリーンショットを撮るためのメソッドが実装されたので、
これを使ってスクリーンショットをツイートする機能を作ってみたのでメモ。

基本はこの記事と同じです。
Cocos2d-xで撮ったスクリーンショットをTwitterに添付してツイートできないか試してみた - おかひろの雑記

※2014/8/8 iOS6でツイート後に画面操作ができなくなる問題があったため、NativeCodeLauncher_objc.mmを修正しました。

今回使用したバージョン

  • Cocos2d-x 3.2


Cocos2d-xでスクリーンショットを撮る方法はこちらhttp://www.cocos2d-x.org/projects/cocos2d-x/wiki/How_to_save_a_screenshotを参考にしました。


ネイティブ連携
ツイートする内容に加え、画像ファイルのフルパスを一緒に渡すようにしています。

共通
NativeCodeLauncher.h

namespace Cocos2dExt {
    class NativeCodeLauncher
    {
    public:
		static void openTweetDialog(char const *tweet,char const *filePath);
    };
}

iOS
NativeCodeLauncher.mm

static void static_openTweetDialog(const char* tweet,const char* filePath)
{
        [NativeCodeLauncher openTweetDialog:[NSString stringWithUTF8String:tweet] filePath:[NSString stringWithUTF8String:filePath]];
}

namespace Cocos2dExt
{
        void NativeCodeLauncher::openTweetDialog(const char *tweet,const char *filePath)
        {
                static_openTweetDialog(tweet,filePath);
        }
}

NativeCodeLauncher_objc.h

@interface NativeCodeLauncher : NSObject

+(void)openTweetDialog:(NSString *)tweet filePath : (NSString *)filePath;

@end

NativeCodeLauncher_objc.mm
※2014/8/8 CompletionHandler内にdismissViewControllerAnimatedメソッドを追加。それに伴う修正。

@implementation NativeCodeLauncher

+(void)openTweetDialog:(NSString *)tweet filePath:(NSString *)filePath
{
	if([SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter])
	{
		AppController *appController = (AppController *)[UIApplication sharedApplication].delegate;

		NSString *serviceType = SLServiceTypeTwitter;
		SLComposeViewController *composeCtl = [SLComposeViewController composeViewControllerForServiceType:serviceType];
		[composeCtl setInitialText:tweet];
		UIImage *image = [UIImage imageWithContentsOfFile:filePath];
		[composeCtl addImage:image];
		[composeCtl setCompletionHandler:^(SLComposeViewControllerResult result) {
			[appController.viewController dismissViewControllerAnimated:YES completion:nil];
			if (result == SLComposeViewControllerResultDone) {
				//投稿成功時の処理
				NSLog(@"ツイートしました");
			}
        	}];

        	[appController.viewController presentViewController:composeCtl animated:YES completion:nil];
	}
	else
	{
		tweet = [NSString stringWithFormat:@"http://twitter.com/home?status=%@",tweet];
		tweet = [tweet stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
		NSURL *url = [NSURL URLWithString:tweet];
		[[UIApplication sharedApplication] openURL:url];
	}
}

@end

iOSの場合は画像ファイルをUIImageに読み込むだけなので簡単です。
Social.frameworkを使う形に変更しました。


Android
NativeCodeLauncher.cpp

namespace Cocos2dExt
{
        void NativeCodeLauncher::openTweetDialog(char const* tweet,char const *filePath)
        {
                openTweetDialogJNI(tweet,filePath);
        }
}

NativeCodeLauncherJni.h

extern "C"
{
	extern void openTweetDialogJNI(char const* tweet,char const *filePath);
}

NativeCodeLauncherJni.cpp

...

#define CLASS_NAME "org/cocos2dx/cpp/AppActivity"

...

extern "C"
{

...

        void openTweetDialogJNI(char const* tweet,char const *filePath)
        {
                JniMethodInfo methodInfo;
        
                if (!getStaticMethodInfo(methodInfo, "openTweetDialog", "(Ljava/lang/String;Ljava/lang/String;)V"))
                {
                    return;
                }
        
                jstring tweetArg = methodInfo.env->NewStringUTF(tweet);
                jstring filePathArg = methodInfo.env->NewStringUTF(filePath);
                methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID, tweetArg,filePathArg);
                methodInfo.env->DeleteLocalRef(tweetArg);
                methodInfo.env->DeleteLocalRef(filePathArg);
                methodInfo.env->DeleteLocalRef(methodInfo.classID);
        }
}

AppActivity.java

public class AppActivity extends Cocos2dxActivity{

	private static Activity me = null;
	
	protected void onCreate(Bundle savedInstanceState){
		super.onCreate(savedInstanceState);
		
		me = this;
	}
	
    static {
         System.loadLibrary("game");
    }
    
    // JNIから呼び出すメソッド
    public static void openTweetDialog(String tweet,String filePath)
    {
        final String path = filePath;
        final String tweetMessage = tweet; 
        
        me.runOnUiThread(new Runnable(){
                @Override
                public void run()
                {
                        File f = new File(path);
                        
                        byte[] data;
                                try
                                {
                                        data = readFileToByte(path);
                                }
                                catch(Exception e)
                                {
                                        Log.e("Debug", e.getMessage());
                                        return;
                                }
                                
                                File savePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
                                File saveFile = new File(savePath,"screenshot.jpeg");
                                
                                if(!savePath.exists())
                                {
                                        savePath.mkdir();
                                }
                                
                                FileOutputStream fos = null;
                                
                                try
                                {
                                        fos = new FileOutputStream(saveFile);
                                        fos.write(data);
                                        fos.close();
                                }
                                catch(Exception e)
                                {
                                        Log.e("Debug", e.getMessage());
                                        return ;
                                }
                        
                                Uri uri = Uri.fromFile(saveFile);
                
                                Intent it = new Intent(Intent.ACTION_SEND);
                                it.putExtra(Intent.EXTRA_SUBJECT, "");
                                it.putExtra(Intent.EXTRA_TEXT, tweetMessage);
                                it.putExtra(Intent.EXTRA_STREAM,uri);
                                it.setType("image/jpeg");
               
                                me.startActivity(Intent.createChooser(it,"共有"));
                }
        });
    }

    private static byte[] readFileToByte(String filePath) throws Exception
    {
                byte[] b = new byte[1];
                FileInputStream fis = new FileInputStream(filePath);
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                while (fis.read(b) > 0) {
                        baos.write(b);
                }
                baos.close();
                fis.close();
                b = baos.toByteArray();
        
                return b;
    }
}

画像ファイルを外部ストレージに一旦コピーしています。
そのため、AndroidManifest.xmlパーミッションが必要になります。

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>


呼び出し

	utils::captureScreen([&](bool succeed,const std::string &fileName){
		// ツイート
		if(succeed)
		{
			// スクリーンショット保存成功
			Cocos2dExt::NativeCodeLauncher::openTweetDialog(”ツイートメッセージ”,fileName.c_str());
		}
		else
		{
			// スクリーンショット保存失敗
			
		}
	}, "screenshot.jpg");

スクリーンショットを撮った後にコールバックされるようなので、その中でツイートの準備を行います。