忍者ブログ
  • 2024.10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 2024.12
[PR]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

【2024/11/23 09:54 】 |
cocos2d-xの参照カウンタについて
cocos2d-xにおいては、すべてのクラスは
cocos2d::Ref
の子孫クラスである。
Refクラスは、参照カウンタを扱っている。

Refクラスのautorelease()メソッドを呼んでおくと、
Refとその子孫クラスのインスタンスを生成したとき、破棄処理を自分でしなくても
ゲームのメインループが来たときに自動で破棄してくれる。

CCRef.cpp

Ref* Ref::autorelease()
{
PoolManager::getInstance()->getCurrentPool()->addObject(this);
return this;
}



CCDirector.cpp

void DisplayLinkDirector::mainLoop()
{
if (_purgeDirectorInNextLoop)
{
_purgeDirectorInNextLoop = false;
purgeDirector();
}
else if (! _invalid)
{
drawScene();

// release the objects
PoolManager::getInstance()->getCurrentPool()->clear();
}
}



CCAutoReleasePool.cpp

void AutoreleasePool::clear()
{
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
_isClearing = true;
#endif
for (const auto &obj : _managedObjectArray)
{
obj->release();
}
_managedObjectArray.clear();
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
_isClearing = false;
#endif
}



CCRef.cpp

void Ref::release()
{
CCASSERT(_referenceCount > 0, "reference count should greater than 0");
--_referenceCount;

if (_referenceCount == 0)
{
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
auto poolManager = PoolManager::getInstance();
if (!poolManager->getCurrentPool()->isClearing() && poolManager->isObjectInPools(this))
{
// Trigger an assert if the reference count is 0 but the Ref is still in autorelease pool.
// This happens when 'autorelease/release' were not used in pairs with 'new/retain'.
//
// Wrong usage (1):
//
// auto obj = Node::create(); // Ref = 1, but it's an autorelease Ref which means it was in the autorelease pool.
// obj->autorelease(); // Wrong: If you wish to invoke autorelease several times, you should retain `obj` first.
//
// Wrong usage (2):
//
// auto obj = Node::create();
// obj->release(); // Wrong: obj is an autorelease Ref, it will be released when clearing current pool.
//
// Correct usage (1):
//
// auto obj = Node::create();
// |- new Node(); // `new` is the pair of the `autorelease` of next line
// |- autorelease(); // The pair of `new Node`.
//
// obj->retain();
// obj->autorelease(); // This `autorelease` is the pair of `retain` of previous line.
//
// Correct usage (2):
//
// auto obj = Node::create();
// obj->retain();
// obj->release(); // This `release` is the pair of `retain` of previous line.
CCASSERT(false, "The reference shouldn't be 0 because it is still in autorelease pool.");
}
#endif

#if CC_USE_MEM_LEAK_DETECTION
untrackRef(this);
#endif
delete this;
}
}



CCAutoReleasePool.cppを見るとわかるが、_managedObjectArrayに格納されているすべてのobjectに対してrelease()を実行している。
ずいぶん潔いやり方だ。生成しても放っておいてよい、というか放っておかなくてはいけない。自分でrelease()とか呼ぶと却ってバグる。
その使い方についてはCCRef.cppのrelase()メソッド内に詳しく書いてある。


一言で言うと
new/retain() と autorelease()/release() を対で呼び出さなければならない。
ということだ。

生成時にnewが呼ばれる。
cocos的には、Node::create()やSprite::create()などのように、static関数のcreate()メソッドが相当。create()の中でnewをしている。
例としてNode::create()を見てみると


Node * Node::create()
{
Node * ret = new Node();
if (ret && ret->init())
{
ret->autorelease();
}
else
{
CC_SAFE_DELETE(ret);
}
return ret;
}


となっている。
createの中で new と autorelease() を両方やっている。

それ以外にも、retain()を呼ぶならrelease()かautorelease()をどこかで呼んでおく。
対で呼び出されるようになっていれば、メモリリークは起こらない。

autorelease()は複数回呼び出しても問題ないようだ。autoreleasePoolに2回登録されるので、2回releaseが走る。


なお、Nodeインスタンスの場合、別のNodeの子になる(addChild)と参照カウンタが1増える。

parentNode->addChild(childNode); //childNodeの参照カウンタが1増える



