Edited at

cocos2d-x/ScrollViewの慣性の動きが気持ちよくないので自分で制御する。

More than 1 year has passed since last update.

Cocos StudioでScrollViewを配置してみたが、慣性の動きが気持ちよくないので、自分で動かす。

ただ、バウンドはできなくなる。

ソースが美しくないのは悪しからず。。。


ScrollViewController.cpp


#include "ScrollViewController.h"

using namespace cocos2d;

const uint32_t ScrollViewController::HISTORY_FRAME = 10;

ScrollViewController::ScrollViewController():_target(nullptr),_touchNow(false),_deceleration(defaultDeceleration)
{

}
ScrollViewController::~ScrollViewController()
{

}
void ScrollViewController::setTarget(cocos2d::ui::ScrollView* scrollview)
{
_target = scrollview;
// もともとの慣性は無効に
_target->setInertiaScrollEnabled(false);

_target->addTouchEventListener([this](Ref* ref, ui::Widget::TouchEventType type){
switch(type)
{
// タッチ開始時
case ui::Widget::TouchEventType::BEGAN:
_touchNow = true;
_history.clear();
_vec = Vec2::ZERO;
_posPre = _target->getInnerContainer()->getPosition();
break;
// タッチ終了時
case ui::Widget::TouchEventType::CANCELED:
case ui::Widget::TouchEventType::ENDED:
if( _history.size() > 0 ){
for( auto& h : _history ){
_vec += h;
}
_vec.x /= _history.size();
_vec.y /= _history.size();
}
_touchNow = false;
break;
default:
break;
}
});

scheduleUpdate();
}

void ScrollViewController::update(float delta)
{
if( _target == nullptr ){
return;
}
auto innner = _target->getInnerContainer();
// タッチ中は動きの履歴を貯める
if( _touchNow ){
auto now = innner->getPosition();
auto vec = now - _posPre;
_history.push_back(vec);
while(_history.size()>HISTORY_FRAME){
_history.pop_front();
}
_posPre = now;
}
// タッチ中じゃなかったら動かす
else{
auto now = innner->getPosition();
auto pos = now + _vec;
auto szLimit = _target->getInnerContainerSize();
auto szTarget = _target->getContentSize();
szLimit.width -= szTarget.width;
szLimit.height -= szTarget.height;

if( pos.y > 0 ){
pos.y = 0;
}
if( pos.y < -szLimit.height ){
pos.y = -szLimit.height;
}
if( pos.x > 0 ){
pos.x = 0;
}
if( pos.x < -szLimit.width ){
pos.x = -szLimit.width;
}
_vec = pos - now;
innner->setPosition(pos);
_deceleration(_vec);
}
}

void ScrollViewController::defaultDeceleration(cocos2d::Vec2& vec)
{
vec *= 0.95f;
if( fabs(vec.x) < 0.1f && fabs(vec.y) < 0.1f ){
vec = Vec2::ZERO;
}
}



ScrollViewController.h


#ifndef __ScrollViewController_H__
#define __ScrollViewController_H__
#include "cocos2d.h"
#include <ui/UIScrollView.h>
#include <deque>

class ScrollViewController : public cocos2d::Node
{
ScrollViewController();
~ScrollViewController();
public:

CREATE_FUNC(ScrollViewController);
void setTarget(cocos2d::ui::ScrollView* scrollview);
virtual void update(float delta);
void setDeceleration(std::function<void(cocos2d::Vec2& vec)> dec){
_deceleration = dec;
}

private:
cocos2d::ui::ScrollView* _target;
bool _touchNow;
cocos2d::Vec2 _vec;
cocos2d::Vec2 _posPre;
std::deque<cocos2d::Vec2> _history;
static const uint32_t HISTORY_FRAME;
std::function<void(cocos2d::Vec2& vec)> _deceleration;
static void defaultDeceleration(cocos2d::Vec2& vec);

};
#endif


使い方は


FooScene.cpp


auto scrollview = (ui::ScrollView*)_node->getChildByName("scrollview");

auto ctrl = ScrollViewController::create();
ctrl->setTarget(scrollview);
_node->addChild(ctrl);


SceneにaddChild()して参照カウンタの管理できるから、cocos2d::Nodeから継承しているが、特に意味はないです。