2014年03月30日

[Cocos2d-x] CCMenuItemをタッチしてもスクロールできるようにしたい(やり直し)

以前、このような記事を書きました。

tappli blog: [Cocos2d-x] CCMenuItemをタッチしてもスクロールできるようにしたい

これを書いた時点では気づいていなかったのですが、だいぶひどいバグがありました。
2つのボタンをタップすると、動作がおかしくなってしまうのです。
マルチタップを考慮してなかったせいでした。

その点に関して修正した(と思われる)ものをGitHubにアップロードしました。

t2low/CocosScrollViewSample

もうちょっとちゃんとしたサンプルが作れたらよかったのですが、とりあえずはスクロールビューとボタンを配置しただけのサンプルです。
iOSとAndroid用のプロジェクトを残してありますが、iOSの方しか動作確認してませんです。
自分は現在Cocos2d-x 2.2.3の環境でやっています。
おかしなところがあったら、ご報告いただけるとありがたいです。
タグ:cocos2d-x
posted by t2low at 23:33| Cocos2d-x

2014年03月18日

[Cocos2d-x] ミリ秒で時間を取得したい

JavaのSystem.currentTimeMillis()のようにミリ秒(というか、秒以下の精度)で時刻を取得したくなりました。
time()関数の単位は秒なので使えません。
clock()関数は精度はtime()関数より高いようですが、時刻を取得するものではないようです。
いろいろ検索してみると、gettimeofday()関数を使えば良いらしいということがわかりました。
#include <stdio.h>
#include <sys/time.h>

int main(int argc, char *argv[]) {
struct timeval t;
gettimeofday(&t, NULL);
unsigned long long now = t.tv_sec * 1000ull + t.tv_usec / 1000ull;
printf("Time: %llu\n", now);
return 0;
}

tv_secは秒なので1000倍、tv_usecはマイクロ秒なので1000で割ったものを足しています。

Javaで同じように出力するプログラムを書いて比較してみました。
class Main {
public static void main(String[] args) {
System.out.println("Time: " + System.currentTimeMillis());
}
}

全く同時には実行できないので返す結果は少し異なりましたが、数値を見る限り大丈夫そうな感じでした。
これで時刻をミリ秒で扱えます。良かった、良かった。
posted by t2low at 22:00| Cocos2d-x

2014年03月05日

[Cocos2d-x] 外部のフォントファイル(.otf)を表示する

今日、外部のフォントファイル(.otf)を使って、文字を表示する部分を作成していました。
フォントファイルはResources/fontsに入れ、CCLabelTTFクラスを使って表示するだけです。
それだけなんですが、えらい苦労しました。

iOS



  • 参考にさせてもらったいくつかのサイトに「ターゲット」の「Fonts provided by application」にフォント名を追記する、というようなことが書かれていたが、どこのことだかわからない

  • いくつかのサイトに載っていた画面とは異なるが、プロジェクトの「ios/info.plist」に「Fonts provided by application」があることを教えてもらった


Android



  • AndroidはCClabelTTFにフォントファイル名を設定したが表示されない。

  • 他の.ttfファイルなら表示できるので、ファイルが.otfだからっぽいということはわかった。

  • この.otfファイルをTextViewで利用した時は表示できるのでCocos2d-xが対応してないっぽい?

  • 最終的に「拡張子を.ttfにすればいけるらしい」ということを聞いて、試したら本当に表示された。信じられない。


こんな感じ。
表示できないotfのフォントを使おうとしている方は、ファイル名を〜.ttfにすると良いですよ。
posted by t2low at 02:00| Cocos2d-x

2014年02月26日

[Cocos2d-x] CCMenuItemをタッチしてもスクロールできるようにしたい

以下のソースには不具合があります。
修正したソースは以下の記事から参照できます。
tappli blog: [Cocos2d-x] CCMenuItemをタッチしてもスクロールできるようにしたい(やり直し)


CCScrollViewとCCMenuを組み合わせて利用した時に、最初のタッチ位置がCCMenuItemだとスクロールさせることが出来ません。
CCMenuItemがタッチイベントを奪ってしまって、CCScrollViewまで届いていないようです。
解決策がないかと検索してみたところ、以下の記事が見つかりました。
というよりもこれくらいしか見つけられませんでした。
あまりCCScrollViewとCCMenuを組み合わせて使うような使い方はされていないのかも知れません。