これは、parentNodeのメンバ変数
Vector<Node*> _children;
に追加されるから。
cocos2d-xのVectorクラスは、pushBack(またはinsert,replace)したときにretain()を呼ぶようになっている。
このため、addChildされた子ノードは破棄されずに残る。


PR
【2014/12/09 13:13 】 | cocos2d-x | 有り難いご意見(0)
cocos2d-xのアニメーション
cocosでアニメーション作成する

以下は、立っている絵から歩きアニメーションを再生するところ。

standSprite = Sprite::create("stand.png");

Animation* walkAnimation;
walkAnimation = Animation::create();
walkAnimation->addSpriteFrame(cache->getSpriteFrameByName("walk_1.png"));
walkAnimation->addSpriteFrame(cache->getSpriteFrameByName("walk_2.png"));
walkAnimation->setDelayPerUnit(0.5f);
walkAnimation->setRestoreOriginalFrame(true); //(A)

Animate* animate = Animate::create(walkAnimation);
standSprite->runAction(RepeatForever::create(animate));



standSpriteは、アニメーションを再生しなければ単なるスプライト。
アニメーション(この場合スプライトアニメーション)を再生することで、立ち絵のスプライトが歩きスプライトのアニメーションに変わる。
setRestoreOriginalFrame(true)
を指定していると、アニメーションが終了したときに元のスプライト(立ち絵の表示)に戻る。ただしここではRepeatForeverアニメーションなので、終了することがないので設定が無意味だが。






【2014/12/09 12:33 】 | cocos2d-x | 有り難いご意見(0)
cocos2.2 android
なんで2.2か、という話でもあるが。


まずAndroidの環境設定(これ書くの何回目?)


参考

まずandroidとeclipseダウンロード

公式からダウンロード

展開。この時点でeclipseは起動する。

次にNDKインストール。
公式からダウンロード

これ、このままだと展開できない。
$ chmod a+x android-ndk-r10c-darwin-x86_64.bin
$ ./android-ndk-r10c-darwin-x86_64.bin

とすると展開できる。
android-ndk-r10c/
のようなディレクトリができるので、どっかに置いておく。

ここまででandroidは終了


cocosのインストールを行う(割愛)


以下のシェルスクリプトを実行

$ PROJ_NAME/cocos2d-x-2.2.4/projects/PROJ_NAME/proj.android/build_native.sh

これをすると、
proj.android/libs/armeabi/libcocos2dcpp.so
というライブラリが生成される。
これがないと、C++ネイティブのcocosの機能が使えないので。
ちなみにこれをロードするには、
MainActivity.javaに以下のように記述する。

public class MainActivity extends Cocos2dxActivity{
static{
System.loadLibrary("cocos2dcpp");
}
}





ここまでやったら、eclipseを起動する。

該当のプロジェクトを開く。
File->New->Other->Android->Android Project from Existing Code->Next
ブラウザからディレクトリ指定。

プロジェクトが開くのを確認。

さらに、cocos2dxのプロジェクトも開く。


該当のプロジェクトから、プロパティを開き、Androidタブを開く。
ライブラリの設定が外れていると思うので、再度設定。


【2014/11/07 10:33 】 | cocos2d-x | 有り難いご意見(0)
cocosのスクロールビュー
cocosでスクロールビューを使う方法。

ここが詳しい


スクロールは設定が少し多いので気をつけよう。

基本は以下のような形。


//作成、ビューサイズの設定(スクロールビュー自体のサイズ)
ScrollView* scrollView = ScrollView::create(viewSize);

//スクロール方向の指定
scrollView->setDirection(ScrollView::Direction::VERTICAL); //HORIZONTAL, BOTH もある

//スクロールビュー自体の座標指定. 左下位置を指定すること
scrollView->setPosition(pos);

//スクロールビューの中身となるノードを指定. このノード(と子孫のノード)がスクロールすることになる
//また、このノードのサイズがScrollViewのcontentSizeになる
scrollView->setContainer(containerNode);

//スクロールビューの中身のサイズを指定。containerNodeのサイズ通りなら、指定は不要
//内部的には、scrollViewのcontentSizeでなく、containerのcontentSizeをセットしている。だからsetContainer()の後に実行しないと反映されない
scrollView->setContentSize(contentSize);

//スクロールイベントを受け取るdelegateをセットする。通常はthisにして、このメソッドを実行しているLayerクラスを指定するのが自然だろう。
scrollView->setDelegate(delegate);

//このスクロールビューを親ノード(parent)の子につける。
parent->addChild(scrollView, order);






