2015年

2月

06日

Cocos2d-x Ver 3.x ラベルのAction 回転 移動 拡大 ジャンプ(Ref対応版)


ボタンを押してラベルを回転、移動、拡大、ジャンプをさせるAction。

このような一連の動きをどのように記述すればよいか把握できる基礎になるかと思いまとめてみました。


Cocos2dx 3.x C++言語

ActionTopScene.hを次のように変更してみてください。


#ifndef __Action__ActionTopScene__

#define __Action__ActionTopScene__


#include "cocos2d.h"


class ActionTopScene :public cocos2d::Layer

{

protected:

    // コンストラクタ

    ActionTopScene();

    // デストラクタ

    virtual ~ActionTopScene();

    // メソッド CREATE_FUNCとの連携

    bool init() override;

    

public:

    static cocos2d::Scene* createScene();

    

    // バックグラウンド

    void Background();

    

    // ボタン表示メソッド

    void buttonBorn();

    

    // ボタンをタッチした時の用のメソッド Object → Ref に変更

    void BtDidPushed(Ref *pSender);

    

    // 回転ラベル表示メソッド

    void rotationLabel();

    

    // アクションToメソッド 指定した数値まで動作する

    void actionTo();

    

    // アクションByメソッド 現在の位置 + 指定した数値まで動作し何度でも動作を繰り返す

    void actionBy();

    

    CREATE_FUNC(ActionTopScene);

    

    // _/_/_/ プロパティー _/_/_/

    // 頻繁に他の場所から参照するためメンバー変数としてシーンクラスに保持させる

    // _label11変数と、getLabel11()メソッド、setLabel11(Label *)メソッドが自動的に実装される

    CC_SYNTHESIZE_RETAIN(cocos2d::Label*, _label11, Label11);

    CC_SYNTHESIZE_RETAIN(cocos2d::Label*, _label12, Label12);

    

};


 

#endif /* defined(__Action__ActionTopScene__) */

 

ActionTopScene.cppを次のように変更してみてください。

#include "ActionTopScene.h"


USING_NS_CC;


// _/_/_/ コンストラクタ プロパティー _/_/_/

ActionTopScene::ActionTopScene()

: _label11(NULL)

, _label12(NULL)

{

    

}


// ActionTopScene デストラクタで解放 メモリーリークを防ぐ

ActionTopScene::~ActionTopScene()

{

    CC_SAFE_RELEASE_NULL(_label11); // _label11releaseしてメモリーリークを防ぎます

    CC_SAFE_RELEASE_NULL(_label12); // _label12releaseしてメモリーリークを防ぎます

}


// createSceneLayerSceneに貼り付けて返すクラスメソッドです。

// 自分自身(ActionTopScene)を生成し、空のSceneに貼り付けて返す簡単な処理を行っているだけです。

// これでほかのシーンからの遷移が楽に行えます。

Scene* ActionTopScene::createScene()

{

    auto scene = Scene::create();

    auto layer = ActionTopScene::create();

    scene->addChild(layer);

    return scene;

}


bool ActionTopScene::init()

{

    if (!Layer::init()) {

        return false;

    }


    // 初期化処理

    

    // バックグラウンド

    Background();

        

    // 回転ラベル表示メソッド

    rotationLabel();

    

    // ラベルボタン表示メソッド

    buttonBorn();

    

    return true;


}


// バックグランド

void ActionTopScene::Background()

{

    

    // 画面サイズを取得

    auto winSize = Director::getInstance()->getVisibleSize();

    

    // バックグランドカラー

    auto background = LayerColor::create(Color4B::BLUE,

                                         winSize.width,

                                         winSize.height);

    // ラベルを生成

    auto label1 = Label::createWithSystemFont("Action 回転 移動 拡大", "Arial", 80);

    

    // ラベルの設置

    label1->setPosition(Vec2(winSize.width / 2 ,winSize.height / 2 + 270));

    

    // ラベルタイトルを追加

    this->addChild(label1,1);

    

    // バックグランドカラー 第2引数は表示順

    this->addChild(background, 0);

}


// 回転ラベル表示メソッド

void ActionTopScene::rotationLabel()

{

    // 画面サイズを取得

    Size winSize = Director::getInstance()->getVisibleSize();

    

    // ラベルを生成

    _label11 = Label::createWithSystemFont("Rotation", "Marker Felt", 60);

    

    // ラベルの設置

    _label11->setPosition(Vec2(winSize.width/4 ,winSize.height/2));

    

    // ラベルタイトルを追加

    this->addChild(_label11,1);

    

    // 回転角度を指定する(setRotation)

    _label11->setRotation(45);

    

    // ラベルを生成

    _label12 = Label::createWithSystemFont("Rotation", "Marker Felt", 60);

    

    // ラベルの設置

    _label12->setPosition(Vec2(winSize.width - winSize.width/4 ,winSize.height/2));

    

    // ラベルタイトルを追加

    this->addChild(_label12,1);

    

    // 回転角度を指定する(setRotation)

    _label12->setRotation(45);

}