Make Ccscrollview Work With Ccmenuitemimage - iOS Coder Talk

全てのコードは載っていないのですが、やっていることは単純でした。

  • CCMenuItemより先にCCScrollViewがタッチイベントを取得するようにする

  • ccTouchEnded時にタッチの移動距離を計算し、一定以下ならCCMenuにタッチイベントを渡す

  • CCMenu側では指定された範囲外だったら無視する


という感じ。

足りないコードを補完して、この実装をしてみると一つ問題がありました。
スクロールはできるし、ボタン押下のイベントも来るのですが、ボタンの見た目が変化しないのです。
それはそのはずで、CCScrollViewExのccTouchEndedでCCMenuのccTouchBeganとccTouchEndedを両方処理しているのです。
これではダメだと書き換えたのが以下のコードです。

.h


class CCMenuEx : public cocos2d::CCMenu {
public:
CREATE_FUNC(CCMenuEx);
virtual bool ccTouchBegan(cocos2d::CCTouch *touch, cocos2d::CCEvent *event);
void setValidTouchRectInWorldSpace(cocos2d::CCRect rect) { this->validTouchRectInWorldSpace = rect; };
private:
cocos2d::CCRect validTouchRectInWorldSpace;
};

class CCScrollViewEx : public cocos2d::extension::CCScrollView {
public:
virtual void registerWithTouchDispatcher();
virtual bool ccTouchBegan(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent);
virtual void ccTouchMoved(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent);
virtual void ccTouchEnded(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent);
virtual void ccTouchCancelled(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent);
CREATE_FUNC(CCScrollViewEx);
void setMenu(cocos2d::CCMenu *menu);

enum {
kCCScrollViewExPriority = (cocos2d::kCCMenuHandlerPriority - 1)
};
protected:
cocos2d::CCPoint pressPoint;
cocos2d::CCMenu *menu;
bool waitingTouchEnd;
};

.cpp


bool CCMenuEx::ccTouchBegan(CCTouch *touch, CCEvent *event) {
if (!validTouchRectInWorldSpace.size.width || !validTouchRectInWorldSpace.size.height) {
return CCMenu::ccTouchBegan(touch, event);
}

CCPoint touchLocation = touch->getLocation();
if (!validTouchRectInWorldSpace.containsPoint(touchLocation)) {
return false;
}

return CCMenu::ccTouchBegan(touch, event);
}

void CCScrollViewEx::registerWithTouchDispatcher() {
CCTouchDispatcher *dispatcher = CCDirector::sharedDirector()->getTouchDispatcher();
dispatcher->addTargetedDelegate(this, kCCScrollViewExPriority, true);
}

bool CCScrollViewEx::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent) {
pressPoint = pTouch->getLocationInView();
if (menu) {
waitingTouchEnd = menu->ccTouchBegan(pTouch, pEvent);
}
return CCScrollView::ccTouchBegan(pTouch, pEvent);
}

void CCScrollViewEx::ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent) {
if (waitingTouchEnd) {
const float MIN_DISTANCE = 10;
CCPoint endPoint = pTouch->getLocationInView();
float distance = sqrtf((endPoint.x - pressPoint.x) * (endPoint.x - pressPoint.x) + (endPoint.y - pressPoint.y) * (endPoint.y - pressPoint.y));

if(distance < MIN_DISTANCE) {
if (menu) {
menu->ccTouchMoved(pTouch, pEvent);
}
} else {
if (menu) {
menu->ccTouchCancelled(pTouch, pEvent);
waitingTouchEnd = false;
}
}
}
CCScrollView::ccTouchMoved(pTouch, pEvent);
}

void CCScrollViewEx::ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent)
{
if (waitingTouchEnd) {
const float MIN_DISTANCE = 10;
CCPoint endPoint = pTouch->getLocationInView();
float distance = sqrtf((endPoint.x - pressPoint.x) * (endPoint.x - pressPoint.x) + (endPoint.y - pressPoint.y) * (endPoint.y - pressPoint.y));

if(distance < MIN_DISTANCE) {
if (menu) {
menu->ccTouchEnded(pTouch, pEvent);
}
} else {
if (menu) {
menu->ccTouchCancelled(pTouch, pEvent);
waitingTouchEnd = false;
}
}
}
CCScrollView::ccTouchEnded(pTouch, pEvent);
}