気をつけるポイント1
ScrollViewはsetAnchorPoint()が効かない(バグ?)。setPosition()は常に左下座標を指定すること。

気をつけるポイント2
スクロールビューは、デフォルトだと一番下までスクロールした状態になっている(VERTICALまたはBOTHの場合)。
普通は一番上にスクロールさせるはずだと思う。

scrollView->setContentOffset(scrollView->minContainerOffset(), false);



こうしておくと、一番上にスクロールした状態になる。
第2引数をtrueにすると、アニメーションしながらスクロールする。
通常はtrueがいいが、この場合は一瞬でスクロールさせたいのでfalseが適切。


スクロールビューは、たとえsetVisible(false)にして見えないようにしていても、タッチイベントに反応してスクロールしてしまう。

これに関する解決は別の記事で。
【2014/09/28 16:57 】 | cocos2d-x | 有り難いご意見(0)
cocos2d-xでタッチ検知
cocos2d-xでタッチを検知する方法は主に2つ。

1. MenuItemを使う方法


//itemの作成
MenuItemImage* item = MenuItemImage::create(fname1, fname2, CC_CALLBACK_1(HellowWorldScene::tapCallback, this));

item->setPosition(pos);
item->setScale(sx,sy);
item->setAnchorPoint(Vec2(.5f,.5f));


//menuの作成. 今作ったitemImageを表示する
Menu *menu = Menu::create(item, NULL);

//menu自体は親座標と同じ位置
menu->setPosition(Vec2::ZERO);

menu->setVisible(isVisible);

//menuをaddChildする
parent->addChild(menu, order);

void HelloWorldScene::tapCallback(Ref* pSender) {
MenuItemImage *item = (MenuItemImage*)pSender;
//何らかの処理
}







この方法だと、Menuの範囲内をタップしたときだけコールバックが呼ばれる。

menu->setEnabled(false);
または
item->setEnabled(false);
とすると反応しなくなる。スプライトは表示されたまま。

menu->setVisible(false);
または
item->setVisible(false);
とすると、スプライトも非表示で、タップも反応しなくなる。



2. イベントリスナーを使う方法


//イベントリスナー作成
auto listener = EventListenerTouchOneByOne::create(); //シングルタップ
//auto listener = EventListenerTouchAllAtOnce::create(); //マルチタップ

//イベントを飲み込むかどうか
listener->setSwallowTouches(true);

//タッチメソッド設定
listener->onTouchBegan = CC_CALLBACK_2(MyScene::onTouchBegan, this);
listener->onTouchMoved = CC_CALLBACK_2(MyScene::onTouchMoved, this);
listener->onTouchEnded = CC_CALLBACK_2(MyScene::onTouchEnded, this);
listener->onTouchCancelled = CC_CALLBACK_2(MyScene::onTouchCancelled, this);

//優先度100でディスパッチャーに登録
this->getEventDispatcher()->addEventListenerWithFixedPriority(listener, 100);


bool HelloWorldScene::onTouchBegan(Touch *touch, Event *unused_event)
{
Vec2 pos = touch->getLocation(); //左下原点. OpenGL座標系
//Vec2 posInView = touch->getLocationInView(); //左上原点. ビュー座標系
Vec2 posInSprite = sprite->convertToNodeSpace(pos); //sprite相対の座標に変換

//何らかの処理

return true;
}
bool HelloWorldScene::onTouchMoved(Touch *touch, Event *unused_event){
}
bool HelloWorldScene::onTouchEnded(Touch *touch, Event *unused_event){
}
bool HelloWorldScene::onTouchCancelled(Touch *touch, Event *unused_event){
}






となる。
この場合、スクリーン全体のどこでもタップが検知されるので、範囲チェックを自分で行うこと。
タップ座標が欲しいときは、
touch->getLocation();
touch->getLocationInView();
のいずれかを使う。
cocosが普通使っている左下原点の座標系はgetLocation()のほうである。

上記の例だと、タップした座標をspriteの座標系に変換している。

また、画面のどこをタップしてもonTouchBegan()らが呼ばれるわけではなく、
タップした位置がMenuであれば、そちらのコールバックの呼び出しが優先され、onTouchBeganは呼ばれなくなる。

参考
【2014/09/20 12:42 】 | cocos2d-x | 有り難いご意見(0)
cocos スプライトなど
cocos2d-xのスプライトインスタンス。
座標を取りたいときは

