日記

C87

気がついたらまた冬コミの季節となっておりました。
何度目の謝罪かわかりませんが、今回も新作は完成してません……。

しかし、今回は前作の「宵闇伝説」の在庫がショップから戻ってきたので
宵闇伝説あります! (最近までショップに在庫あるの忘れてた)

ということで、今回のお品書きはこんな感じです。

コミックマーケット87 2日目 東ハ^56a

既刊
宵闇伝説  500円
宵闇伝説~おりじなるさうんどとらっく~ 500円

すみません、CPルーミアは今回体験版ないです……。
次の例大祭くらいまでにはなんとかします……。

2014/12/27


例大祭11

告知全然してなかった時点で薄々感付かれてたと思いますが、CPルーミアは今回も体験版です……。


お品書き
ゲーム CPルーミア 体験版  100円
サントラ 宵闇伝説 ~おりじなるさうんどとらっく~ 500円


ちょっと開発期間が開いたりとかして、ソースがアレな状態だったりしたので作りなおしたりとかしてたら結局終わりませんでした。(言い訳)
お詫びといいますか、この体験版にはオンラインアップデート機能がついてありますので、完成した際には体験版からそのままアップデートできます。
(今までの体験版からも可能です)

お仕事の関係とかでいつ完成するかは確約できない状態になっちゃいましたが、なるべく早めに完成させたいです……。
一応、7/6のルーミアオンリー「東方想七日2」に参加予定なので、なるべくその辺りで出したいですね……。


東方関係ないですが、ちょっとしたパズル作ってました。
AndroidとWindowsとMac OSXで動きます。特設サイトはないですが、GooglePlayにスクショとか載せてるんで、そちらでどうぞ。

SweetWitches (Android) GooglePlayで見る
SweetWitches (Windows)をDL
SweetWitches (Mac OSX)をDL

2014/05/10


Androidで”JNI_GetCreatedJavaVMs”を使ってJavaVMを取得する方法

cocos2d-x v3.0rc1が出てから1週間以上経ってますね。大きなバグがなければ1週間で正式版が出ると言ってたので、もうそろそろですかね?


rc0からrc1になって結構大きな変化がありました。
たぶん一番の目玉はWindowsPhone8の対応ですよね。
でも日本じゃ全然普及してないし、そもそもキャリアから販売されてないので、国内をターゲットにする場合はあんまり関係ないですね。
(Lumia欲しい)

で、次に大きいのはAndroidでNativeActivityから普通のJavaActivityになったこと。(2.xまでのに戻った)
広告表示とかやりやすいようなので、いい変更だとは思うんですが、これのせいで個人的にちょっとハマったんでメモ。

cocos2d-xのデフォのSimpleAudioEngineは、簡単に使えて、最低限の機能は用意されています。
ただ、プラットフォーム毎に再生する方法が違うため、再生できるフォーマットがそれぞれ違うという残念仕様です。
(wavとかmp3ならわりと全部対応してたりするんですけど、個人的にはoggを使いたい)
なので、OpenAL使って自前でやってます。(この辺は長くなりそうなので、そのうち記事にします)

AndroidのOpenALのライブラリではJNI_OnLoad()でJavaVMを取得して使っています。
rc0まではこれで問題なかったのですが、rc1ではcocos2d-x側でJNI_OnLoad()を使っています。
JNI_OnLoadは重複して実装できないので、片方消さないといけないですね。
JNI_OnLoadの中で何をやっているかというと、JavaVMを受け取って、保持しています。
片方でJavaVMを受け取ってもう片方に渡せば済む話ですが、別のライブラリ同士なのでちょいと面倒です。
他にJavaVMを取得する方法はないかと思って調べてみたら、”JNI_GetCreatedJavaVMs”なるものがあるようです。
アプリ上で作られたJavaVMを取得する関数のようです。JavaVMは1つしか作られないはずなので、これでたぶん大丈夫だと思います。
しかし、これAndroidでは実装されていないみたいです。
ググっても、JNI_OnLoadを使えっていう内容ばかりです。たぶんそっちが正攻法なんですよね。
でも、どうしてもJNI_GetCreatedJavaVMsを使いたくて調べてみたら、一応できるようです。
この関数はlibdvm.soにあって、dlopenを使えば、GetCreatedJavaVMsを使えるようになります。

#include <jni.h>
#include <dlfcn.h>
static JavaVM* javaVM = NULL;

static void GetJavaVM()
{
	if (!javaVM) {
		jint (*func)(JavaVM**, jsize, jsize*);
		jsize ct;
		void *handle = dlopen("/system/lib/libdvm.so", RTLD_NOW | RTLD_GLOBAL);
		func = dlsym(handle, "JNI_GetCreatedJavaVMs");
		(*func)(&javaVM, 1 ,&ct);
		dlclose(handle);
	}
}

こんな感じで使えます。これをJavaVMを使う部分の前に呼べばOKです。
あんまりいい方法じゃないのかもしれませんが、自分の環境ではちゃんと動いたのでとりあえず良しとします。
AndroidでのJNI_GetCreatedJavaVMsの使い方は調べても、全然日本語での情報が見つからないので記事にしてみました。
余談ですが、ファイルパスの”/system/lib/libdvm.so”は、英語の掲示板で書いてあったのをコピペしたんですが、
それがトラップで”/system/lib/libvdm.so”と誤字ってるのに気づかず、ハマりました。
コピペするときはちゃんと中身確認しないと駄目ですね。


rc1での他の変更点として、ScrollViewの改良があります。
スクロールの限界以上に引っ張ると、バネのように弾性のある感じ(いい表現がわからないけど伝わるよね?)になりました。
それとWindows/Mac/Linuxで、ウィンドウ内をマウスを押したあとに、外で離すと、onTouchEndedのイベントが呼ばれないバグが治りました。
これらのせいで、TableViewはイマイチ使えない感じだったのが、いい感じに使えるようになりました。
ただ、マウスホイールに対応していなかったり、スクロールバーがなかったりとちょっと残念なところはあるので、
その辺は自前で用意してます。次当たりにその辺の記事書いてみようと思います。

2014/04/13


C++11での乱数

世間はエイプリルフールで賑わっていますが、当サイトは特に何もないのです……。
いつも、4月1日になってから何か用意しとけば良かったなぁと思うんですけど、1年経ったら忘れてるんですよねぇ。不思議。


cocos2d-x v3.0からはC++11を使うようになりました。
auto型とかラムダ式とか色々と便利なものが使えるようになりましたが、その中の1つに乱数があります。

C言語のrand()が色々と残念な感じなのは言わずもがな、共通認識だと思いますが、C++11ではそれに代わる乱数ライブラリが追加されました。
話題としてはちょっと古いし今更話題にするようなことでもないような気がしますが、最近C++を勉強し出した初心者の中には知らない人も多そうな気がしたので(私とか)