void CCScrollViewEx::ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent) {
if (menu && waitingTouchEnd) {
menu->ccTouchCancelled(pTouch, pEvent);
waitingTouchEnd = false;
}
CCScrollView::ccTouchCancelled(pTouch, pEvent);
}

void CCScrollViewEx::setMenu(CCMenu *menu) {
this->menu = menu;
CCMenuEx *ex = dynamic_cast<CCMenuEx *>(menu);
if (ex) {
ex->setValidTouchRectInWorldSpace(getViewRect());
}
}


ccTouchMovedとccTouchEndedにコピペコードがあるのはご愛嬌。
変更したのは、以下のとおり。

  • CCMenuにはccTouchBeganでイベントを流す

  • タッチ位置が一定以上移動したらCCMenuのタッチイベントはキャンセル(ccTouchCancelled)する

  • setMenu()にCCMenuExが渡されたら、CCScrollViewExの表示サイズをタッチ可能領域とする


期待通りの動作はしているので、これで良しとします。

確認はしていないですが、Cocos2d-x 3.0ではタッチイベント周りがかなり改善されているようなので、こんなことしなくても良いかも知れません。
3.0はまだβ版のようなので、まだ必要としている人がいるかも、と記事にしてみました。


posted by t2low at 22:00| Cocos2d-x

2014年02月19日

[Cocos2d-x] std::vectorの中身の保存

CCHttpClientを使って取得したデータをファイルに保存するコードを書いていました。

std::vector<char> *data = response->getResponseData();

std::string path = CCFileUtils::sharedFileUtils()->getWritablePath() + "hoge.txt";
FILE *fp = fopen(path.c_str(), "w");
size_t count = fwrite(&data[0], sizeof(char), data->size(), fp);
fclose(fp);

こんなコードを書いたのですが、ファイルの内容は正しくありませんでした。
しかし、fwrite()の戻り値はdata->size()と一致するのです。
しばらく悩んで気付きました。
size_t count = fwrite(&(*data)[0], sizeof(char), data->size(), fp);

こうですね。
そりゃあ変なデータが書き込まれるわけですね。

posted by t2low at 22:00| Cocos2d-x

2014年02月18日

[Cocos2d-x] OS毎に処理を分ける

「Cocos2d-x 開発のレシピ」にOS毎に処理を分ける方法が載っていました。

マクロで分岐させる方法と
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
...
#elif (CC_TARGET_PLATFORM ==CC_PLATFORM_ANDROID)
...
#endif

実行時に分岐させる方法
TargetPlatform platform = CCApplication::sharedApplication()->getTargetPlatform();
if (platform == kTargetIphone || platform == kTargetIpad) {
...
} else if (platform == kTargetAndroid) {
...
}

使い分けとしては、関数でできるなら関数、ヘッダで使うとき等はマクロで、とのことだけど、実行時に分岐させたいことってあるのかな?
すぐには思いつかないな…。
可読性が落ちるというのはわかる。




posted by t2low at 22:00| Cocos2d-x

2014年02月17日

[Cocos2d-x] C++で忘れがちなこと

Javaをやってた時間が長いので、よく忘れてしまうのです。
staticなメンバ変数の使い方。

Hoge.h
class Hoge {
public:
static Hoge *getInstance();
private:
Hoge() {};
static Hoge *instance;
};

Hoge.cpp
Hoge *Hoge::getInstance() {
if (!instance) {
instance = new Hoge();
}
return instance;
}

こんなコードを書くとエラーになりますよね。
Hoge.cppに
Hoge *Hoge::instance;

の1行が必要です。
エラーが出てstaticなメンバ変数が原因だということがわかっても、毎度のようにしばらく解決できないことがあるので、今度こそ忘れないようにとメモしておきます。
posted by t2low at 22:00| Cocos2d-x

2014年02月12日

[Cocos2d-x] 階層化されたCCLayerの内側からモーダルレイヤを画面の中心に表示する(改)

以前、以下のような記事を書きました。
tappli blog: [Cocos2d-x] 階層化されたCCLayerの内側からモーダルレイヤを画面の中心に表示する
親階層との位置を計算してモーダルレイヤを中心に表示するというものでした。
今日ふと気づいたのですが、そんな面倒な計算をしなくても親階層に追加(addChild)すれば良いだけだったのでは…。
CCNode *parent = this;
while (parent->getParent()) {
parent = parent->getParent();
}
parent->addChild(modal);