// ラベルボタン表示メソッド

void ActionTopScene::buttonBorn()

{

    

    // 画面サイズを取得

    auto winSize = Director::getInstance()->getVisibleSize();

    

    // ラベルを生成

    //Ver3.x CC_CALLBACK_1 マクロ(引数が1つ)にすること

    auto labelBtnLabel01 = Label::createWithSystemFont("(左)Action RotateTo", "Arial", 48);

    // ラベルメニューアクション先の設定

    auto labelItem01 = MenuItemLabel::create(labelBtnLabel01,

                                   CC_CALLBACK_1(ActionTopScene::BtDidPushed, this));

    

    // ラベルメニュー1タグをセット

    labelItem01->setTag(1);

    

    // ラベルを生成

    auto labelBtnLabel02 = Label::createWithSystemFont("(右)Action RotateBy", "Arial", 48);

    // ラベルメニューアクション先の設定

    auto labelItem02 = MenuItemLabel::create(labelBtnLabel02,

                                   CC_CALLBACK_1(ActionTopScene::BtDidPushed, this));

    

    // ラベルメニュー2タグをセット

    labelItem02->setTag(2);

    

    // 左ラベルの設置

    labelItem01->setPosition(Vec2(winSize.width/2 - 300,winSize.height/2 - 260));

    // 右ラベルの設置

    labelItem02->setPosition(Vec2(winSize.width/2 + 300,winSize.height/2 - 260));

    

    

    // メニューを作成 自動解放オブジェクト

    auto menu = Menu::create(labelItem01,labelItem02,NULL);

    

    menu->setPosition(Point::ZERO);

    // メニューを追加

    this->addChild(menu, 1);

}


// ボタンをタッチした時の用のメソッド

void ActionTopScene::BtDidPushed(Ref *pSender)

{

    // pSender menuItemに型変換

    MenuItem* menuItem = (MenuItem*)pSender;

    

    // タグ番号GETで判定

    switch(menuItem->getTag())

    {

        case 1:

            

            CCLOG("RotateTo");

            

            // アクションToメソッド 指定した数値まで動作する

            actionTo();

            break;

            

        case 2:

            

            CCLOG("RotateBy");

            

            // アクションByメソッド 現在の位置 + 指定した数値まで動作し何度でも動作を繰り返す

            actionBy();

            break;

    }

}


// アクションToメソッド 指定した数値まで動作する

void ActionTopScene::actionTo()

{

    // CCActionRotateTo 3 45度に180度を加算した225度まで回転

    auto rotateTo = RotateTo::create(3.0f, 180.0f);

    

    // 「指定した座標」まで移動させます

    auto moveTo = MoveTo::create(3, Vec2(30, _label11->getPositionY()));

    

    //    // 大きさ(拡大)アクションを適用 2 3

    //    auto scaleTo = ScaleTo::create(2.0f, 3.0f);


    //    //「指定した座標」までジャンプしながら移動します

    //    auto jumpTo = JumpTo::create(3, Vec2(200, 10), 60, 5);


    // 回転、移動アクションを適用

    auto sequence = Sequence::create(rotateTo, moveTo, NULL);

    

    // 回転、移動アクションを適用

    _label11->runAction(sequence);


}


// アクションByメソッド 現在の位置 + 指定した数値まで動作し何度でも動作を繰り返す

void ActionTopScene::actionBy()

{

    // CCActionRotateBy 3 45度に180度を加算した225度まで回転

    auto rotateBy = RotateBy::create(3.0f, 180.0f);

    

    // 「指定した座標」まで移動させます

    auto moveBy = MoveBy::create(3, Vec2(30, _label11->getPositionY()));

    

    //    // 大きさ(拡大)アクションを適用 2 3

    //    auto scaleBy = ScaleBy::create(2.0f, 3.0f);


    //    //「指定した座標」までジャンプしながら移動します

    //    auto jumpBy = JumpBy::create(3, Vec2(200, 10), 60, 5);

    

    // 回転、移動

    auto sequence = Sequence::create(rotateBy, moveBy, NULL);

    

    // 回転、移動アクションを適用

    _label12->runAction(sequence);

}

 

GitHub Action_Cocos2d-X


※重要

 

リファレンスカウンタ


Cocos2d-xでの開発では、プログラマが手動でメモリー管理を行っていかなくてはなりません。
そのためには、このリファレンスカウンタの仕組みの理解が不可欠です。

autoreleaseの方法

Cocos2d-xにはクラスメソッドとして定義されている、createから始まる関数が用意されています。

これを使用します。

auto node = Node::create();
auto sprite = Sprite::create("image.png");
auto label1 = Label::createWithSystemFont("Action 回転 移動 拡大", "Arial", 80);