Rect bgRect = bgSprite->getSpriteFrame()->getRectInPixels();
Rect bgRect = bgSprite->getSpriteFrame()->getRect();
などが使える。
ただしこれは、cocoaとは違い、frameというのは自分から見た座標系らしく、bgRect.originは零ベクトルになっている。

Vec2 bgPoint = bgSprite->getSpriteFrame()->getOffset();

というメソッドもあるが、これを使っても零ベクトル。
仕方ないので
bgSprite->getPositionY() + bgRect.size.height
みたいにやることになる。

ここでさらに注意したいのは、cocosはy座標が上向きになっていること。
自己流でyを下向きに持っていたりすると、getPositionY の値が意外なところにいたりするので注意
【2014/09/16 13:44 】 | cocos2d-x | 有り難いご意見(0)
cocos2d-xをバージョンアップする方法
これは簡単で、

まずcocos2d-xの公式ページへ行く
ダウンロードページ

で、最新のバージョンを選択。


ダウンロードされます。

適当なディレクトリに解凍したあと、
コンソールから setup.py を実行。



$ ./setup.py

バージョンアップでなく、初めてのcocos2d-xのインストールだった場合は、ここでAndroidのSDK_ROOTなどを入れる必要がある。それはまた別記事で。

完了すると、bash_profile を更新するよう言われる。
そこで以下のコマンドを実行する。

$ source ~/.bash_profile

で、コンソールを再起動する。

反映されたかを確認してみる。

$ which cocos
-> /path/to/cocos2d-x-3.2/tools/cocos2d-console/bin/cocos

という具合に表示される。
ディレクトリのバージョン数値があっていればOK。

これにてバージョンアップは完了。

新しいcocosプロジェクトを作成する場合は、以下のコマンド。

$ cocos new NewProject -p com.yourcompany.NewProject -l cpp -d /path/to/NewProject

【2014/09/15 10:58 】 | cocos2d-x | 有り難いご意見(0)
cocosデフォルトプロジェクト作成
毎回使うので、cocosを使ったゲームのプロジェクトのひな形を作った。
フレームワーク化すべきところだが、
臨機応変にやりたいので、git-hubにまるっと置くことにした。

git-hubのプライベートリポジトリに、cocos(や他のフレームワークなども全部入りの状態)のプロジェクトを丸ごと置いて、
新しいゲームを作るときはそれをforkしてローカルに取ってきて始めるという形。

共通部分を手直ししたいときも、git-hubの元プロジェクトを書き換えて、
各自ゲームのプロジェクトで再度pullすればいいだけのはずだ。

ブランチを機能ごとに細かく分ける。
master(基本)
ad_imobile (アイモバイル広告部分)
landscape(横持ち設定部分)
project_name(プロジェクト固有の設定部分。スプラッシュロゴとか、定数マクロとか)

とりあえず、1つのブランチに細かくコミットしていって
あとで各ブランチに分けるときにcherry-pickしていけばよい。


【2014/08/28 14:45 】 | cocos2d-x | 有り難いご意見(0)
cocos2dでARC
cocos2d-x 3.1rcで、ARC設定を有効にした。


プロジェクトの設定で
Build Settings の Objective-C Automatic Reference Counting を Yes にする

ただしこれだとコンパイルに失敗する。

一部のファイルをARC対象から除外する。
その方法は


Build phases の Compile Sources から
AppController.mm
RootViewController.mm
main.m
の3つを選択し、Compiler Flagに
-fno-objc-arc
と書く。

これでコンパイルが通る。


ちなみにAppController.mm もARC対象にしたい場合は
コンパイルエラーがでる dealloc や release の行を消せば概ね良く、一カ所だけ

cocos2d::GLView *glview = cocos2d::GLView::createWithEAGLView(eaglView);




という行だけ、キャストに問題があるエラーが出る。ここは

cocos2d::GLView *glview = cocos2d::GLView::createWithEAGLView((__bridge_retained void *)eaglView);




と書き換えればOK。


残り2ファイルも行けるか?


http://tf.hateblo.jp/entry/2013/04/06/175000

cocos2d-x 2.0の記事だけど。多少参考になる
Cocos2D 2.X プロジェクトをARCに対応させる方法
【2014/06/26 15:08 】 | cocos2d-x | 有り難いご意見(0)
cocos2d-xにアイモバイル広告導入
フレームワークを入れるまではマニュアル通りでOK。
コードを書く段になるとややこしい。
cocos2d-xなのでビューコントローラがテンプレートと違っているとか、コードがC++で書かれているからObj-C側が呼び出しにくいとかそういう問題を解決していく必要がある。