こんな感じ。
問題なさそう。

posted by t2low at 22:00| Cocos2d-x

2014年02月05日

[Cocos2d-x] Classesの中を整理する

Cocos2d-xの開発をEclipseで行うときの設定です。
Xcodeだとこの辺はあまり気にしなくても良い感じになってますね。

Cocos2d-xでプロジェクトを作ると、Classesに4つのファイルができます。
Classes/
├AppDelegate.cpp
├AppDelegate.h
├HelloWorldScene.cpp
└HelloWorldScene.h

最初のうちは良いのですが、ファイルが増えてくるとわかりにくくなります。
↓こんな感じ。わかりにくいですね。
Classes/
├AppDelegate.cpp
├AppDelegate.h
├Fuga.cpp
├Fuga.h
├HelloWorldScene.cpp
├HelloWorldScene.h
├Hoge.cpp
├Hoge.h
├Piyo.cpp
└Piyo.h

こうなってしまう前にフォルダにまとめたいですね。
Classes/
├AppDelegate.cpp
├AppDelegate.h
├HelloWorldScene.cpp
├HelloWorldScene.h
└HogeHoge/
 ├Fuga.cpp
 ├Fuga.h
 ├Hoge.cpp
 ├Hoge.h
 ├Piyo.cpp
 └Piyo.h

しかし、ファイルの場所を変えてしまうとビルドできなくなってしまいます。
ファイルが参照できるようにしましょう。
// before
#include "Hoge.h"
// ↓
// after
#include "HogeHoge/Hoge.h"

としても参照できますが、一つ一つ変更するのはめんどくさいです。
Makefile(Android.mk)の中にHogeHogeからもincludeできるように指定するのが良い気がします。
↓このようにしておけば、#include "Hoge.h"のまま参照可能です。
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../Classes \
$(LOCAL_PATH)/../../Classes/HogeHoge/

ソースファイルの場所も修正しておきましょう。
LOCAL_SRC_FILES := hellocpp/main.cpp \
../../Classes/AppDelegate.cpp \
../../Classes/HelloWorldScene.cpp \
../../Classes/HogeHoge/Hoge.cpp \
../../Classes/HogeHoge/Fuga.cpp \
../../Classes/HogeHoge/Piyo.cpp

これでビルドできるようになるはずです。
ソースをグループ化できるのでわかりやすくなりますね。
posted by t2low at 22:00| Cocos2d-x

2014年01月17日

[C++] double型を3桁ずつカンマで区切られた文字列に変換する

double型を3桁ずつカンマで区切られた文字列に変換する。
たったこれだけのことにえらい苦労しました。
C++の基本的なとこがわかっていないのですね。C++の基礎をひと通り勉強したほうが良さそうです。

良し悪しはともかく出来上がったのは以下のようなコード。
std:string str;
double value = 99999999.999;

std::stringstream stream;
stream << std::setprecision(2) << std::setiosflags(std::ios::fixed) << value;
stream >> str;

int signLen = value < 0 ? 1 : 0;
int dotPos = str.find('.');
for (int pos = dotPos - 3; pos > signLen; pos -= 3) {
str.insert(pos, ",", 1);
}

insert()で1個ずつカンマを挿入するようにしましたが効率は悪そう…。

参考:
cp_C++言語(予備知識) 

posted by t2low at 23:00| Cocos2d-x

2014年01月16日

[Cocos2d-x][C++] vector、mapが参照しているメモリを開放しつつ全削除

C++のmap、vectorに慣れておらず、苦労しました。
これで良いのではないかと思ってますが、間違ってたらつっこんでください…。

vector
for (vector<Hoge *>::iterator it = hoges.begin(); it != hoges.end();) {
delete *it;
it = hoges.erase(it);
}


map
for (map<int, Hoge *>::iterator it = hoges.begin(); it != hoges.end(); it++) {
delete it->second;
}
hoges.clear();

secondが値を指しているということなんですかね。わかりにくいですね。なんでこんな名前にしたんですかね。

参考:
c++ - How to erase & delete pointers to objects stored in a vector? - Stack Overflow
C++とのつきあい方(16)
posted by t2low at 23:00| Cocos2d-x