C++11で使える乱数 – メルセンヌ・ツイスター法 (`・ω・´)シャキーン | わくわくとおーぼえ
C++11 の乱数ライブラリ <random> – Siv3D 開発ブログ

↑を参考にさせて頂きました。他にもググるとたくさん情報出てきます。

使い方としては、まずをインクルードします。
std::random_deviceクラスのインスタンスを用意します。
このクラスは、外部デバイスとかの情報を元に乱数を生成するらしいです。
機械的な計算だけに基づくものではないので、予測不能な乱数を得られるとか何とか。
その分、処理が遅いので、最初の乱数のシードに使います。
処理速度をあまり気にしないアクション性のないものならこれだけでもいいかも。

std::mt19937はメルセンヌ・ツイスター法で乱数を生成します。
これにstd::random_deviceで得られた乱数をシードとして初期化します。
これで乱数を生成できるのですが、乱数を使う時って普通は特定の範囲内の数値が欲しいですよね。
std::uniform_int_distributionやstd::uniform_real_distribution等を使います。
これらは、乱数を元に指定した範囲内の値を返してくれます。
上記の2つは一様分布の整数と一様分布の浮動小数点数です。
他にも正規分布とかで偏った値を得られるものがあるようです。

rand()と比べて、ちょっと使い方がめんどいんで、私は↓みたいな感じで使ってます。

#include <random>
static std::mt19937 _mt;

void initRand(){ 
	std::random_device rndDev; 
	_mt = std::mt19937(rndDev()); 
}

int getRand(int min, int max){
	std::uniform_int_distribution<int> dist(min, max);
	return dist(_mt);
}

double getRand(double min, double max){ 
	std::uniform_real_distribution<double> dist(min, max);
	return dist(_mt); 
}

乱数のシードの部分で、std::random_deviceを複数使って、よりランダム性を高くすることができるそうです。
1個でも、rand()使うよりは遥かにマシになったので、とりあえずこれでOKってことで。

2014/04/01


cocos2d-x v3.0 時間を取得する

ちょっと前まではv3.0についてググってもあんまり日本語の情報がなかったんですが、最近ちょっと増えてきましたね。


だいぶ前からの変更点ですが、v3.0ではv2.xにあったCCTimeクラスに代わるクラスはありません。
“cocos2d::CCTime::gettimeofdayCocos2d()”は”cocos2d::gettimeofday()”になっています。
使い方は前と一緒です。

double getSec(){
	timeval tv;
	gettimeofday(&tv, nullptr);
	return (tv.tv_sec) + (tv.tv_usec) * 1e-6;
}

これで起動してからの時間(秒)が取得できます。
ゲームスタートからの経過時間を調べるには、スタート時の時間を保持しておいて、
現在の時間 – スタート時の時間
で計算できますね。

他の方法として、”void update(float delta)”を使う方法もあるので、一応紹介しておきます。
この関数はSpriteやLayerなどの継承元にあるNodeクラスに定義されているので、これをオーバーライドして使います。

bool HelloWorldScene::init(){
	if ( !Layer::init() ) return false;
	scheduleUpdate();
	return true;
}

この”scheduleUpdate();”で”update()”関数が毎フレーム呼ばれるようになります。
そして、その時引数の(float delta)には前のフレームで呼ばれてから今のフレームまでの時間が渡されます。
これを利用して、この値をどんどん加算していけば経過時間がわかります。
gettimeofday()を使う方法と比べて簡単でお手軽なのですが、ちょっとだけ問題があります。

試しに↓のようなコードで計測してみました。

bool HelloWorldScene::init(){
	if ( !Layer::init() ) return false;
	scheduleUpdate();
	_startTime = Helper::getSec();
	_updateTime = 0;
	return true;
}

void HelloWorldScene::update(float delta){
	double currentTime = Helper::getSec() - _startTime;
	_updateTime += delta;
	log("gettimeofday() : %f \nupdate() : %f", currentTime,_updateTime);
}

getSec()は私の環境ではHelper名前空間に置いてますので”Helper::getSec();”になってます。
“_startTime”と” _updateTime”はdouble型のメンバ変数です。
これで約1分ほど計測してみたところ↓の結果になりました。

gettimeofday() : 59.999193
update() : 60.000305

update()の方がちょっぴり多いですね。この差は時間が経つにつれどんどん大きくなっていきます。
原因はfloat型の丸めによる誤差とかだと思います。
1分程度なら正直気にするほどの大きさでもないような気がしますが、厳密にやるのであればgettimeofday()の方が確実ですね。

……と言いたいところですが、スマホの場合ホーム画面に戻ってから復帰した時の問題があります。
gettimeofday()で時間を取得していた場合はホーム画面にいる間も時間が進んじゃうんですね。
これを回避するには、
AppDelegate::applicationDidEnterBackground()
AppDelegate::applicationWillEnterForeground()
を使えばいいです。AppDelegate.cppの下の方にあります。
これらは、ホーム画面に移った時と復帰した時にそれぞれ呼ばれます。ホームに移った時間を記憶しておけば、戻った時に何秒間待機していたかがわかるので、その値をスタート時の時間に足してあげれば大丈夫です。

2014/03/31


≪前  |  次≫