この辺、appCCloudだとcocos2d版SDKも用意されていてとても開発者フレンドリーなのだが、見込める広告収入はアイモバイルのほうが高いとのことなので、そもそも何のためにゲーム作ってるのかと考えると頑張ってアイモバイルを入れることに意味がある。

バナー広告
まずバナー広告を入れる。
バナー広告はViewとして用意されている。(IMobileAdView)
なので、いずれかのビューに addSubview する形を取る。
cocos2d-xの場合、RootViewController が用意されていて、このビューコントローラについているビューは CCEAGLView である。設定箇所は AppController.mm になる。
つまりこのCCEAGLViewに addSubview することになる。

なので、そのままAppController.mmの
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
メソッド内で addSubview しちゃえばよい。マニュアルに書いてあるコードをほぼそのまま使えるぞ。
あとは…分かるな?
もうこれで表示されるぞ!
ちゃんとアイモバイルのほうで設定はしてあるな?シミュレータ上では表示されないぞ!

アイコン広告
アイコン広告を入れる。
仕組みはバナー広告と同じ。Viewとして提供されているので、適切なビューに addSubview するだけ。生成時の引数の形が少し違うだけ。(並べるアイコンの数の指定があったりする)
やることはバナーと同じだ!
マニュアルによると、1画面に出せる広告は1種類だけ、と書いてあるけど、別にそんなこともなくバナーとアイコン同時に出せる。


ミディアムレクタングル広告
なぜか別のSDKとして提供されている。(一緒にしろよ
なぜかこっちはパブリッシャーIDとかが文字列指定になってる。(揃えろよ
表示するとき、ビューコントローラを指定する方法と、ビューを指定する方法とがある。どっちでも好きな方を使おう

//登録
[ImobileSdkAds registerWithPublisherID:@"publisherId" MediaID:@"mediaId" SpotID:@"spotid"];
//広告取得開始
[ImobileSdkAds startBySpotID:@"spotid"];

//ビューコントローラ指定で表示
[ImobileSdkAds showBySpotID:@“spotId“ ViewController:viewController Position:CGPointMake(x, y)];
//ビュー指定で表示
UIView *subview = [[UIView alloc] initWithFrame:CGRectMake(x, y, width, height)];
[view addSubview:subview];
[ImobileSdkAds showBySpotID: @"spotId" View:subview];



ちなみにこちらには、広告取得が完了したときに呼び出すdelegateが指定できる。
クラスにIMobileSdkAdsDelegate を継承させて
[ImobileSdkAds setSpotDelegate:@"spotid" delegate:self];
としておくと、
- (void)imobileSdkAdsSpot:(NSString *)spotid didReadyWithValue:(ImobileSdkAdsReadyResult)value
というメソッドが呼び出される。
これ以降、表示メソッドを呼び出すと広告が出る。(この前に表示メソッド呼んでも何も起きないので注意)
(バナーとかアイコン広告は準備でき次第表示されるので、先にビューを作っておいても構わない。ミディアムレクタングルとかのインライン系はそうでないので、必ず広告取得完了したかの確認をすること)

ちなみに、Web上で広告スポットの登録をしてから実際に使えるようになるまで何時間もかかるので注意。
この間、広告はでないしエラーも出ない。(お粗末!
「安心しろ!君のプログラムは間違ってなんかいない!」



雑感
とりあえず、SDKは相当お粗末。(この点appCCloudは優秀だった)
バナー・アイコン版とインタースティシャル版でSDKが別になってるし、
インタースティシャル版のサンプルプロジェクトで広告表示されないし(何のサンプル?)
中のコードも大概な感じ。

if (IMOBILE_SDK_ADS_SPOT_ID_1 == nil || [IMOBILE_SDK_ADS_SPOT_ID_1 isEqualToString:@""] ) {
//.....
} else if (IMOBILE_SDK_ADS_SPOT_ID_1 != nil || ![IMOBILE_SDK_ADS_SPOT_ID_1 isEqualToString:@""]) {
//........
}



書いた人プログラミング素人なの?

【2014/06/11 14:14 】 | cocos2d-x | 有り難いご意見(0)
<<前ページ | ホーム |