2014年01月15日

[Cocos2d-x] Resources内のファイルの読み込み

Resources内のファイルを読み込んで使うには以下のようにすれば良いみたい。
CCFileUtils *fileUtils = CCFileUtils::sharedFileUtils();
string path = fileUtils->fullPathForFilename("hoge.json");
unsigned long size = 0;
unsigned char *data = fileUtils->getFileData(path.c_str(), "r", &size);

// ... data を使ってなんかする

delete[] data;

getFileData()の中を見たら、第2引数をそのままfopen()につっこんでいたので、バイナリデータを読み込むときは"rb"にするのかな?


タグ:C++ cocos2d-x
posted by t2low at 22:00| Cocos2d-x

2014年01月10日

[Cocos2d-x] 階層化されたCCLayerの内側からモーダルレイヤを画面の中心に表示する


以下の本の「レシピ38」を参考にモーダルレイヤを作成しました。



画面の真ん中にドンとモーダルレイヤが表示される…はずだったのですが、僕の書いたコードでは上にずれて表示されてしまいました。
原因はCCLayerをいくつか階層化していたために、画面の左下とレイヤの原点がずれてしまっていたためです。

Cocos2d-xでは位置を親との相対座標で指定するようです。
これによって、階層の途中に位置が(0, 0)でないレイヤがあると、モーダルレイヤの位置を(0, 0)にしていたとしてもずれて表示されてしまいます。
少し調べただけでは、そのようなときに使う特別なメソッドはわかりませんでした。
それならばと、親階層をたどって左下の座標を計算するコードを書きました。
float x = 0;
float y = 0;
CCNode *node = modal->getParent();
while (node) {
x -= node->getPositionX();
y -= node->getPositionY();
node = node->getParent();
}
modal->setPosition(ccp(x, y));

とりあえずこれで画面の中心に表示できるようになりました。

タグ:cocos2d-x
posted by t2low at 23:00| Cocos2d-x

2014年01月07日

[Cocos2d-x] CCSpriteの重なり順

CCSpriteの重なり順を設定するには、reorderChild()というメソッドを呼べば良いらしい。

Cocos2d-x: 重なり順の取得、変更 | Lady Wendy

CCLayerを継承したクラスで以下のように呼んでみた。
for (int i = 0; i < MAX_HOGE; i++) {
CCSprite *sprite = (CCSprite *) getChildByTag(i);
if (sprite == NULL) {
continue;
}
reorderChild(sprite, HEIGHT - sprite->getPositionY());
}

結果は失敗。重なり順は変わらない。
少し調べてみると以下のような記事が見つかった。

cocos2d-x - CCSpriteBatchNodeのzOrder - Qiita [キータ]

確かにCCSpriteBatchNodeは使っているけれど、これを使ったせいで重なり順が変えられないなんてことあるわけない(はず!)。
と、ここで気がついた。
CCSpriteBatchNode *batch = (CCSpriteBatchNode *) getChildByTag(TAG_BATCH);
for (int i = 0; i < MAX_HOGE; i++) {
CCSprite *sprite = (CCSprite *) batch->getChildByTag(i);
if (sprite == NULL) {
continue;
}
batch->reorderChild(sprite, HEIGHT - sprite->getPositionY());
}

階層としては

・CCLayer
 └CCSpriteBatchNode
  ├CCSprite
  ├CCSprite
  ├CCSprite
  :
  └CCSprite

となっているのだから、CCSpriteBatchNodeのreorderChild()を呼ばなきゃダメだったわけね。
これで期待通りの重なり順になりました。

タグ:cocos2d-x
posted by t2low at 22:00| Cocos2d-x

2014年01月06日

[Cocos2d-x] テクスチャアトラスとCCSpriteBatchNodeを使ってスプライトを描画する

また忘れてしまいそうなのでメモ。
テクスチャアトラス(atlas.plist/atlas.png)があって、その中にhoge.pngがある場合、以下のようにすると良い(っぽい)。
 CCSpriteFrameCache *cache = CCSpriteFrameCache::sharedSpriteFrameCache();
cache->addSpriteFramesWithFile("atlas.plist");

CCSpriteBatchNode *batch = CCSpriteBatchNode::create("atlas.png");
addChild(batch);

