[cocos2d-x 3.x]関連メモ

あくまで私がやってきたことの雑なメモなので参考程度に。


##cocosコマンド - プロジェクトの作成
```
$ cocos new projectName -p com.package -l cpp -d ~/Directory
```
見ればわかるが引数は以下の通り
new - プロジェクト名
-p  - パッケージ名
-l  - 使用言語(cpp, lua, js)
-d  - プロジェクトを保存するディレクトリ

##新規クラスの作成
xcodeのnew fileよりc++クラスを作成する。
ヘッダーには以下のようなコードを追加しておく。

```cpp:NewClass.h
#include "cocos2d.h"

class NewClass : public cocos2d::Layer
{
public:
    virtual bool init();
    static cocos2d::Scene* scene();
    //createを使えるようにする
    CREATE_FUNC(MapLayer);
    
};
```

cppファイルにはcocos2dのネームスペースを使えるようにする以下のコードを書いておくとよい。

```cpp:NewClass.cpp
USING_NS_CC;
```
その後ヘッダーで宣言しておいたメソッドの実装をしていく。

[詳しい参考](http://study-cocos2d-x.info/game/132/)

##シーンの切り替え
シーンの切り替えの方法は4種類あるみたい。
####runWithScene
プログラムの起動時に最初のシーンを起動する。
一番最初に設定するシーンはこれで処理する。

```AppDelegate.cpp
auto scene = HelloWorld::createScene();
director->runWithScene(scene);
```
cocosコマンドでプロジェクトを作成するとあらかじめAppDelegateに実装されている…はず


####replaceScene
移動する際に直前のシーンをメモリから消してしまう方法。基本的にはこれ。
直前のシーンをもう使わない場合はこの方法を使う。

```cpp
// 次のシーンを生成する
auto scene = LayerClass::createScene();
Director::getInstance()->replaceScene(scene);
```


####pushScene
移動する際に直前のシーンをメモリに残しておく方法。
前のシーンに戻る予定がある場合はこの方法を使う。

```cpp
// いまのシーンをメモリに残して次のシーンを生成する
auto scene = Layer::createScene();
Director::getInstance()->pushScene(scene);
```
やり方はreplaceSceneと同じ。
replaceをpushに変えるだけ。


####popScene
プッシュしたシーンを消して元のシーンに戻る方法。

```cpp
//元のシーンに戻る
Director::getInstance()->popScene();
```
プッシュ先のシーンで上の1行を実装するだけ

###シーン移動時にエフェクトを掛ける
いろいろある。
[シーンの移動に特殊なエフェクトをかける](http://study-cocos2d-x.info/scenelayer/55/)

##ValueVector、ValueMapの取り扱い
cocos2d::ValueVector = 配列
cocos2d::ValueMap = 辞書
という認識で合っていると思う。
NSDictionaryやNSArrayをcpp上で扱いたい場合はこいつらに変えてあげる。

###注意点
代入やifなどの比較に使うときにはasメソッドを忘れずに付けること。

```cpp
cocos2d::ValueVector valueVector = valueMap["string"].asValueVector();

if (valueVector[1].asString() == "string"){}
```

これを怠ると

```
Invalid operands to binary expressions
```
というエラーを喰らい先生のお世話になることになります。


##データの保存
プレイヤーなどのデータを永続的に保持しておきたいときはplistかUserDefaultにしてクライアントの端末に保存しておく必要がある。

###UserDefault
簡単なデータを保存しておきたいならUserDefaultのほうが簡単に出来て便利。


####データを保存する
データの保存はsetStringForKeyメソッドを使って行う。
引数はkeyと値。

```cpp
UserDefault* userDef = UserDefault->getInstance();

//文字列
userDef->setStringForKey("key1", "Save String Data");
 
//int型
userDef->setIntegerForKey("key2",100);
 
//float型
userDef->setFloatForKey("key3",2.0f);
 
//bool型
userDef->setBoolForKey("key4",false);
```

####データの読み出し
UserDefaultから読み込むときにauto型で変数を宣言しておくととても楽。

```cpp
UserDefault* userDef = UserDefault->getInstance();

auto data = userDef->getStringForKey("key");
```

####インスタンスを2つ作った場合
2箇所のクラスでUserDefaultを作成した場合どうなるのか？
ちょっと気になって試してみたので一応残しておく。

Datasource.cppとLayer.cppでUserDefaultを作成する。
各ヘッダーで`UserDefault* userDef`を宣言しておくものとする。
Datasource.cppにはUserDefaultの値を出力するメソッドを用意しておく。

```cpp:Datasource.cpp
void Datasource::initDatasource()
{
	userDef = UserDefault::getInstance();
	//テキトーな文字列をセット
	userDef->setStringForKey("test", "データソースでセットされた文字");
	
	CCLOG("Datasource userDef = %p", &userDef);		//アドレスを表示
	CCLOG("Datasource userDef = %s", userDef->getStringForKey("test"));		//セットされている文字列を表示
}

void Datasource::dispUserDef()
{
	//レイヤークラスからデータソースクラスの値を確認するためのメソッド
	CCLOG("Datasource userDef = %s", userDef->getStringForKey("test").c_str());
}
```

```cpp:Layer.cpp
	Datasource* datasource = new Datasource();
	datasource->initDatasource();
	
	userDef = UserDefault::getInstance();
	//文字が入ってるか確認
	CCLOG("Layer userDef = %s", userDef->getStringForKey("test").c_str());
	
	//データソースのときと同じkeyを使って文字列をセット
	userDef->setStringForKey("test", "レイヤーでセットされた文字");

	CCLOG("Layer userDef = %p", &userDef);		//アドレスを表示
	CCLOG("Layer userDef = %s", userDef->getStringForKey("test").c_str());		//セットされている文字列を表示
	
	datasource->dispUserDef();
```
こんな感じで出力された。

```Output
Datasource userDef = 0x7f908bc084f0
Datasource userDef = データソースでセットされた文字
Layer userDef = データソースでセットされた文字
Layer userDef = 0x7f908a8895f0
Layer userDef = レイヤーでセットされた文字
Datasource userDef = レイヤーでセットされた文字
```
Layer.cppのUserDefaultにセットしたものがDatasource.cppのUserDefaultにも反映されている。

結果:
ポインタのアドレスは違うけど中身は共有されている？

ちなみに

```cpp:Layer.cpp
userDef = datasource->userDef;
```
と、いうようにLayer.cppにてuserDefにgetInstance()ではなくDatasource.cppのuserDefの代入を行った場合も同じ結果になる。

###plist
データがなんだかんだで複雑になりそうなときはplistにしておくと良いかも。

####書き込み(保存)

```cpp
//保存する奴。中身のセットは割愛
cocos2d::ValueMap writeList;

//plistのファイルパスを取得
std::string filepath = cocos2d::FileUtils::getInstance()->getWritablePath() + "writable.plist";

//書き込みを行う
cocos2d::FileUtils::getInstance()->writeToFile(writeList, filepath);
```
####読み込み

```cpp
//plistのファイルパスを取得
std::string mFilepath = cocos2d::FileUtils::getInstance()->getWritablePath() + "Map.plist";
std::string vFilepath = cocos2d::FileUtils::getInstance()->getWritablePath() + "vector.plist";


//Dictionary型
cocos2d::ValueMap mapList = cocos2::FileUtils::getInstance()->getValueMapFromFile(mFilepath + "Map.plist");

//Array型
cocos2d::Value vectorList = cocos2d::FileUtils::getInstance()->getValueVectorFromFile(vFilepath + "Vector.plist");
```

####備考
plistを実ファイルで確認したいなんてときがあったりする。
iPhone Simulatorで保存を行うと以下のパスに保存される。(Xcode6以降)
/Users/[User]/Library/Developer/CoreSimulator/Devices/[DeviceID]/data/Containers/Data/Application/[App]

##画像ボタン
画像付きのボタンを作るならこんな感じ

```cpp
//ボタンの作成
auto button = MenuItemImage::create("押す前.png","押した時.png", CC_CALLBACK_1(HelloWorld::tapEvent, this));
button->setPosition(Point(x, y));

//メニューの作成
Menu* menu = Menu::create(button, NULL);

//メニューの配置
menu->setPosition(Vec2::ZERO);
addChild(menu);
```
CC_CALLBACKのケツに付いてる数字は呼ぶ先の引数の数らしいけどよくわかってない。

###コールバック先に数値を渡したい
CC_CALLBACKで呼ぶメソッドにある数値を渡したい…なんてときがあった。
引数として渡す方法はあるのかないのかわからないけどとりあえずint値ならsetTagすることで渡せることがわかった。

```cpp
//タグのセット
int i = 7;
button->setTag(i);
```

```cpp
//コールバック先
auto button = (MenuItemImage*)sender;
//タグの取得
int tag = button->getTag();
```
senderは引数の`Ref* sender`である。
tagには数値の7が入る。

いまのところint渡せればなんとかなってるからいいけど他の型渡したくなったらどうすればいいんだろ…。



##アニメーション
ドットキャラが移動するアニメーションをちょこっと作った。
###移動アニメーション
動かすスプライトを作る。

```cpp
//スプライトの作成
chrSpr = Sprite::create("char01.png");

//適当に座標セット
float charX = 100.0;
float charY = 50.0;
chrSpr->setPosition(Point(charX, charY));    

//レイヤーにスプライトを追加
addChild(chrSpr);
```
アニメーションの作成、実行。

```cpp
//移動先の座標
float moveX = 200.0;
float moveY = 100.0;
//移動先の指定	第１引数:秒数  第２引数:移動先の座標
MoveTo* move = MoveTo::create(3, Point(moveX, moveY));

//アニメーションの作成
Animation* animation = Animation::create();
//移動1 - 停止 - 移動2 - 停止の4コマアニメーション
animation->addSpriteFrameWithFile("char01.png");
animation->addSpriteFrameWithFile("char02.png");
animation->addSpriteFrameWithFile("char03.png");
animation->addSpriteFrameWithFile("char02.png");

//アニメーションの設定 : 1コマを0.1秒で切り替える
animation->setDelayPerUnit(0.1);
//アニメーションの設定 : 8回ループさせる
animation->setLoops(8);

//アニメーションの実行
Animate* animate = Animate::create(animation);
chrSpr->runAction(animate);
chrSpr->runAction(move);
```

###アニメーションを止める
スプライトのアクションをすべて止める

```cpp
//アニメーションの停止
sprite->stopAllActions();
```

一時停止させる

```cpp
//アニメーションの一時停止
sprite->pause();
```

再開させる

```cpp
//一時停止させたやつを再開
sprite->resume();
```

他にも指定したアクションだけ止めたり、タグを使って指定したり出来るみたい。
詳しいことは[LadyWendy](http://ladywendy.com/lab/cocos2d-x/75.html)様のサイトをどうぞ。


##setPosition
setPositionの引数の渡し方にはPointとVec2の渡し方があることをいまのとこ確認している。
Pointについてちょっとだけわかってきたのでメモしておこう。
###Point
画像付きボタンの作成でも書いてあるがこんな使う時は

```cpp
button->setPosition(Point(x, y))
```
こんな感じ。
基本的に作成した(画像ボタンの場合)画像ボタンの中心点を0として考える。
なのでsetPosition(Point::ZERO)とした場合、端末左下に画像ボタンの右上だけが顔を出すみたいな感じになる。実際の画像がないとわかりずらいけどそうなる。

ボタン全体を見えるようにしたいならば

```cpp
button->setPosition(Point(button->getContentSize().width / 2, button->getContentSize().height / 2));
```
てな具合にすればとりあえず画面左下ピッタリに画像ボタンを出すことができる。
ContentSize()は名前の通りそのアイテムのサイズを取得することが出来る。

###Vec2
**よくわからない**

##CCLOG
%@で出るかなと思ったら出なかった(当然)からメモ。
###std::string
std::stringを出力したい場合はc_str()を付けなければいけない。

```cpp
std::string str = "string";
CCLOG("%s", str.c_str());
```
CCLOGに限らずchar*に変換(?)する場合は必要なのかな？

その他の演算子とか細かいのは[公式](http://www.cocos2d-x.org/wiki/How_to_use_CCLOG)を見るのがはやい。

##createに引数を持たせる
CREATE_FUNCマクロを使わずに以下のようにcreateを実装してあげれば良い。

```cpp:MyClass.h
public:
	static MyClass* create(int n);
```

```cpp:MyClass.cpp
MyClass* MyClass::create(int n)
{
    MyClass* pRet = new MyClass();
    
    if(pRet && pRet->init(n)){
        pRet->autorelease();
        return pRet;
    }else{
        delete  pRet;
        pRet = NULL;
        return NULL;
    }
}
```
あとはinit関数を引数持ちにしてあげる。

```cpp::MyClass.h
public:
	virtual bool init(int n);
```

```cpp::MyClass.cpp
bool MapLayer::init(int n)
{
    if (!Layer::init()) {
        return false;
    }
    
    CCLOG("引数n  =  %d", n);
    
    return true;
}
```
こんな感じでやればちゃんと引数nが渡されているのが確認出来るので大丈夫だと思う。

######参考
[CREATE_FUNCマクロに引数を持たせるには(cocos2d-x)](http://qiita.com/Riyaaaa_a/items/33a3e1beae8860638f3f)
こちらの記事参考にさせていただきました。