【cocos2d-x v3.0 beta2】カメラを追尾させてみよう！あれ、使いにくい？

# カメラが自動で追尾する
cocos2dにはFollowという機能がある。
過去VerではCCFollowという名前になっているでしょう。
これは、特定のNodeに対して、画面が付いていくというActionです。
カメラがずっと追尾してくるようなものをイメージしてくれればOK。

実装はこんな感じになります。

``` cpp:TestScene.cpp

this->runAction(Follow::create(cocos2d::Node *followedNode));

```

この一行で画面がfollowedNodeの動きに合わせて付いていきます。
Followのソースを見てみると

``` cpp:CCAction.cpp

void Follow::step(float dt)
{
    CC_UNUSED_PARAM(dt);

    if(_boundarySet)
    {
        // whole map fits inside a single screen, 
        // no need to modify the position - unless map boundaries are increased
        if(_boundaryFullyCovered)
            return;

        Point tempPos = _halfScreenSize - _followedNode->getPosition();

        _target->setPosition(Point(clampf(tempPos.x, _leftBoundary, _rightBoundary),
                                   clampf(tempPos.y, _bottomBoundary, _topBoundary)));
    }
    else
    {
        _target->setPosition(_halfScreenSize - _followedNode->getPosition());
    }
}

```

このように、指定NodeがAnchorPointに来るように画面が付いていくのが分かります。

## boundarySet？

Follow::createする時に引数でrectを指定することが出来ます。
これは、カメラの追尾範囲を決めることが出来るというものです。
注意としては、この追尾範囲が画面サイズより小さかった場合、
追尾する必要がないため追尾しない、という動きになります。

(_boundaryFullyCoveredに引っかかります）

# ピーキー過ぎてお前には無理だよ

このFollow。"遊び"が無く、リアルタイムで追尾するため、
キャラが激しく動くアクションゲームだったりすると
上下左右に激しく揺れるため、目が回ってしまいます。

ちょっと使いにくいですね。
じゃあカスタムしちゃいましょう。

# 拡張してみる

CustomFollowという名前で作成。
通常の追尾機能に加えて、以下の３つの機能を加えます。
・X軸のみ追尾する
・Y軸のみ追尾する
・指定範囲外に出た時のみ追尾する

```cpp:CustomFollow.h

enum CustomFollowType{
    kCustomFollowNone,
    kCustomFollowXOnly,
    kCustomFollowYOnly
};

class CustomFollow : public Follow
{
private:
    CustomFollowType _type;
    Rect _marginRect;
    
    CustomFollow();
    ~CustomFollow();
public:
    static CustomFollow* create(Node *followedNode);
    static CustomFollow* create(Node *followedNode, CustomFollowType type);
    static CustomFollow* create(Node *followedNode, Rect marginRect);
    
    virtual void step(float dt) override;
};

```

```cpp:CustomFollow.cpp

CustomFollow::CustomFollow(){
    _type = kCustomFollowNone;
    _marginRect = Rect::ZERO;
}

CustomFollow::~CustomFollow(){
    CC_SAFE_RELEASE(_followedNode);
}

CustomFollow* CustomFollow::create(Node* followNode){
    CustomFollow *follow = new CustomFollow();
    if (follow && follow->initWithTarget(followNode, Rect::ZERO))
    {
        follow->autorelease();
        return follow;
    }
    CC_SAFE_DELETE(follow);
    return nullptr;
}

CustomFollow* CustomFollow::create(Node* followNode, CustomFollowType type){
    CustomFollow *follow = new CustomFollow();
    follow->_type = type;
    if (follow && follow->initWithTarget(followNode, Rect::ZERO))
    {
        follow->autorelease();
        return follow;
    }
    CC_SAFE_DELETE(follow);
    return nullptr;
}

CustomFollow* CustomFollow::create(Node* followNode, Rect marginRect){
    CustomFollow *follow = new CustomFollow();
    follow->_marginRect = marginRect;
    if (follow && follow->initWithTarget(followNode, Rect::ZERO))
    {
        follow->autorelease();
        return follow;
    }
    CC_SAFE_DELETE(follow);
    return nullptr;
}

void CustomFollow::step(float dt)
{
    CC_UNUSED_PARAM(dt);
    
    if(_boundarySet)
    {
        if(_boundaryFullyCovered)
            return;
        
        Point tempPos = _halfScreenSize - _followedNode->getPosition();

        float x = clampf(tempPos.x, _leftBoundary, _rightBoundary);
        float y = clampf(tempPos.y, _bottomBoundary, _topBoundary);
        
        if(_type == kCustomFollowXOnly){
            y = _target->getPositionY();
        }
        else if (_type == kCustomFollowYOnly){
            x = _target->getPositionX();
        }
        
        _target->setPosition(Point(x , y));

    }
    else
    {
        Point calcPos = _target->convertToWorldSpace(_followedNode->getPosition());

        if(_marginRect.containsPoint(calcPos)){
            return;
        }
        
        float x = _halfScreenSize.x - _followedNode->getPositionX();
        float y = _halfScreenSize.y - _followedNode->getPositionY();
        
        if(_type == kCustomFollowXOnly){
            y = _target->getPositionY();
        }
        else if (_type == kCustomFollowYOnly){
            x = _target->getPositionX();
        }
        
         _target->setPosition(Point(x , y));
    }
}

```

こんな感じ。

# 使ってみる

![スクリーンショット 2014-02-23 19.00.28.png](https://qiita-image-store.s3.amazonaws.com/0/26453/f352a96a-3fb3-b8dd-5759-ee161807a31d.png)

静止画では伝わりませんね、はい。

X軸のみ、Y軸のみはイメージしやすいと思いますが、
指定範囲外に出ると追尾する、というのをちょっと説明。

# 画面外に出ると追尾するようにしてみる

指定はこんな感じ。

```cpp:TestScene.cpp

this->runAction(CustomFollow::create(player , Rect(x, y, visibleSize.width - x , visibleSize.height - y)));

```

これで、画面外に出た時のみカメラが追尾します。

画面外に出ると…
![スクリーンショット 2014-02-23 19.07.04.png](https://qiita-image-store.s3.amazonaws.com/0/26453/a81cab6c-420f-0d54-de87-44c1dbafa899.png)

追尾！
![スクリーンショット 2014-02-23 19.03.37.png](https://qiita-image-store.s3.amazonaws.com/0/26453/2ac4f6c9-4ca0-1553-3c03-6752abbea29a.png)

# あとがき

Follow機能については、まだまだ拡張出来る事が多いと思います。
ゼルダの伝説やロックマンのようにMoveしつつ追いかける。
追いかけた後、画面逆側が出るようにする。
などなど

色々と広がる機能なんですが、そのままではちょっと使いにくい…。
じゃあ拡張しちゃえ！と条件反射でやってみた事を書いてみました。

簡単でしたが、以上。Followのお話でしたー。