for (int i = 0; i < 100; i++) {
CCSprite *sprite = CCSprite::createWithSpriteFrameName("hoge.png");
sprite->setPosition(ccp(i, i));
batch->addChild(sprite);
}


  • CCSpriteFrameCacheにはテクスチャアトラスの○○.plist

  • CCSpriteBatchNodeにはテクスチャアトラスの○○.png

  • CCSpriteにはテクスチャアトラス内の□□.png

  • をそれぞれ指定する。
    CCSpriteBatchNodeにもテクスチャアトラス内の画像名を渡していて少しハマっていた…。

posted by t2low at 22:00| Cocos2d-x

2013年12月25日

[Cocos2d-x][C++] C++で型推論

今までC++をほとんどやってこなかったので知りませんでした。
C++では型推論できたんですね。
CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
CCTableView *tableView = CCTableView::create(this, CCSizeMake(visibleSize.width, visibleSize.height));
tableView->setDirection(kCCScrollViewDirectionVertical);
// 以下略

↑コレを↓のように書けます。
auto visibleSize = CCDirector::sharedDirector()->getVisibleSize();
auto tableView = CCTableView::create(this, CCSizeMake(visibleSize.width, visibleSize.height));
tableView->setDirection(kCCScrollViewDirectionVertical);
// 以下略

autoで定義された変数に対しても、Eclipse/Xcodeの補完が効きます。すごい!

どうやらC++11で導入されたようですね。
C++11 - Wikipedia



posted by t2low at 22:00| Cocos2d-x

2013年12月24日

[Cocos2d-x] CCTableViewでヘッダーを使いたい

iOSのUITableView(?)にはヘッダー・フッターを扱う機能があるらしいですね。
Cocos2d-xのCCTableViewにも同様の機能があるかと思って調べたのですが、(僕が調べた限りでは)どうやらそういった機能はなさそう…。
自分でなんとかするしかないようです。
とりあえず、実現できそうだったのでやり方をメモ。


  • ヘッダーとして表示したいViewのインスタンスを用意する

    • 開放されないようにretain()しておく

    • 使わなくなったらrelease()する



  • CCTableViewDataSource#tableCellAtIndex()で返却するViewを選択する

    • idxが0のときは、ヘッダーのインスタンスを返却

    • idxが0以外のときは、その他のインスタンスを返却。CCTableView#dequeueCell()したり、create()したり。

    • CCTableView#dequeueCell()でヘッダーのインスタンスが返ってきたら使わないようにする。もっかいdequeueCell()したり、create()したり。





たぶんコレできちんと再利用されるはず。
(ソースがなくて申し訳ない)

タグ:cocos2d-x
posted by t2low at 22:00| Cocos2d-x

2013年12月20日

[Cocos2d-x] 画面を縦向きにする

Cocos2d-xで縦向き画面のゲームを作りたいので、画面を縦向きにする方法を調べました。

とりあえず、公式サイトの情報。
Device Orientation | Cocos2d-x

タイトルのDevice Orientation (need to be updated)の、「need to be updated」って、「内容が古い」ってことでしょうか…。
実際、2つあると書かれている方法のうち、最初の「setDeviceOrientation()」というメソッドはもう無いようです。

公式情報があてにならないので、非公式なブログなどをいろいろと見てみました。
どうやらAndroidはAndroidManifest.xmlのscreenOrientationをportraitに変えるだけのようですね。

AndroidManifest.xml
android:screenOrientation="portrait"


iOSの方はiOS5とiOS6で処理を変えなきゃいけないようで、iOSの知識がほとんどない僕は結構混乱しました。

【iPhoneアプリ】iOS6では画面の向きが変わった時に呼ばれる関数が違う | 桜花満開/テンシホタル

How to change the orientation to Portrait? | Cocos2d-x

RootViewController.mmというファイルをいじるようです。

RootViewController.mm
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return UIInterfaceOrientationIsPortrait( interfaceOrientation );
}

- (NSUInteger) supportedInterfaceOrientations{
#ifdef __IPHONE_6_0
return UIInterfaceOrientationMaskPortrait;
#endif
}

- (BOOL) shouldAutorotate {
return NO;
}


あと、Xcodeでプロジェクトのトップを選択した時に編集できる画面の「Deployment Info → Device Orientation」の「Portrait」にチェックを入れる必要がありそう。

スクリーンショット 2013-12-20 11.21.14.png

OS毎に別々の設定が必要ということは、画面の回転についてはCocos2d-xの中だけではどうにもならないみたいですね。
OSが違うし仕方がないのかも知れませんね。


posted by t2low at 22:00| Cocos2d-x

2013年12月04日

[Cocos2d-x][Android] animations.plistの配置場所が原因でエラー

Cocos2d-xを始めてから間もないというのに、iOSでは動いて、Androidでは動かないという状況が発生してしまい、調査に疲れました。
結論から言うと、原因はanimations.plistの配置場所でした。

僕は普段はAndroidアプリを開発しているので、開発環境としてはEclipseに慣れています。
しかし、Cocos2d-xで作られたアプリをAndroid実機で動作させるのは少しめんどくさいです。
シェルスクリプトを動かした後で、Eclipseでのビルドを行わなければなりません。
(もっと効率よくビルドする方法もあると思いますが、まだ初心者なので調べておりません)
そのため、コードを書くのはEclipse上で行い、動作の確認はXcodeからiOSシミュレータを起動して行うようになりました。
そんなこんなでしばらくiOSシミュレータでしか動作を確認していなかったのです。

久しぶりにAndroid実機で動作を確認してみたところ、アプリがクラッシュするではありませんか。
おかしいと思いiOSシミュレータで動作を確認すると、こちらは正常に動きます。
まだデバッグ方法もわからない状態なので、CCLog("...")を各所に埋め込んで異常が発生している箇所を特定しました。
結果、animations.plistが読み込めていないことがわかりました。

animations.plistは「Resources/animations/animations.plist」として配置していました。
コード上では以下のように読み込んでいました。
	CCAnimationCache *cache = CCAnimationCache::sharedAnimationCache();
cache->addAnimationsWithFile("animations.plist");

iOSで動かすときはXcodeで明示的に参照を行っていたので、これでも正常に動いたようです。
Androidで動かすときは明示的にどうこうしないので、ファイルの場所がわからず実行時にクラッシュしていたようです。
配置場所を「Resources/animations.plist」とすることで、どちらの環境でも正常に動きました。

階層を設けてもAndroid/iOSともに同じコードで動かす方法もあるような気がしますが、そこは未調査でございます。

posted by t2low at 22:00| Cocos2d-x

2013年12月03日

[Cocos2d-x][Android] エラー発生

create_project.pyで作成されたプロジェクトがどのような構成になっているのか確認しようと、ソースファイルを見ていたら…。
「jni/hellocpp/main.cpp」をEclipseで開いたところ、盛大にエラーが発生していました。
どうやらいろいろと定義が見つからない状態の様子。直しましょう。

■Eclipseの環境設定(Cocos2d-xへの参照)

Cocos2d-x開発入門(p29〜)に「Eclipseの設定」という項目がありました。
この項目でCocos2d-xへの参照を設定していました。
Eclipseの「環境設定...」を開き「General→Workspace→LinkedRecources」の「New...」ボタンを押します。
Name:に「COCOS2DX」と入力し、Location:にはCocos2d-xを展開した場所を絶対パスで指定します。
この設定の後、プロジェクトをリフレッシュしたところ、エラーがかなり減りました。
しかし、まだ残っています。

■Eclipseの環境設定(NDKへの参照)

再びEclipseの環境設定を開きます。
今度は「C/C++→Build→Environment」の「Add...」ボタンを押します。
Name:に「NDK_ROOT」、Value:にNDKを展開した場所を指定します。
こちらは相対パスを指定しても怒られません。良いのでしょうか。わからないので、とりあえず絶対パスで指定しました。
続いて「C/C++→Code Analysis」の「Syntax and Semantic Errors」のチェックを外します。
「Apply」ボタンを押したところ、main.cppのエラーがすべて消えました。
発生すべきエラーをもみ消してしまったような印象があるのですが、これで良いのでしょうか?
(このチェックを外すところはプロジェクト毎のプロパティで設定した方が良いかも?)

■他
この設定が抜けていたせいなのかどうかはわかりませんが、これまでClassesやcocos2dxのフォルダが開けませんでした。
一度プロジェクトをEclipseから削除し、再度インポートしなおしたところ、Classesフォルダ内のファイルも開けるようになりました。
良かった良かった。

とりあえず、エラーは消えたので作業を続けます。



posted by t2low at 21:00| Cocos2d-x