Help us understand the problem. What is going on with this article?

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

More than 5 years have passed since last update.

カメラが自動で追尾する

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

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

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

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

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という名前で作成。
通常の追尾機能に加えて、以下の3つの機能を加えます。
・X軸のみ追尾する
・Y軸のみ追尾する
・指定範囲外に出た時のみ追尾する

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;
};

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

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

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

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

指定はこんな感じ。

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

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

画面外に出ると…
スクリーンショット 2014-02-23 19.07.04.png

追尾!
スクリーンショット 2014-02-23 19.03.37.png

あとがき

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

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

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

kuuki_yomenaio
元々サーバサイドを得意としていますが、 クライアントもやります。 ゲーム開発のお仕事をしています。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした