2016年6月3日金曜日

バネの動きをするAction

Cocos2d-xでアプリを作っていて結局使わなかったがバネの動きを再現したActionを作ったので公開する。
 libcocos2dのソースの中に組み込んでやれば他のActionと同様に呼び出せるようになる。
ソースの中にdefineしてしまっているが自然長や初速度、摩擦係数などを変更すれば動きをもっと激しくすることも可能。

SpringMoveBy.h
#ifndef SPRINGMOVE_H_
#define SPRINGMOVE_H_

#include "cocos2d.h"

class CC_DLL SpringMoveBy : public cocos2d::ActionInterval
{
public:
    static SpringMoveBy* create(float duration, const cocos2d::Vec2& position, float mass, float k);

    //
    // Overrides
    //
    virtual SpringMoveBy* clone() const override;
    virtual SpringMoveBy* reverse(void) const override;
    virtual void startWithTarget(cocos2d::Node *target) override;
    /**
     * @param time In seconds.
     */
    virtual void update(float time) override;

CC_CONSTRUCTOR_ACCESS:
SpringMoveBy() {}
    virtual ~SpringMoveBy() {}

    /**
     * initializes the action
     * @param duration in seconds
     */
    bool initWithDuration(float duration, const cocos2d::Vec2& position, float mass, float k);

protected:
    cocos2d::Vec2 _startPosition;
    cocos2d::Vec2 _delta;
    float  _k;
    float   _mass;
    cocos2d::Vec2 _previousPos;
    float  _velocity;
    float  _accel;
    float  _position;

private:
    CC_DISALLOW_COPY_AND_ASSIGN(SpringMoveBy);
};

#endif /* SPRINGMOVE_H_ */

SpringMoveBy.cpp
#include "SpringMove.h"

USING_NS_CC;

//
// SpringMoveBy
//

#define GRAVITY 9.80665 // 重力加速度
#define NATURAL_LENGTH 0 // 自然長
#define FIRST_ACCEL GRAVITY // 初期加速度
#define FIRST_VEROCITY 60 // 初期速度
#define FIRST_POSITION 0 // 初期位置
#define FRICTION 0.996 // 摩擦係数

USING_NS_CC;

SpringMoveBy* SpringMoveBy::create(float duration, const Vec2& position, float mass, float k)
{
    SpringMoveBy *springMoveBy = new (std::nothrow) SpringMoveBy();
    springMoveBy->initWithDuration(duration, position, mass, k);
    springMoveBy->autorelease();

    return springMoveBy;
}

bool SpringMoveBy::initWithDuration(float duration, const Vec2& position, float mass, float k)
{
    CCASSERT(mass >=0, "Number of mass must be >= 0");

    if (ActionInterval::initWithDuration(duration) && mass>=0)
    {
        _delta = position;
        _mass = mass;
        _k = k;
        _velocity = FIRST_VEROCITY;
        _accel = FIRST_ACCEL;
        _position = FIRST_POSITION;

        return true;
    }

    return false;
}

SpringMoveBy* SpringMoveBy::clone() const
{
    // no copy constructor
    auto a = new (std::nothrow) SpringMoveBy();
    a->initWithDuration(_duration, _delta, _mass, _k);
    a->autorelease();
    return a;
}

void SpringMoveBy::startWithTarget(Node *target)
{
    ActionInterval::startWithTarget(target);
    _previousPos = _startPosition = target->getPosition();
}

void SpringMoveBy::update(float t) // _delta = 現在座標
{
    if (_target)
    {
     auto force = -_k * ( _position - NATURAL_LENGTH ) + _mass * GRAVITY;
     _accel = force / _mass;
     _velocity += _accel * t;
     _velocity = _velocity * FRICTION;
     _position += _velocity * t;

#ifdef CC_ENABLE_STACKABLE_ACTIONS
        Vec2 currentPos = _target->getPosition();

        Vec2 diff = currentPos - _previousPos;
        _startPosition = diff + _startPosition;

        Vec2 newPos = _startPosition;

        newPos = _startPosition + Vec2(0,_position);

        _target->setPosition(newPos);

        _previousPos = newPos;
#else
        _target->setPosition(_startPosition + Vec2(0, _position));
#endif // !CC_ENABLE_STACKABLE_ACTIONS

    }
}

SpringMoveBy* SpringMoveBy::reverse() const
{
    return SpringMoveBy::create(_duration, Vec2(-_delta.x, -_delta.y),
        _mass, _k);
}

動作例
 

0 件のコメント:

コメントを投稿