auto node = Node::create(); // カウンタ1(autorelease);
// メイングループ終了時にnodeはreleaseされる。

Cocos2d-xでは、標準で実施されているクラスはこの方法でオブジェクトを生成していくことになります。

 

※重要


一時的に使用する変数はプログラマの解放忘れを防ぐため、autoreleaseされたオブジェクトを使用したほうが良いことを示しました。

メイングループを抜けるタイミングでオブジェクトが削除されると他のところでメンバー変数を参照できなくなってしまいます。

実際のコードでは、プログラマがretainを呼び出すことなく、プロパティマクロを利用します。
メンバー変数をどのタイミングでタイミングすればよいか?
ほとんどの場合は、メンバー変数はクラスのデストラクタでreleaseします。

Cocos2d-xには、Refの解放のための便利なマクロが用意されており、一般的にはそれを使用します。

// デストラクタ ~ActionTopScene()
ActionTopScene::~ActionTopScene()
{

    // _label11がNULLでなければreleaseしてNULLにする

    CC_SAFE_RELEASE_NULL(_label11); // _label11をreleaseしてメモリーリークを防ぎます
    CC_SAFE_RELEASE_NULL(_label12); // _label12をreleaseしてメモリーリークを防ぎます

}

CC_SAFE_RELEASE_NULLは、引数を渡した変数がNULLでなければ、releaseを呼び出した後、変数にNULLを格納するマクロです。このマクロを利用することで、安心してオブジェクトを解放することができます。

 

 

メンバー変数を追加したいときはこのようにプロパティの設定と、初期化構文での初期化、
デストラクタでの解放をワンセットで行ってください

ワンセット

ActionTopScene.h

// 他のクラスからnewを使って生成できないようにするためにprotectedに宣言しています。
protected:

    // コンストラクタ
    ActionTopScene();

    // デストラクタ
    virtual ~ActionTopScene();

// プロパティの設定
// _/_/_/ プロパティー _/_/_/
    // 頻繁に他の場所から参照するためメンバー変数としてシーンクラスに保持させる
    // _label11変数と、get Label11()メソッド、set Label11(Label *)メソッドが自動的に実装される
    CC_SYNTHESIZE_RETAIN(cocos2d::Label*, _label11, Label11);   // ※重要
    CC_SYNTHESIZE_RETAIN(cocos2d::Label*, _label12, Label12);


ActionTopScene.cpp

// コンストラクタ:オブジェクトの生成時に呼び出されるメソッド
ActionTopScene::ActionTopScene()

//  _label11をNULLで初期化
:  _label11(NULL)                         // ※重要
,  _label12(NULL)
{

}

// デストラクタ:オブジェクトが開放(消える)時に呼び出さるメソッド
// ActionTopScene デストラクタで解放 メモリーリークを防ぐ

ActionTopScene::~ActionTopScene()
{
    // デストラクタ CC_SAFE_RELEASE_NULLを使ってメンバー変数をrelease
    // _label11をreleaseしてメモリーリークを防ぎます
    // Objective-C deallocと同じようなものです。

    CC_SAFE_RELEASE_NULL(_label11); // _label11をreleaseしてメモリーリークを防ぎます
    CC_SAFE_RELEASE_NULL(_label12); // _label12をreleaseしてメモリーリークを防ぎます
}

※重要


選択されたラベルボタンの情報をどうpSenderに渡しまたそれをどう次のアクションメソッドの条件判定に結びつけるかがなかなか解説情報が無く見つけるのに苦慮しました。

選択されたラベルボタンlabelItem01をsetTag(1)タグ1番に付けBtDidPushed(Ref *pSender)関数(メソッド)に渡しタグ番号をMenuItemクラスに型変換をしswitch文で条件判定させることで複数のメソッドを作らずシンプルな形でアクションメソッドが実行できました。

// ラベルメニュー1タグをセット
labelItem01->setTag(1);

// pSender menuItemに型変換
MenuItem* menuItem = (MenuItem*)pSender;

// タグ番号GETで判定
switch(menuItem->getTag())


Tag


『タグを設定する』『タグを調べる』『タグを指定して呼び出す』の3つの事ができます。
①タグを設定する
sprite->setTag(1);  //spriteに『1』と言うタグを設定する

②タグを調べる
int tag = sprite->getTag();  //spriteにはどの様なタグが設定されているか調べる

③タグを指定して呼び出す
auto sprite = this->getChildByTag(1);  //タグが『1』と設定してあるデータを呼び出す


ActionTopScene.h ヘッダー部に関して

public(グローバル)変数、関数(メソッド名)を記述します。

Objective-Cでは、メソッドをヘッダー部を記述しないのでこの点が少し違いがあり面倒だと感じC++の文法を味わいました。


プログラムをビルド&実行し表示ボタンを押すと画面に「Hello Cocos2d-x」が表示されると思います。

 

目 次