-
-
Save stevetranby/6813fbb427fb57a0cc2ed268b8d15ac0 to your computer and use it in GitHub Desktop.
Input Handling for Touch Pan (drag) and Zoom (pinch)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* CCLayerPanZoom | |
* | |
* Copyright (c) 2011 Alexey Lang | |
* Copyright (c) 2011 Pavel Guganov | |
* | |
* http://www.cocos2d-x.org | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
* in the Software without restriction, including without limitation the rights | |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
* copies of the Software, and to permit persons to whom the Software is | |
* furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in | |
* all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
* THE SOFTWARE. | |
* | |
*/ | |
/* | |
* STLayerPanZoom by Steve Tranby | |
* - modified CCLayerPanZoom to support disable and set zoom intervals | |
*/ | |
#pragma once | |
#define kSTLayerPanZoomMultitouchGesturesDetectionDelay (0.1f) | |
#define kZoomScaleAnimationDuration (0.2f) | |
typedef enum | |
{ | |
/** Standard mode: swipe to scroll */ | |
kSTLayerPanZoomModeSheet, | |
/** Frame mode (i.e. drag inside objects): hold finger at edge of the screen to the sroll in this direction */ | |
kSTLayerPanZoomModeFrame | |
} STLayerPanZoomMode; | |
// https://raw.github.com/gameslovin/cocos2dx_extension/master/Extensions/CCLayerPanZoom/CCLayerPanZoom.h | |
typedef enum | |
{ | |
kCCLayerPanZoomFrameEdgeNone, | |
kCCLayerPanZoomFrameEdgeTop, | |
kCCLayerPanZoomFrameEdgeBottom, | |
kCCLayerPanZoomFrameEdgeLeft, | |
kCCLayerPanZoomFrameEdgeRight, | |
kCCLayerPanZoomFrameEdgeTopLeft, | |
kCCLayerPanZoomFrameEdgeBottomLeft, | |
kCCLayerPanZoomFrameEdgeTopRight, | |
kCCLayerPanZoomFrameEdgeBottomRight | |
} CCLayerPanZoomFrameEdge; | |
// forward decl | |
class STLayerPanZoom; | |
class GameLayer; | |
class STLayerPanZoomDelegate | |
{ | |
public: | |
virtual ~STLayerPanZoomDelegate(); | |
virtual bool layerPanZoomClickedAtPoint(const cocos2d::Vec2& touchPosition, int touchCount, bool shortCircuited = false) = 0; | |
virtual bool layerPanZoomTouchBegan(STLayerPanZoom* layerPanZoom, const cocos2d::Vec2& touchPositionInLayer) = 0; | |
virtual bool layerPanZoomTouchMoved(STLayerPanZoom* layerPanZoom, const cocos2d::Vec2& touchPositionInLayer) = 0; | |
virtual bool layerPanZoomTouchEnded(STLayerPanZoom* layerPanZoom, const cocos2d::Vec2& touchPositionInLayer) = 0; | |
virtual bool layerPanZoomMouseBegan(STLayerPanZoom* layerPanZoom, const cocos2d::Vec2& screenPositionInLayer) = 0; | |
virtual bool layerPanZoomMouseMoved(STLayerPanZoom* layerPanZoom, const cocos2d::Vec2& screenPositionInLayer) = 0; | |
virtual bool layerPanZoomMouseEnded(STLayerPanZoom* layerPanZoom, const cocos2d::Vec2& screenPositionInLayer) = 0; | |
virtual bool layerPanZoomMouseMovedOver(STLayerPanZoom* layerPanZoom, const cocos2d::Vec2& screenPosition) = 0; | |
/// return false to prevent mouse edge scrolling or wheel zooming | |
virtual bool layerPanZoomCanMouseEdgePanOrScrollZoom(STLayerPanZoom* /*layerPanZoom*/) { return true; } | |
}; | |
/** class STLayerPanZoom Class that represents the layer that can be scrolled | |
* and zoomed with one or two fingers. */ | |
class STLayerPanZoom : public cocos2d::Layer | |
{ | |
public: | |
STLayerPanZoom(); | |
virtual ~STLayerPanZoom(); | |
virtual bool init(); | |
static STLayerPanZoom* layer(); | |
CREATE_FUNC(STLayerPanZoom); | |
///////////////////////////////////////////////////////////// | |
// STEVE | |
/** Delegate for callbacks. */ | |
int _tapCount; | |
bool _tapEnabled; | |
cocos2d::Vec2 _tapBeganPosition; | |
cocos2d::Vec2 _tapPosition; | |
cocos2d::Rect _worldBounds; | |
CC_SYNTHESIZE(STLayerPanZoomDelegate*, _delegate, Delegate); | |
CC_SYNTHESIZE_BOOL(_isDoubleTapAllowed, DoubleTapAllowed); | |
///////////////////////////////////////////////////////////// | |
// TODO: remove getters | |
// TODO: maybe remove setters?? | |
protected: | |
bool _isDisabled; | |
bool _inputDisabled; | |
bool _panDisabled; | |
bool _zoomDisabled; | |
public: | |
virtual bool isDisabled(void) const; | |
virtual void setDisabled(bool var); | |
virtual bool isInputDisabled(void) const; | |
virtual void setInputDisabled(bool var); | |
virtual bool isPanDisabled(void) const; | |
virtual void setPanDisabled(bool var); | |
virtual bool isZoomDisabled(void) const; | |
virtual void setZoomDisabled(bool var); | |
///////////////////////////////////////////////////////////// | |
void resetTouches(); | |
void tapWaitFinished(); | |
void tapHandler(); | |
void setWorldBounds(cocos2d::Rect rect); | |
///////////////////////////////////////////////////////////// | |
void setMaxScale(float maxScale); | |
float maxScale(); | |
void setMinScale(float minScale); | |
float minScale(); | |
void setRubberEffectRatio(float rubberEffectRatio); | |
float rubberEffectRatio(); | |
// TODO: add delegate | |
CC_SYNTHESIZE(float, _maxTouchDistanceToClick, MaxTouchDistanceToClick); | |
CC_SYNTHESIZE_PASS_BY_REF(cocos2d::Vector<cocos2d::Touch*>, _touches, Touches); | |
CC_SYNTHESIZE(float, _touchDistance, TouchDistance); | |
CC_SYNTHESIZE(float, _minSpeed, MinSpeed); | |
CC_SYNTHESIZE(float, _maxSpeed, MaxSpeed); | |
CC_SYNTHESIZE(float, _topFrameMargin, TopFrameMargin); | |
CC_SYNTHESIZE(float, _bottomFrameMargin, BottomFrameMargin); | |
CC_SYNTHESIZE(float, _leftFrameMargin, LeftFrameMargin); | |
CC_SYNTHESIZE(float, _rightFrameMargin, RightFrameMargin); | |
//CC_SYNTHESIZE(CCScheduler*, _scheduler, Scheduler); | |
CC_SYNTHESIZE(float, _rubberEffectRecoveryTime, RubberEffectRecoveryTime); | |
cocos2d::Rect _panBoundsRect; | |
float _maxScale; | |
float _minScale; | |
STLayerPanZoomMode _mode; | |
// previous position in layer if single touch was moved. | |
cocos2d::Vec2 _prevSingleTouchPositionInLayer; | |
// Time when single touch has began, used to wait for possible multitouch | |
// gestures before reacting to single touch. | |
time_t _singleTouchTimestamp; | |
// Flag used to call touchMoveBeganAtPosition: only once for each single touch event. | |
bool _touchMoveBegan; | |
float _rubberEffectRatio; | |
bool _rubberEffectRecovering; | |
bool _rubberEffectZooming; | |
// Updates position in frame mode. | |
virtual void update(float dt); | |
void onEnter(); | |
void onExit(); | |
//Scale and Position related | |
void setPanBoundsRect(cocos2d::Rect rect); | |
virtual void setPosition(const cocos2d::Vec2 &position); | |
virtual void setScale(float scale); | |
//Ruber Edges related | |
void recoverPositionAndScale(); | |
void recoverEnded(); | |
//Helpers | |
float topEdgeDistance(); | |
float leftEdgeDistance(); | |
float bottomEdgeDistance(); | |
float rightEdgeDistance(); | |
float minPossibleScale(); | |
CCLayerPanZoomFrameEdge frameEdgeWithPoint(const cocos2d::Vec2& point); | |
float horSpeedWithPosition(const cocos2d::Vec2& pos); | |
float vertSpeedWithPosition(const cocos2d::Vec2& pos); | |
// NEW STUFF (Mouse & Desktop) | |
public: | |
void setupInput(); | |
void setupMouseInput(); | |
void setupTouchInputInternal(); | |
void setupKeyboardInput(); | |
void setZoomLevels(std::vector<float> zoomLevels) { | |
dlog("enter"); | |
_zoomLevelsNew = zoomLevels; | |
setMinScale(_zoomLevelsNew.front()); | |
setMaxScale(_zoomLevelsNew.back()); | |
dlog("before sorting [%f - %f]", _minScale, _maxScale); | |
for(auto zoom : _zoomLevelsNew) { | |
dlog("zoom: %f", zoom); | |
} | |
std::sort(_zoomLevelsNew.begin(), _zoomLevelsNew.end()); | |
setMinScale(_zoomLevelsNew.front()); | |
setMaxScale(_zoomLevelsNew.back()); | |
dlog("after sorting [%f - %f]", _minScale, _maxScale); | |
for(auto zoom : _zoomLevelsNew) { | |
dlog("zoom: %f", zoom); | |
} | |
} | |
private: | |
int _zoomIndex; | |
std::vector<float> _zoomLevelsNew; | |
cocos2d::Vec2 _mouseBeganPosition; | |
// unsure if necessary as 2nd pos tracking | |
cocos2d::Vec2 _mouseBeganPositionRightDrag; | |
cocos2d::Vec2 _mouseBeganPositionRightMouseLocation; | |
float _lastMouseScrollVelocity; | |
bool _isMouseDown; | |
bool _isMouseScrolling; | |
float _zoomWaitTimer; | |
float _zoomWaitTimerDampen; | |
std::map<cocos2d::EventKeyboard::KeyCode, bool> _keyState; | |
std::map<cocos2d::EventKeyboard::KeyCode, cocos2d::Vec2> _cameraInputsArrows; | |
std::map<cocos2d::EventKeyboard::KeyCode, cocos2d::Vec2> _cameraInputs; | |
public: | |
//////////////////////////////////////////////////////////////////////////// | |
// camera pan/zoom action (without Simulation Time, _timeScale affecting) | |
void updateCameraAction(float dt); | |
void runCameraAction(float delay, float duration, const cocos2d::Vec2& newPos, float newScale, float easingRate, const std::function<void()>& callback = nullptr); | |
int _stage; | |
float _targetDelay; | |
float _easeRate; | |
float _elapsed; | |
float _targetTime; | |
std::function<void()> _targetCallback; | |
cocos2d::Vec2 _startPosition; // do we need? | |
cocos2d::Vec2 _previousPosition; | |
cocos2d::Vec2 _targetPosition; | |
cocos2d::Vec2 _deltaPosition; | |
float _targetScale; | |
float _previousScale; | |
float _startScaleX; | |
float _startScaleY; | |
float _startScaleZ; | |
float _endScaleX; | |
float _endScaleY; | |
float _endScaleZ; | |
float _deltaScaleX; | |
float _deltaScaleY; | |
float _deltaScaleZ; | |
iRect _tileCameraBounds; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* CCLayerPanZoom | |
* | |
* Copyright (c) 2011 Alexey Lang | |
* Copyright (c) 2011 Pavel Guganov | |
* | |
* http://www.cocos2d-x.org | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
* in the Software without restriction, including without limitation the rights | |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
* copies of the Software, and to permit persons to whom the Software is | |
* furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in | |
* all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
* THE SOFTWARE. | |
* | |
*/ | |
#include "extensions/STLayerPanZoom.h" | |
#include "managers/CameraManager.h" | |
USING_NS_CC; | |
//////////////////////////////////////////////////// | |
STLayerPanZoomDelegate::~STLayerPanZoomDelegate() {} | |
STLayerPanZoom::STLayerPanZoom() | |
: _tapCount(0) | |
, _tapEnabled(false) | |
, _tapBeganPosition(Vec2::ZERO) | |
, _tapPosition(Vec2::ZERO) | |
, _worldBounds(cocos2d::Rect::ZERO) | |
, _delegate(nullptr) | |
, _isDoubleTapAllowed(false) | |
, _isDisabled(false) | |
, _inputDisabled(false) | |
, _panDisabled(false) | |
, _zoomDisabled(false) | |
, _maxTouchDistanceToClick(0.f) | |
, _touchDistance(0.f) | |
, _minSpeed(0.f) | |
, _maxSpeed(0.f) | |
, _topFrameMargin(0.f) | |
, _bottomFrameMargin(0.f) | |
, _leftFrameMargin(0.f) | |
, _rightFrameMargin(0.f) | |
, _rubberEffectRecoveryTime(0.f) | |
, _panBoundsRect(cocos2d::Rect::ZERO) | |
, _maxScale(1.f) | |
, _minScale(0.2f) | |
, _mode(kSTLayerPanZoomModeSheet) | |
, _prevSingleTouchPositionInLayer(Vec2::ZERO) | |
, _singleTouchTimestamp(0) | |
, _touchMoveBegan(false) | |
, _rubberEffectRatio(0.f) | |
, _rubberEffectRecovering(false) | |
, _rubberEffectZooming(false) | |
, _zoomIndex(3) | |
//, maxZoom(0) | |
//, minZoom(0) | |
, _mouseBeganPosition(Vec2::ZERO) | |
, _lastMouseScrollVelocity(0) | |
, _isMouseDown(false) | |
, _isMouseScrolling(false) | |
, _zoomWaitTimer(0) | |
, _zoomWaitTimerDampen(0) | |
{ | |
setZoomLevels({ | |
.0625f, | |
.125f, | |
.25f, | |
.5f, | |
.75f, | |
1.f, | |
1.5f, | |
2.f, | |
// 5.f, | |
// 10.f, TODO: too close at 736x320 | |
}); | |
_stage = -1; | |
_targetDelay = 0.f; | |
_targetScale = 1.f; | |
_targetPosition = Vec2::ZERO; | |
_startPosition = Vec2::ZERO; | |
_startScaleX = 0; | |
_startScaleY = 0; | |
_startScaleZ = 0; | |
_deltaScaleX = 0; | |
_deltaScaleY = 0; | |
_deltaScaleZ = 0; | |
_easeRate = 0.f; | |
_elapsed = 0.f; | |
} | |
STLayerPanZoom::~STLayerPanZoom() | |
{ | |
// CC_SAFE_RELEASE(_touches); | |
} | |
////////// | |
bool STLayerPanZoom::isDisabled(void) const { return _isDisabled; } | |
void STLayerPanZoom::setDisabled(bool var) | |
{ | |
_isDisabled = var; | |
dlog("setting panzoom disabled = %d", var); | |
} | |
bool STLayerPanZoom::isInputDisabled(void) const { return _inputDisabled; } | |
void STLayerPanZoom::setInputDisabled(bool var) | |
{ | |
if(var) | |
dlog("stupid!"); | |
_inputDisabled = var; | |
dlog("setting input disabled = %d", var); | |
} | |
bool STLayerPanZoom::isPanDisabled(void) const { return _panDisabled; } | |
void STLayerPanZoom::setPanDisabled(bool var) | |
{ | |
_panDisabled = var; | |
dlog("setting pan disabled = %d", var); | |
} | |
bool STLayerPanZoom::isZoomDisabled(void) const { return _zoomDisabled; } | |
void STLayerPanZoom::setZoomDisabled(bool var) | |
{ | |
_zoomDisabled = var; | |
dlog("setting zoom disabled = %d", var); | |
} | |
/////////// | |
void STLayerPanZoom::setMaxScale(float maxScale) | |
{ | |
_maxScale = maxScale; | |
setScale(MIN(getScaleX(), _maxScale)); | |
} | |
float STLayerPanZoom::maxScale() | |
{ | |
return _maxScale; | |
} | |
void STLayerPanZoom::setMinScale(float minScale) | |
{ | |
_minScale = minScale; | |
setScale(MAX(getScaleX(), minScale)); | |
} | |
float STLayerPanZoom::minScale() | |
{ | |
return _minScale; | |
} | |
void STLayerPanZoom::setRubberEffectRatio(float rubberEffectRatio) | |
{ | |
_rubberEffectRatio = rubberEffectRatio; | |
// Avoid turning rubber effect On in frame mode. | |
if (_mode == kSTLayerPanZoomModeFrame) | |
{ | |
CCLOGERROR("STLayerPanZoom#setRubberEffectRatio: rubber effect is not supported in frame mode."); | |
_rubberEffectRatio = 0.f; | |
} | |
} | |
float STLayerPanZoom::rubberEffectRatio() | |
{ | |
return _rubberEffectRatio; | |
} | |
STLayerPanZoom* STLayerPanZoom::layer() | |
{ | |
STLayerPanZoom *layer = STLayerPanZoom::create(); | |
return layer; | |
} | |
// MARK: Init - | |
// on "init" you need to initialize your instance | |
bool STLayerPanZoom::init() | |
{ | |
if ( ! Layer::init() ) { | |
return false; | |
} | |
setIgnoreAnchorPointForPosition(true); | |
_maxScale = 2.f * SCALE_LARGE; | |
_minScale = 0.1f * SCALE_LARGE; | |
_worldBounds = cocos2d::Rect::ZERO; | |
_panBoundsRect = cocos2d::Rect::ZERO; | |
_touchDistance = 0.f; | |
_maxTouchDistanceToClick = 15.f; | |
_mode = kSTLayerPanZoomModeSheet; | |
_minSpeed = 100.f; | |
_maxSpeed = 1000.f; | |
_topFrameMargin = 100.f; | |
_bottomFrameMargin = 100.f; | |
_leftFrameMargin = 100.f; | |
_rightFrameMargin = 100.f; | |
_rubberEffectRatio = 0.5f; | |
_rubberEffectRecoveryTime = 0.2f; | |
_rubberEffectRecovering = false; | |
_rubberEffectZooming = false; | |
return true; | |
} | |
// MARK: Steve Additions - | |
void STLayerPanZoom::setWorldBounds(cocos2d::Rect rect) | |
{ | |
_worldBounds = rect; | |
} | |
void STLayerPanZoom::resetTouches() | |
{ | |
_touches.clear(); | |
_tapCount = 0; | |
} | |
// MARK: Update - | |
// Updates position in frame mode. | |
void STLayerPanZoom::update(float dt) | |
{ | |
if(isInputDisabled()) { | |
updateCameraAction(dt); | |
CCASSERT(getScheduler()->getTimeScale() > 0, "timescale IS ZERO!"); | |
return; | |
} | |
#if ((CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX)) | |
// Mouse Pan Support (Desktop) | |
if(_delegate && _delegate->layerPanZoomCanMouseEdgePanOrScrollZoom(this)) | |
{ | |
// TODO: ONLY EXECUTE | |
if(_zoomWaitTimer > 0) { | |
_zoomWaitTimer -= dt; | |
//dlog("[update] _zoomWaitTimer = %f", _zoomWaitTimer); | |
// if(_zoomWaitTimer <= 0) { | |
// _zoomWaitTimer = 0; | |
// } | |
} | |
// handle near edge movement | |
// TODO: don't apply camera scroll if camera keyboard input is active | |
bool edgeScrollEnabled = false; | |
if(edgeScrollEnabled) | |
{ | |
auto ws = Director::getInstance()->getWinSize(); | |
auto screenRect = cocos2d::Rect(0, 0, ws.width, ws.height); | |
auto p = _mouseBeganPosition; | |
const float scrollEdgeMargin = 35.f * SCALE_LARGE; | |
if(screenRect.containsPoint(p)) { | |
Vec2 moveBy = Vec2::ZERO; | |
float left = scrollEdgeMargin; | |
float bottom = scrollEdgeMargin; | |
float top = ws.height - scrollEdgeMargin; | |
float right = ws.width - scrollEdgeMargin; | |
// make sure to not include CORNERS (or rather hopefully HUD buttons are checked for first) | |
if(p.x < left && p.y > bottom && p.y < top) { moveBy.x = -1; } | |
if(p.x > right && p.y > bottom && p.y < top) { moveBy.x = 1; } | |
if(p.y < bottom && p.x > left && p.x < right) { moveBy.y = -1; } | |
if(p.y > top && p.x > left && p.x < right) { moveBy.y = 1; } | |
auto unitPerSec = 1 * SCALE_LARGE; | |
auto speed = unitPerSec * (60 * dt); | |
CameraManager::get()->moveBy(moveBy * speed); | |
} | |
} | |
} | |
#endif // ifdef desktop | |
updateCameraAction(dt); | |
CCASSERT(getScheduler()->getTimeScale() > 0, "timescale IS ZERO!"); | |
#if ((CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX)) | |
// Keyboard Pan Support (Desktop) | |
// TODO: may want to set flag that any input is available or none | |
if(! isPanDisabled()) { | |
float timeFactor = dt * (.5f / getScale()) * (1.f / getScheduler()->getTimeScale()); | |
for(auto pair : _cameraInputs) | |
{ | |
auto keycode = pair.first; | |
auto it = _keyState.find(keycode); | |
if(it != _keyState.end()) { | |
bool isPressed = it->second; | |
if(isPressed) { | |
auto velocity = pair.second * timeFactor; | |
CameraManager::get()->moveBy(velocity, false); | |
} | |
} | |
} | |
} | |
// don't allow pan with arrow keys when menu is displayed | |
if(GameManager::get()->getCurrentGameState() != GameStateType::CommandMenu) | |
{ | |
if(! isPanDisabled()) { | |
float timeFactor = dt * (.5f / getScale()) * (1.f / getScheduler()->getTimeScale()); | |
for(auto pair : _cameraInputsArrows) | |
{ | |
auto keycode = pair.first; | |
auto it = _keyState.find(keycode); | |
if(it != _keyState.end()) { | |
bool isPressed = it->second; | |
if(isPressed) { | |
auto velocity = pair.second * timeFactor; | |
CameraManager::get()->moveBy(velocity, false); | |
} | |
} | |
} | |
} | |
} | |
#endif | |
// Only for frame mode with one touch. | |
if ( _mode == kSTLayerPanZoomModeFrame && _touches.size() == 1 ) | |
{ | |
CCASSERT(false, "DIDNT THINK THIS CODE WAS REACHABLE"); | |
// Do not update position if click is still possible. | |
if (_touchDistance <= _maxTouchDistanceToClick) { | |
return; | |
} | |
// Do not update position if pinch is still possible. | |
time_t seconds; | |
seconds = time(nullptr); | |
time_t delta = seconds - _singleTouchTimestamp; | |
if (delta > 0 && delta < kSTLayerPanZoomMultitouchGesturesDetectionDelay) { | |
return; | |
} | |
// Otherwise - update touch position. Get current position of touch. | |
Touch* touch = (Touch*)_touches.at(0); | |
Vec2 curPos = Director::getInstance()->convertToGL(touch->getLocationInView()); | |
// Scroll if finger in the scroll area near edge. | |
if (frameEdgeWithPoint(curPos) != kCCLayerPanZoomFrameEdgeNone) | |
{ | |
float x = getPosition().x + dt * horSpeedWithPosition(curPos); | |
float y = getPosition().y + dt * vertSpeedWithPosition(curPos); | |
setPosition(Vec2(x,y)); | |
} | |
// Inform delegate if touch position in layer was changed due to finger or layer movement. | |
Vec2 touchPositionInLayer = convertToNodeSpace(curPos); | |
if ( ! _prevSingleTouchPositionInLayer.equals(touchPositionInLayer)) | |
{ | |
_prevSingleTouchPositionInLayer = touchPositionInLayer; | |
_delegate && _delegate->layerPanZoomTouchMoved(this, touchPositionInLayer); | |
} | |
} | |
} | |
void STLayerPanZoom::onEnter() | |
{ | |
Layer::onEnter(); | |
scheduleUpdate(); | |
setupInput(); | |
} | |
void STLayerPanZoom::onExit() | |
{ | |
getEventDispatcher()->removeEventListenersForTarget(this); | |
unscheduleUpdate(); | |
Layer::onExit(); | |
} | |
// MARK: Scale and Position related - | |
void STLayerPanZoom::setPanBoundsRect(cocos2d::Rect rect) | |
{ | |
_panBoundsRect = rect; | |
setScale(minPossibleScale()); | |
setPosition(getPosition()); | |
} | |
Vec2 nearestPointInPerimiter(cocos2d::Rect r, Vec2 p) | |
{ | |
//local function getNearestPointInPerimeter(l,t,w,h, x,y) | |
float left = r.origin.x; | |
float right = left + r.size.width; | |
float bottom = r.origin.y; | |
float top = bottom + r.size.height; | |
float x = clampf(p.x, left, right); | |
float y = clampf(p.y, bottom, top); | |
float dl = fabsf(x - left); | |
float dr = fabsf(x - right); | |
float db = fabsf(y - bottom); | |
float dt = fabsf(y - top); | |
auto min4 = [](float a, float b, float c, float d) { | |
return MIN(a, MIN(b, MIN(c, d))); | |
}; | |
float m = min4(dl, dr, dt, db); | |
if(m == db) return Vec2(x, bottom); | |
if(m == dt) return Vec2(x, top); | |
if(m == dl) return Vec2(left, y); | |
if(m == dr) return Vec2(right, y); | |
return Vec2(x,y); | |
} | |
void STLayerPanZoom::setPosition(const cocos2d::Vec2 &position) | |
{ | |
if(isPanDisabled()) | |
return; | |
// TODO: allow disabling bounds check (without resetting) for cutscenes | |
// this->position is negative the world coord (w/scale factored in) | |
Vec2 prevPosition = getPosition(); | |
Layer::setPosition(position); | |
Vec2 newPos = position; | |
// TODO: make this a game options setting | |
//auto a = SCGlobals::DebugMode && SCGlobals::get("Enable Camera Boundary"); | |
if (! _worldBounds.equals(cocos2d::Rect::ZERO)) | |
{ | |
// TODO: affect by scale | |
Vec2 worldCoord = CameraManager::get()->currentWorldCoord(); | |
if(! _worldBounds.containsPoint(worldCoord)) | |
{ | |
Vec2 newWorld = nearestPointInPerimiter(_worldBounds, worldCoord); | |
float scale = getScaleX(); | |
newPos = CameraManager::get()->worldToScreen(newWorld, scale); | |
} | |
//dlog("scale = %f, old = %s, new = %s, world = %s", getScale(), CStrFromPoint(prevPosition), CStrFromPoint(newPos), CStrFromPoint(worldCoord)); | |
Node::setPosition(newPos); | |
} | |
else | |
{ | |
if ( ! _panBoundsRect.equals(cocos2d::Rect::ZERO) && !_rubberEffectZooming) | |
{ | |
if (_rubberEffectRatio != 0.f && _mode == kSTLayerPanZoomModeSheet) | |
{ | |
if (!_rubberEffectRecovering) | |
{ | |
float topEdgeDist = topEdgeDistance(); | |
float bottomEdgeDist = bottomEdgeDistance(); | |
float leftEdgeDist = leftEdgeDistance(); | |
float rightEdgeDist = rightEdgeDistance(); | |
float dx = getPosition().x - prevPosition.x; | |
float dy = getPosition().y - prevPosition.y; | |
dlog("%f,%f,%f,%f => dx = %f, dy = %f", topEdgeDist, bottomEdgeDist, leftEdgeDist, rightEdgeDist, dx, dy); | |
if (bottomEdgeDist != 0.f || topEdgeDist != 0.f) | |
{ | |
Node::setPosition(Vec2(getPosition().x, | |
prevPosition.y + dy * _rubberEffectRatio)); | |
} | |
if (leftEdgeDist != 0.f || rightEdgeDist != 0.f) | |
{ | |
Node::setPosition(Vec2(prevPosition.x + dx * _rubberEffectRatio, | |
getPosition().y)); | |
} | |
} | |
} | |
else | |
{ | |
cocos2d::Rect boundBox = getBoundingBox(); | |
dlog("bbox = %s", CStrFromRect(boundBox)); | |
if (getPosition().x - boundBox.size.width * getAnchorPoint().x > _panBoundsRect.origin.x) | |
{ | |
Node::setPosition(Vec2(boundBox.size.width * getAnchorPoint().x + _panBoundsRect.origin.x, | |
getPosition().y)); | |
} | |
if (getPosition().y - boundBox.size.height * getAnchorPoint().y > _panBoundsRect.origin.y) | |
{ | |
Node::setPosition(Vec2(getPosition().x, boundBox.size.height * getAnchorPoint().y + | |
_panBoundsRect.origin.y)); | |
} | |
if (getPosition().x + boundBox.size.width * (1 - getAnchorPoint().x) < _panBoundsRect.size.width + | |
_panBoundsRect.origin.x) | |
{ | |
Node::setPosition(Vec2(_panBoundsRect.size.width + _panBoundsRect.origin.x - | |
boundBox.size.width * (1 - getAnchorPoint().x), getPosition().y)); | |
} | |
if (getPosition().y + boundBox.size.height * (1 - getAnchorPoint().y) < _panBoundsRect.size.height + | |
_panBoundsRect.origin.y) | |
{ | |
Node::setPosition(Vec2(getPosition().x, _panBoundsRect.size.height + _panBoundsRect.origin.y - | |
boundBox.size.height * (1 - getAnchorPoint().y))); | |
} | |
} | |
} | |
Node::setPosition(Vec2(ceilf(getPosition().x), | |
ceilf(getPosition().y))); | |
} | |
} | |
void STLayerPanZoom::setScale(float scale) | |
{ | |
if(isZoomDisabled() || isPanDisabled()) | |
return; | |
scale = MAX(scale, _minScale); | |
scale = MIN(scale, _maxScale); | |
// CCLOG("scale = %f [%f,%f]", scale, _minScale, _maxScale); | |
Layer::setScale(scale); | |
} | |
// MARK: Ruber Edges related - | |
void STLayerPanZoom::recoverPositionAndScale() | |
{ | |
if ( ! _panBoundsRect.equals(cocos2d::Rect::ZERO)) | |
{ | |
cocos2d::Size winSize = Director::getInstance()->getWinSize(); | |
float rightEdgeDist = rightEdgeDistance(); | |
float leftEdgeDist = leftEdgeDistance(); | |
float topEdgeDist = topEdgeDistance(); | |
float bottomEdgeDist = bottomEdgeDistance(); | |
float scale = minPossibleScale(); | |
if (rightEdgeDist == 0.f && leftEdgeDist == 0.f && topEdgeDist == 0.f && bottomEdgeDist == 0.f) | |
{ | |
return; | |
} | |
if (getScaleX() < scale) | |
{ | |
_rubberEffectRecovering = true; | |
Vec2 newPosition = Vec2::ZERO; | |
if (rightEdgeDist != 0.f && leftEdgeDist != 0.f && topEdgeDist != 0.f && bottomEdgeDist != 0.f) | |
{ | |
float dx = scale * getContentSize().width * (getAnchorPoint().x - 0.5f); | |
float dy = scale * getContentSize().height * (getAnchorPoint().y - 0.5f); | |
newPosition = Vec2(winSize.width * 0.5f + dx, winSize.height * 0.5f + dy); | |
} | |
else if (rightEdgeDist != 0.f && leftEdgeDist != 0.f && topEdgeDist != 0.f) | |
{ | |
float dx = scale * getContentSize().width * (getAnchorPoint().x - 0.5f); | |
float dy = scale * getContentSize().height * (1.f - getAnchorPoint().y); | |
newPosition = Vec2(winSize.width * 0.5f + dx, winSize.height - dy); | |
} | |
else if (rightEdgeDist != 0.f && leftEdgeDist != 0.f && bottomEdgeDist != 0.f) | |
{ | |
float dx = scale * getContentSize().width * (getAnchorPoint().x - 0.5f); | |
float dy = scale * getContentSize().height * getAnchorPoint().y; | |
newPosition = Vec2(winSize.width * 0.5f + dx, dy); | |
} | |
else if (rightEdgeDist != 0.f && topEdgeDist != 0.f && bottomEdgeDist != 0.f) | |
{ | |
float dx = scale * getContentSize().width * (1.f - getAnchorPoint().x); | |
float dy = scale * getContentSize().height * (getAnchorPoint().y - 0.5f); | |
newPosition = Vec2(winSize.width - dx, winSize.height * 0.5f + dy); | |
} | |
else if (leftEdgeDist != 0.f && topEdgeDist != 0.f && bottomEdgeDist != 0.f) | |
{ | |
float dx = scale * getContentSize().width * getAnchorPoint().x; | |
float dy = scale * getContentSize().height * (getAnchorPoint().y - 0.5f); | |
newPosition = Vec2(dx, winSize.height * 0.5f + dy); | |
} | |
else if (leftEdgeDist != 0.f && topEdgeDist != 0.f) | |
{ | |
float dx = scale * getContentSize().width * getAnchorPoint().x; | |
float dy = scale * getContentSize().height * (1.f - getAnchorPoint().y); | |
newPosition = Vec2(dx, winSize.height - dy); | |
} | |
else if (leftEdgeDist != 0.f && bottomEdgeDist != 0.f) | |
{ | |
float dx = scale * getContentSize().width * getAnchorPoint().x; | |
float dy = scale * getContentSize().height * getAnchorPoint().y; | |
newPosition = Vec2(dx, dy); | |
} | |
else if (rightEdgeDist != 0.f && topEdgeDist != 0.f) | |
{ | |
float dx = scale * getContentSize().width * (1.f - getAnchorPoint().x); | |
float dy = scale * getContentSize().height * (1.f - getAnchorPoint().y); | |
newPosition = Vec2(winSize.width - dx, winSize.height - dy); | |
} | |
else if (rightEdgeDist != 0.f && bottomEdgeDist != 0.f) | |
{ | |
float dx = scale * getContentSize().width * (1.f - getAnchorPoint().x); | |
float dy = scale * getContentSize().height * getAnchorPoint().y; | |
newPosition = Vec2(winSize.width - dx, dy); | |
} | |
else if (topEdgeDist != 0.f || bottomEdgeDist != 0.f) | |
{ | |
float dy = scale * getContentSize().height * (getAnchorPoint().y - 0.5f); | |
newPosition = Vec2(getPosition().x, winSize.height * 0.5f + dy); | |
} | |
else if (leftEdgeDist != 0.f || rightEdgeDist != 0.f) | |
{ | |
float dx = scale * getContentSize().width * (getAnchorPoint().x - 0.5f); | |
newPosition = Vec2(winSize.width * 0.5f + dx, getPosition().y); | |
} | |
auto moveToPosition = MoveTo::create( _rubberEffectRecoveryTime,newPosition); | |
auto scaleToPosition = ScaleTo::create( _rubberEffectRecoveryTime, scale, scale); | |
auto callback = CallFunc::create([this]() { | |
recoverEnded(); | |
}); | |
auto spawn = Spawn::create(scaleToPosition, moveToPosition, callback, nullptr); | |
runAction(spawn); | |
} | |
else | |
{ | |
_rubberEffectRecovering = false; | |
auto delta = Vec2(getPosition().x + rightEdgeDist - leftEdgeDist, | |
getPosition().y + topEdgeDist - bottomEdgeDist); | |
auto callback = CallFunc::create([this]() { | |
recoverEnded(); | |
}); | |
auto moveToPosition = MoveTo::create(_rubberEffectRecoveryTime, delta); | |
auto seq = Spawn::create(moveToPosition, callback, nullptr); | |
runAction(seq); | |
} | |
} | |
} | |
void STLayerPanZoom::recoverEnded() | |
{ | |
_rubberEffectRecovering = false; | |
} | |
// MARK: Helpers - | |
float STLayerPanZoom::topEdgeDistance() | |
{ | |
cocos2d::Rect boundBox = getBoundingBox(); | |
return STROUND(MAX(_panBoundsRect.size.height + _panBoundsRect.origin.y - getPosition().y - | |
boundBox.size.height * (1 - getAnchorPoint().y), 0.f)); | |
} | |
float STLayerPanZoom::leftEdgeDistance() | |
{ | |
cocos2d::Rect boundBox = getBoundingBox(); | |
return STROUND(MAX(getPosition().x - boundBox.size.width * getAnchorPoint().x - _panBoundsRect.origin.x, 0.f)); | |
} | |
float STLayerPanZoom::bottomEdgeDistance() | |
{ | |
cocos2d::Rect boundBox = getBoundingBox(); | |
return STROUND(MAX(getPosition().y - boundBox.size.height * getAnchorPoint().y - _panBoundsRect.origin.y, 0.f)); | |
} | |
float STLayerPanZoom::rightEdgeDistance() | |
{ | |
cocos2d::Rect boundBox = getBoundingBox(); | |
return STROUND(MAX(_panBoundsRect.size.width + _panBoundsRect.origin.x - getPosition().x - | |
boundBox.size.width * (1 - getAnchorPoint().x), 0)); | |
} | |
float STLayerPanZoom::minPossibleScale() | |
{ | |
if ( ! _panBoundsRect.equals(cocos2d::Rect::ZERO)) | |
{ | |
return MAX(_panBoundsRect.size.width / getContentSize().width, | |
_panBoundsRect.size.height /getContentSize().height); | |
} | |
else | |
{ | |
return _minScale; | |
} | |
} | |
CCLayerPanZoomFrameEdge STLayerPanZoom::frameEdgeWithPoint( const Vec2& point) | |
{ | |
bool isLeft = point.x <= _panBoundsRect.origin.x + _leftFrameMargin; | |
bool isRight = point.x >= _panBoundsRect.origin.x + _panBoundsRect.size.width - _rightFrameMargin; | |
bool isBottom = point.y <= _panBoundsRect.origin.y + _bottomFrameMargin; | |
bool isTop = point.y >= _panBoundsRect.origin.y + _panBoundsRect.size.height - _topFrameMargin; | |
if (isLeft && isBottom) | |
{ | |
return kCCLayerPanZoomFrameEdgeBottomLeft; | |
} | |
if (isLeft && isTop) | |
{ | |
return kCCLayerPanZoomFrameEdgeTopLeft; | |
} | |
if (isRight && isBottom) | |
{ | |
return kCCLayerPanZoomFrameEdgeBottomRight; | |
} | |
if (isRight && isTop) | |
{ | |
return kCCLayerPanZoomFrameEdgeTopRight; | |
} | |
if (isLeft) | |
{ | |
return kCCLayerPanZoomFrameEdgeLeft; | |
} | |
if (isTop) | |
{ | |
return kCCLayerPanZoomFrameEdgeTop; | |
} | |
if (isRight) | |
{ | |
return kCCLayerPanZoomFrameEdgeRight; | |
} | |
if (isBottom) | |
{ | |
return kCCLayerPanZoomFrameEdgeBottom; | |
} | |
return kCCLayerPanZoomFrameEdgeNone; | |
} | |
float STLayerPanZoom::horSpeedWithPosition(const Vec2& pos){ | |
CCLayerPanZoomFrameEdge edge = frameEdgeWithPoint(pos); | |
float speed = 0.f; | |
if (edge == kCCLayerPanZoomFrameEdgeLeft) | |
{ | |
speed = _minSpeed + (_maxSpeed - _minSpeed) * | |
(_panBoundsRect.origin.x + _leftFrameMargin - pos.x) / _leftFrameMargin; | |
} | |
if (edge == kCCLayerPanZoomFrameEdgeBottomLeft || edge == kCCLayerPanZoomFrameEdgeTopLeft) | |
{ | |
speed = _minSpeed + (_maxSpeed - _minSpeed) * | |
(_panBoundsRect.origin.x + _leftFrameMargin - pos.x) / (_leftFrameMargin * sqrtf(2)); | |
} | |
if (edge == kCCLayerPanZoomFrameEdgeRight) | |
{ | |
speed = - (_minSpeed + (_maxSpeed - _minSpeed) * | |
(pos.x - _panBoundsRect.origin.x - _panBoundsRect.size.width + | |
_rightFrameMargin) / _rightFrameMargin); | |
} | |
if (edge == kCCLayerPanZoomFrameEdgeBottomRight || edge == kCCLayerPanZoomFrameEdgeTopRight) | |
{ | |
speed = - (_minSpeed + (_maxSpeed - _minSpeed) * | |
(pos.x - _panBoundsRect.origin.x - _panBoundsRect.size.width + | |
_rightFrameMargin) / (_rightFrameMargin * sqrtf(2))); | |
} | |
return speed; | |
} | |
float STLayerPanZoom::vertSpeedWithPosition(const Vec2& pos){ | |
CCLayerPanZoomFrameEdge edge = frameEdgeWithPoint(pos); | |
float speed = 0.f; | |
if (edge == kCCLayerPanZoomFrameEdgeBottom) | |
{ | |
speed = _minSpeed + (_maxSpeed - _minSpeed) * | |
(_panBoundsRect.origin.y + _bottomFrameMargin - pos.y) / _bottomFrameMargin; | |
} | |
if (edge == kCCLayerPanZoomFrameEdgeBottomLeft || edge == kCCLayerPanZoomFrameEdgeBottomRight) | |
{ | |
speed = _minSpeed + (_maxSpeed - _minSpeed) * | |
(_panBoundsRect.origin.y + _bottomFrameMargin - pos.y) / (_bottomFrameMargin * sqrtf(2)); | |
} | |
if (edge == kCCLayerPanZoomFrameEdgeTop) | |
{ | |
speed = - (_minSpeed + (_maxSpeed - _minSpeed) * | |
(pos.y - _panBoundsRect.origin.y - _panBoundsRect.size.height + | |
_topFrameMargin) / _topFrameMargin); | |
} | |
if (edge == kCCLayerPanZoomFrameEdgeTopLeft || edge == kCCLayerPanZoomFrameEdgeTopRight) | |
{ | |
speed = - (_minSpeed + (_maxSpeed - _minSpeed) * | |
(pos.y - _panBoundsRect.origin.y - _panBoundsRect.size.height + | |
_topFrameMargin) / (_topFrameMargin * sqrtf(2))); | |
} | |
return speed; | |
} | |
// MARK: Input - | |
void STLayerPanZoom::tapWaitFinished() | |
{ | |
dlog("tap enabled"); | |
_tapEnabled = true; | |
_tapCount = 0; | |
_touches.clear(); | |
} | |
void STLayerPanZoom::tapHandler() | |
{ | |
if(_tapCount == 2) { | |
_delegate && _delegate->layerPanZoomClickedAtPoint(_tapPosition, 2, false); | |
} else if (_tapCount == 1) { | |
// TODO: check that hasn't moved since | |
_delegate && _delegate->layerPanZoomClickedAtPoint(_tapPosition, 1, false); | |
} | |
//Resetting double tap count.. | |
dlog("resetting tap count"); | |
_tapCount = 0; | |
} | |
void STLayerPanZoom::setupInput() | |
{ | |
getEventDispatcher()->removeEventListenersForTarget(this); | |
#if ((CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX)) | |
setupMouseInput(); | |
setupKeyboardInput(); | |
#endif | |
setupTouchInputInternal(); | |
} | |
void STLayerPanZoom::setupMouseInput() | |
{ | |
auto director = Director::getInstance(); | |
auto ws = director->getWinSize(); | |
auto screenRect = cocos2d::Rect(0, 0, ws.width, ws.height); | |
auto listener0 = EventListenerMouse::create(); | |
listener0->onMouseDown = [this,screenRect](Event* event) { | |
if(isInputDisabled()) { return; } | |
auto mouseEvent = static_cast<EventMouse*>(event); | |
// bail if outside window | |
Vec2 mouseLocation = mouseEvent->getLocationInView(); | |
if(! screenRect.containsPoint(mouseLocation)) { return; } | |
if(mouseEvent->getMouseButton() == EventMouse::MouseButton::BUTTON_RIGHT) { | |
_isMouseDown = true; | |
Vec2 worldLocation = convertToNodeSpace(mouseLocation); | |
_delegate && _delegate->layerPanZoomTouchBegan(this, worldLocation); | |
_mouseBeganPositionRightDrag = mouseLocation; | |
_mouseBeganPositionRightMouseLocation = mouseLocation; | |
} | |
else { | |
_isMouseDown = true; | |
} | |
}; | |
listener0->onMouseMove = [this,screenRect](Event* event) { | |
if(isInputDisabled()) { return; } | |
auto mouseEvent = static_cast<EventMouse*>(event); | |
// bail if outside window | |
Vec2 mouseLocation = mouseEvent->getLocationInView(); | |
if(mouseEvent->getMouseButton() == EventMouse::MouseButton::BUTTON_LEFT) | |
{ | |
// handled by touch events | |
} | |
else if(mouseEvent->getMouseButton() == EventMouse::MouseButton::BUTTON_RIGHT) | |
{ | |
// let's allow panning with right mouse | |
if(! screenRect.containsPoint(mouseLocation)) { return; } | |
if(_isMouseDown)//TODO: && ! isPanDisabled()) | |
{ | |
Vec2 worldLocation = convertToNodeSpace(mouseLocation); | |
// TODO: may want to allow delegate to claim first? | |
//_delegate && _delegate->layerPanZoomTouchMoved(this, worldLocation); | |
// Always scroll in sheet mode. | |
if (_mode == kSTLayerPanZoomModeSheet) | |
{ | |
// move "game camera" by moving map | |
auto delta = mouseLocation - _mouseBeganPositionRightDrag; | |
dlog("delta = %s", CStrFromPoint(delta)); | |
auto newPos = getPosition() + delta; | |
dlog("newPos = %s", CStrFromPoint(newPos)); | |
Layer::setPosition(newPos); | |
_mouseBeganPositionRightDrag = mouseLocation; | |
} | |
} | |
} | |
else | |
{ | |
Vec2 worldLocation = convertToNodeSpace(mouseLocation); | |
if(! _isMouseDown) { | |
_delegate && _delegate->layerPanZoomMouseMovedOver(this, worldLocation); | |
} | |
} | |
}; | |
listener0->onMouseUp = [this,screenRect](Event* event) { | |
if(isInputDisabled()) { return; } | |
auto mouseEvent = static_cast<EventMouse*>(event); | |
_isMouseDown = false; | |
// bail if outside window | |
Vec2 mouseLocation = mouseEvent->getLocationInView(); | |
if(! screenRect.containsPoint(mouseLocation)) { return; } | |
if(mouseEvent->getMouseButton() == EventMouse::MouseButton::BUTTON_LEFT) | |
{ | |
// handled by touch events | |
} | |
else if(mouseEvent->getMouseButton() == EventMouse::MouseButton::BUTTON_RIGHT) | |
{ | |
// TODO: may want to invalidate the click if moves too far while in mousemove instead | |
// check if close to began position | |
auto dist = _mouseBeganPositionRightDrag.distance(_mouseBeganPositionRightMouseLocation); | |
if (dist < getMaxTouchDistanceToClick()) | |
{ | |
// Didn't move, register as click | |
Vec2 worldLocation = convertToNodeSpace(mouseLocation); | |
_delegate && _delegate->layerPanZoomTouchEnded(this, worldLocation); | |
} | |
} | |
}; | |
listener0->onMouseScroll = [this](Event* event) { | |
if(isInputDisabled()) { return; } | |
if(isZoomDisabled()) { return; } | |
if(_delegate && ! _delegate->layerPanZoomCanMouseEdgePanOrScrollZoom(this)) { | |
return; | |
} | |
if(CameraManager::get()->isMoving()) { | |
return; | |
} | |
if(_zoomWaitTimer > 0) { | |
return; | |
} | |
// TODO: check and make sure only works in window | |
auto mouseEvent = static_cast<EventMouse*>(event); | |
float scrollVelocity = mouseEvent->getScrollY(); | |
// TODO: could reset zoom wait timer | |
if(_zoomWaitTimer > 0) | |
{ | |
// wait | |
} | |
else | |
{ | |
// TODO: is SCALE_LARGE necessary? | |
// don't process unless large enough scroll | |
const float scrollVelocityRequired = 0.4f; | |
if(fabsf(scrollVelocity) < scrollVelocityRequired) { | |
return; | |
} | |
// TODO: should allow inverting, and changing rate of scroll | |
// zoom only on single press | |
auto curZoom = getScaleX(); | |
// find cur zoom?? TODO: WHY?? | |
auto n = _zoomLevelsNew.size(); | |
auto nextZoomIndex = 0; | |
for (auto i = 0; i < n; ++i) { | |
auto zoom = _zoomLevelsNew[i] * SCALE_LARGE; | |
if (fabsf(zoom - curZoom) < FLT_EPSILON) { | |
nextZoomIndex = i; | |
} | |
} | |
// get new zoom | |
//int nextZoomIndex = _zoomIndex; | |
if(scrollVelocity < 0) { | |
nextZoomIndex++; | |
} else if(scrollVelocity > 0) { | |
nextZoomIndex--; | |
} | |
nextZoomIndex = CLAMP(nextZoomIndex, 0, int(_zoomLevelsNew.size() - 1)); | |
if(nextZoomIndex != _zoomIndex) | |
{ | |
dinfo("_zoomIndex = %d, nextzoomindex = %d", _zoomIndex, nextZoomIndex); | |
auto newScale = _zoomLevelsNew[nextZoomIndex] * SCALE_LARGE; | |
dinfo("scaling to %f", newScale); | |
float dur = 0.3f; //kZoomScaleAnimationDuration; | |
CameraManager::get()->moveBy(Vec2::ZERO, true, newScale, dur); | |
_zoomIndex = nextZoomIndex; | |
_isMouseScrolling = true; | |
_zoomWaitTimer = dur * 2.f; // TODO: make into const | |
_zoomWaitTimerDampen = _zoomWaitTimer; | |
_lastMouseScrollVelocity = scrollVelocity; | |
} | |
} | |
}; | |
getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener0, this); | |
} | |
//// TODO: | |
//bool STLayerPanZoom::onTouchesBegan(const std::vector<Touch*>& pTouches, Event *) | |
//{ | |
//} | |
//void STLayerPanZoom::onTouchesMoved(const std::vector<Touch*>& pTouches, Event *) | |
//{ | |
//} | |
//void STLayerPanZoom::onTouchesEnded(const std::vector<Touch*>& pTouches, Event *) | |
//{ | |
//} | |
//void STLayerPanZoom::onTouchesCancelled(const std::vector<Touch*>& pTouches, Event *) | |
//{ | |
//} | |
void STLayerPanZoom::setupTouchInputInternal() | |
{ | |
dlog("enter: %p", this); | |
//Create a "one by one" touch event listener (processes one touch at a time) | |
auto listener1 = EventListenerTouchAllAtOnce::create(); | |
// Example of using a lambda expression to implement onTouchBegan event callback function | |
#warning TODO: listener1->onTouchesBegan = CC_CALLBACK_1(&STLayerPanZoom::onTouchesBegan, this); | |
listener1->onTouchesBegan = [this](const std::vector<Touch*>& pTouches, Event*) { | |
dlog("STLayerPanZoom[onTouchesBegan] isInputDisabled = %d", isInputDisabled()); | |
if (isInputDisabled()) { return; } | |
if (! isVisitableByVisitingCamera()) { return; } | |
for (auto& pTouch : pTouches) | |
{ | |
Vec2 viewLocation = pTouch->getLocationInView(); | |
dlog("touch view location: %s", CStrFromPoint(viewLocation)); | |
Vec2 screenLocation = Director::getInstance()->convertToGL(viewLocation); | |
dlog("touch screen location: %s", CStrFromPoint(screenLocation)); | |
Vec2 worldLocation = convertToNodeSpace(screenLocation); | |
dlog("touch world location: %s", CStrFromPoint(worldLocation)); | |
_touches.pushBack(pTouch); | |
} | |
if (_touches.size() == 1) | |
{ | |
_touchMoveBegan = false; | |
_singleTouchTimestamp = time(nullptr); | |
Touch* touch = (Touch*)_touches.at(0); | |
Vec2 curPos = Director::getInstance()->convertToGL(touch->getLocationInView()); | |
_tapBeganPosition = convertToNodeSpace(curPos); | |
// REMOVE: this seems to be useless, method prob already restricted by platform | |
#if ((CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX)) | |
auto mouseLocation = touch->getLocationInView(); | |
// touch is not (or already) reversed Y | |
auto ws = Director::getInstance()->getWinSize(); | |
mouseLocation.y = ws.height - mouseLocation.y; | |
_isMouseDown = true; | |
_mouseBeganPosition = mouseLocation; | |
_delegate && _delegate->layerPanZoomMouseBegan(this, mouseLocation); | |
#endif | |
} | |
else | |
{ | |
// dlog("[began] multitouch"); | |
_singleTouchTimestamp = 999999; | |
} | |
//dlog("touch count = " PRINTF_ZD "", _touches->count()); | |
}; | |
listener1->onTouchesMoved = [this](const std::vector<Touch*>&, Event *) { | |
if(isInputDisabled()) { return; } | |
if (_touches.size() >= 2) | |
{ | |
// Get the two first touches | |
Touch* touch1 = (Touch*)_touches.at(0); | |
Touch* touch2 = (Touch*)_touches.at(1); | |
// Get current and previous positions of the touches | |
Vec2 curPosTouch1 = touch1->getLocation(); | |
Vec2 curPosTouch2 = touch2->getLocation(); | |
Vec2 prevPosTouch1 = touch1->getPreviousLocation(); | |
Vec2 prevPosTouch2 = touch2->getPreviousLocation(); | |
if(curPosTouch1 == curPosTouch2) | |
{ | |
_touches.clear(); | |
return; | |
} | |
float curTouchDist = curPosTouch1.getDistance(curPosTouch2); | |
float prevTouchDist = prevPosTouch1.getDistance(prevPosTouch2); | |
// Calculate new scale | |
float prevScale = getScaleX(); | |
float newScale = prevScale * curTouchDist / prevTouchDist; | |
newScale = clampf(newScale, _minScale, _maxScale); | |
// If current and previous position of the multitouch's center aren't equal -> change position of the layer | |
Vec2 midpointDelta = Vec2::ZERO; | |
// Calculate current and previous positions of the layer relative the anchor point | |
Vec2 curMidpointTouch = curPosTouch1.getMidpoint(curPosTouch2); | |
Vec2 prevMidpointTouch = prevPosTouch1.getMidpoint(prevPosTouch2); | |
// dlog("pos: %s", CStrFromPoint(getPosition())); | |
// dlog("cur1: %s", CStrFromPoint(curPosTouch1)); | |
// dlog("cur2: %s", CStrFromPoint(curPosTouch2)); | |
// dlog("midPrev: %s", CStrFromPoint(prevMidpointTouch)); | |
// dlog("midCur: %s", CStrFromPoint(curMidpointTouch)); | |
// dlogFloat(prevScale); | |
// dlogFloat(newScale); | |
//////////////////////////////////////////////////////////////////////// | |
// pan if the midpoint has moved | |
// TODO: should probably check with an epsilon | |
if ( ! prevMidpointTouch.equals(curMidpointTouch)) | |
{ | |
// In Screen Coords (opposite direction of world coords) | |
Vec2 midpointPanDelta = (prevMidpointTouch - curMidpointTouch); | |
dlog("midpointPanDelta [before]: %s", CStrFromPoint(midpointPanDelta)); | |
// stretch/shrink to match whether zooming in/out | |
//midpointPanDelta *= (newScale / prevScale); | |
midpointPanDelta *= (prevScale / newScale); | |
dlog("midpointPanDelta [after]: %s", CStrFromPoint(midpointPanDelta)); | |
// convert into world coords | |
// this mostly corrects for too much/little camera move if scale is <> 1 | |
// paranoia: FLT_EPSILON | |
midpointPanDelta *= (1.f / (newScale + FLT_EPSILON)); | |
midpointDelta += midpointPanDelta; | |
dlog("midpointDelta: %s", CStrFromPoint(midpointDelta)); | |
} | |
//////////////////////////////////////////////////////////////////////// | |
// Pan/Move of camera required to center on midpoint while scaling | |
{ | |
auto wsHalf = Director::getInstance()->getWinSize() * .5f; | |
Vec2 screenCenter { wsHalf.width, wsHalf.height }; | |
Vec2 offsetFromCenter = curMidpointTouch - screenCenter; | |
// ??? why 1/newscale ??? ... maybe because needs to also be in world coord??? | |
float scaleDelta = (prevScale - newScale); | |
offsetFromCenter *= scaleDelta; | |
offsetFromCenter *= (-1.f / prevScale); | |
offsetFromCenter *= (1.f / newScale); | |
midpointDelta += offsetFromCenter; // negative since camera screen is opposite camera world coord system | |
dlog("[test2]"); | |
dlog("screenCenter: %s", CStrFromPoint(screenCenter)); | |
dlog("offsetFromCenter: %s", CStrFromPoint(offsetFromCenter)); | |
dlog("midpointDelta: %s", CStrFromPoint(midpointDelta)); | |
dlogFloat(scaleDelta); | |
} | |
// NOTE: this is move by in "world coords" | |
CameraManager::get()->moveBy(midpointDelta, false, newScale, 0.f); | |
// Don't click with multitouch | |
_touchDistance = 999999.9f; | |
} | |
else if(_touches.size() == 1) | |
{ | |
Touch* touch = (Touch*)_touches.at(0); | |
// Get the single touch and it's previous & current position. | |
Vec2 curTouchPosition = touch->getLocation(); | |
Vec2 prevTouchPosition = touch->getPreviousLocation(); | |
// Accumulate touch distance for all modes. | |
setTouchDistance(getTouchDistance() + curTouchPosition.getDistance(prevTouchPosition)); | |
// Inform delegate about starting updating touch position, if click isn't possible. | |
if (getTouchDistance() > getMaxTouchDistanceToClick()) | |
{ | |
// MOVING | |
#if ((CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX)) | |
Vec2 mouseLocation = touch->getLocationInView(); | |
// touch is not (or already) reversed Y | |
auto ws = Director::getInstance()->getWinSize(); | |
mouseLocation.y = ws.height - mouseLocation.y; | |
// if(! screenRect.containsPoint(mouseLocation)) { return; } | |
if(_isMouseDown) { | |
_delegate && _delegate->layerPanZoomMouseMoved(this, mouseLocation); | |
} | |
#else | |
bool claimed = false; | |
if(! _touchMoveBegan) | |
{ | |
//ToDo add delegate here | |
//[self.delegate layerPanZoom: self | |
// touchMoveBeganAtPosition: [self convertToNodeSpace: prevTouchPosition]]; | |
// claimed = _delegate->layerPanZoomTouchBegan(this, convertToNodeSpace(prevTouchPosition)); | |
_touchMoveBegan = true; | |
} | |
// else { | |
// claimed = _delegate->layerPanZoomTouchMoved(this, convertToNodeSpace(prevTouchPosition)); | |
// } | |
if( ! claimed) { | |
// Always scroll in sheet mode. | |
if (_mode == kSTLayerPanZoomModeSheet) | |
{ | |
// Set new position of the layer. | |
float x = getPosition().x + curTouchPosition.x - prevTouchPosition.x; | |
float y = getPosition().y + curTouchPosition.y - prevTouchPosition.y; | |
setPosition(Vec2(x,y)); | |
} | |
} | |
#endif | |
} | |
} | |
}; | |
//Process the touch end event | |
listener1->onTouchesEnded = [this](const std::vector<Touch*>& pTouches, Event *) { | |
//dlog("[STLayerPanZoom::ccTouchesEnded] touch end"); | |
if(isInputDisabled()) { | |
for (auto& pTouch : pTouches) { | |
_touches.eraseObject(pTouch); | |
} | |
return; | |
} | |
_singleTouchTimestamp = 9999999; | |
// Process click event in single touch. | |
//ToDo add delegate | |
if ( (getTouchDistance() < getMaxTouchDistanceToClick()) && getDelegate()) | |
{ | |
if(_touches.size() == 1) | |
{ | |
Touch* touch = (Touch*)_touches.at(0); | |
_tapPosition = touch->getLocation(); | |
// check for short circuited touch | |
bool shortCircuited = _delegate->layerPanZoomClickedAtPoint(_tapPosition, 1, true); | |
dlog("touched, and shortCircuited = %d", shortCircuited); | |
if(shortCircuited) | |
{ | |
// delegate swalled the short circuit | |
// // (Maximum allowed delay between taps) | |
// DelayTime* delayAction = DelayTime::create(kDoubleTapDelay); | |
// CallFunc* callSelectorAction = CallFunc::create([this]() { | |
// tapWaitFinished(); | |
// }); | |
// runAction(Sequence::create(delayAction,callSelectorAction,nullptr)); | |
// _tapEnabled = false; | |
_tapCount = 0; | |
_touches.clear(); | |
stopActionByTag(5); | |
return; | |
} | |
else if (_tapCount == 0) | |
{ | |
// auto delayAction = DelayTime::create(kDoubleTapDelay); | |
// auto callSelectorAction = CallFunc::create([this]() { | |
// tapHandler(); | |
// }); | |
// auto seq = Sequence::createWithTwoActions(delayAction, callSelectorAction); | |
// seq->setTag(5); | |
// runAction(seq); | |
} | |
_tapCount++; | |
CCLOG("tapcount = %d", _tapCount); | |
} | |
// if(_tapCount == 0) | |
// { | |
// _tapPosition = convertToNodeSpace(curPos); | |
// | |
// // TODO: | |
// // - setup delayed handler | |
// // (Maximum allowed delay between taps) | |
// | |
// // - if claimed cancel handler | |
// | |
// // - if delayed handler fires, call click | |
// // - if tapcount 2 call click w/(2) | |
// | |
// else if(_tapEnabled) | |
// { | |
// float dist = _tapPosition.getDistanceSq(_tapBeganPosition); | |
// const float maxDist = 10*10; | |
// bool canDoublTap = _isDoubleTapAllowed && dist < maxDist; | |
// if(canDoublTap) | |
// { | |
// // (Maximum allowed delay between taps) | |
// auto action = getActionByTag(5); | |
// if(! action || action->isDone()) | |
// { | |
// auto delayAction = DelayTime::create(kDoubleTapDelay); | |
// auto callSelectorAction = CallFunc::create([this]() { | |
// tapHandler(); | |
// }); | |
// auto seq = Sequence::createWithTwoActions(delayAction, callSelectorAction); | |
// seq->setTag(5); | |
// runAction(seq); | |
// } | |
// else { | |
// dinfo("touch while taphandler action still running"); | |
// } | |
// } | |
// else | |
// { | |
// _delegate->layerPanZoomClickedAtPoint(_tapPosition, 1, false); | |
// _tapCount = 0; | |
// _touches.clear(); | |
// return; | |
// } | |
// } | |
// } | |
// else if(_tapCount > 2) | |
// { | |
// CCLOG("_tapcount > 3"); | |
// _tapEnabled = false; | |
// auto delayAction = DelayTime::create(kDoubleTapDelay); | |
// auto callSelectorAction = CallFunc::create([this]() { | |
// tapWaitFinished(); | |
// }); | |
// runAction(Sequence::createWithTwoActions(delayAction, callSelectorAction)); | |
// _tapCount = 0; | |
// _touches.clear(); | |
// return; | |
// } | |
// else if(_tapCount > 0) | |
// { | |
// dinfo("_tapcount was %d", _tapCount); | |
// } | |
// | |
// ++_tapCount; | |
// CCLOG("tapcount = %d", _tapCount); | |
// } | |
// _touches.clear(); | |
} | |
else if(! isPanDisabled()) | |
{ | |
bool multitouch = _touches.size() > 1; | |
if (multitouch) | |
{ | |
// Get the two first touches | |
Touch* touch1 = (Touch*)_touches.at(0); | |
Touch* touch2 = (Touch*)_touches.at(1); | |
// Get current and previous positions of the touches | |
auto loc1 = touch1->getLocationInView(); | |
auto loc2 = touch2->getLocationInView(); | |
auto prevLoc1 = touch1->getPreviousLocationInView(); | |
auto prevLoc2 = touch2->getPreviousLocationInView(); | |
Vec2 curPosTouch1 = Director::getInstance()->convertToGL(loc1); | |
Vec2 curPosTouch2 = Director::getInstance()->convertToGL(loc2); | |
Vec2 prevPosTouch1 = Director::getInstance()->convertToGL(prevLoc1); | |
Vec2 prevPosTouch2 = Director::getInstance()->convertToGL(prevLoc2); | |
if(curPosTouch1 == curPosTouch2) | |
{ | |
_touches.clear(); | |
return; | |
} | |
float curTouchDist = curPosTouch1.getDistance(curPosTouch2); | |
float prevTouchDist = prevPosTouch1.getDistance(prevPosTouch2); | |
// Calculate new scale | |
float prevScale = getScaleX(); | |
float newScale = prevScale; | |
if(prevTouchDist > 0.1f) | |
{ | |
newScale = newScale * curTouchDist / prevTouchDist; | |
// ipad air (4x): 8,4,2,1.2 | |
// ipad non-retina 3rd (2x): 8,4,2,1.2 | |
// iphone 6 plus (3x): | |
// iphone 6 (3x): | |
// iphone 5s (3x): | |
float retinaScale = SCALE_LARGE; | |
if(newScale < 0.15f * retinaScale) { | |
newScale = 0.1f * retinaScale; | |
} | |
else if(newScale < 0.25f * retinaScale) { | |
newScale = 0.2f * retinaScale; | |
} | |
else if(newScale < 0.4f * retinaScale) { | |
newScale = 0.3f * retinaScale; | |
} | |
else if(newScale < 0.75f * retinaScale) { | |
newScale = 0.5f * retinaScale; | |
} | |
else if(newScale < 1.5f * retinaScale) { | |
newScale = 1.f * retinaScale; | |
} | |
else { //if(newScale < 2.5f * retinaScale) { | |
newScale = 2 * retinaScale; | |
} | |
// else { //if(newScale < 3.5f * retinaScale) { | |
// newScale = 3.f * retinaScale; | |
// } | |
if(newScale != prevScale) | |
{ | |
// TODO: use same calcs as in onMove | |
Vec2 midpointDelta { Vec2::ZERO }; | |
Vec2 curMidpointTouch = curPosTouch1.getMidpoint(curPosTouch2); | |
// test #2 move adjust to allow pinch to zoom in non-centered screen locations | |
{ | |
auto wsHalf = Director::getInstance()->getWinSize() * .5f; | |
Vec2 screenCenter { wsHalf.width, wsHalf.height }; | |
Vec2 offsetFromCenter = curMidpointTouch - screenCenter; | |
float scaleDelta = (prevScale - newScale) / newScale; // ??? why ??? ... maybe because needs to also be in world coord??? | |
offsetFromCenter *= scaleDelta; | |
midpointDelta = -1.f / prevScale * offsetFromCenter * 1.f; // negative since camera screen is opposite camera world coord system | |
dlog("[test2]"); | |
dlog("screenCenter: %s", CStrFromPoint(screenCenter)); | |
dlog("offsetFromCenter: %s", CStrFromPoint(offsetFromCenter)); | |
dlog("midpointDelta: %s", CStrFromPoint(midpointDelta)); | |
dlogFloat(scaleDelta); | |
} | |
bool animated = true; | |
float dur = kZoomScaleAnimationDuration; | |
dlog("newScale = %f, retinaScale = %f", newScale); | |
CameraManager::get()->moveBy(midpointDelta, animated, newScale, dur); | |
} | |
} | |
// Don't click with multitouch | |
setTouchDistance(999999.9f); | |
} | |
else if(_touches.size() == 1) | |
{ | |
#if ((CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX)) | |
// Get the two first touches | |
Touch* touch = (Touch*)_touches.at(0); | |
// handle end for moved | |
Vec2 mouseLocation = touch->getLocationInView(); | |
// touch is not (or already) reversed Y | |
auto ws = Director::getInstance()->getWinSize(); | |
mouseLocation.y = ws.height - mouseLocation.y; | |
// TODO: remove or use for making sure this event is valid | |
if(_isMouseDown) {} | |
auto dist = _mouseBeganPosition.distance(mouseLocation); | |
if(dist < getMaxTouchDistanceToClick()) { | |
// mouse didn't move, register as CLICK event | |
Vec2 worldLocation = convertToNodeSpace(mouseLocation); | |
dinfo2("calling TOUCH for single click @ %s", CStrFromPoint(worldLocation)); | |
_delegate && _delegate->layerPanZoomClickedAtPoint(worldLocation, 1, false); | |
} else { | |
dinfo2("calling MOUSE END @ %s", CStrFromPoint(mouseLocation)); | |
_delegate && _delegate->layerPanZoomMouseEnded(this, mouseLocation); | |
} | |
#else | |
// nothing | |
#endif | |
} | |
} | |
// remove touches | |
for (auto& pTouch : pTouches) { | |
_touches.eraseObject(pTouch); | |
} | |
if (_touches.size() == 0) { | |
setTouchDistance(0.f); | |
} | |
}; | |
#warning TODO: listener1->onTouchesCancelled = std::bind(); | |
listener1->onTouchesCancelled = [this](const std::vector<Touch*>& pTouches, Event *) { | |
for (auto& pTouch : pTouches) { | |
_touches.eraseObject(pTouch); | |
} | |
if (_touches.size() == 0) { | |
setTouchDistance(0.f); | |
} | |
}; | |
//Add listener | |
dlog("adding stlayerpanzoom event listener1"); | |
getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener1, this); | |
} | |
static const float keyInputCameraSpeed = 1000.f; | |
void STLayerPanZoom::setupKeyboardInput() | |
{ | |
// TODO: keystates don't get removed | |
if(_cameraInputs.empty()) | |
{ | |
_cameraInputs[EventKeyboard::KeyCode::KEY_A] = Vec2(-keyInputCameraSpeed, 0); | |
_cameraInputs[EventKeyboard::KeyCode::KEY_D] = Vec2(keyInputCameraSpeed, 0); | |
_cameraInputs[EventKeyboard::KeyCode::KEY_W] = Vec2(0, keyInputCameraSpeed); | |
_cameraInputs[EventKeyboard::KeyCode::KEY_S] = Vec2(0, -keyInputCameraSpeed); | |
} | |
if(_cameraInputsArrows.empty()) | |
{ | |
_cameraInputsArrows[EventKeyboard::KeyCode::KEY_LEFT_ARROW] = Vec2(-keyInputCameraSpeed, 0); | |
_cameraInputsArrows[EventKeyboard::KeyCode::KEY_RIGHT_ARROW] = Vec2(keyInputCameraSpeed, 0); | |
_cameraInputsArrows[EventKeyboard::KeyCode::KEY_UP_ARROW] = Vec2(0, keyInputCameraSpeed); | |
_cameraInputsArrows[EventKeyboard::KeyCode::KEY_DOWN_ARROW] = Vec2(0, -keyInputCameraSpeed); | |
} | |
auto listener = EventListenerKeyboard::create(); | |
listener->onKeyPressed = [this](cocos2d::EventKeyboard::KeyCode keyCode, Event*) { | |
dinfo2("PANZOOM :: key pressed: %d", keyCode); | |
_keyState[keyCode] = true; | |
}; | |
listener->onKeyReleased = [this](cocos2d::EventKeyboard::KeyCode keyCode, Event*) { | |
dinfo2("PANZOOM :: key released: %d", keyCode); | |
_keyState[keyCode] = false; | |
if(CameraManager::get()->isMoving()) | |
{ | |
return; | |
} | |
else if(! isZoomDisabled()) | |
{ | |
// zoom only on single press | |
auto curZoom = getScaleX(); | |
// TODO: find cur zoom ... WHY? | |
auto n = _zoomLevelsNew.size(); | |
size_t nextZoomIndex = 0; | |
for (auto i = 0; i < n; ++i) { | |
auto zoom = _zoomLevelsNew[i] * SCALE_LARGE; | |
if (fabsf(zoom - curZoom) < FLT_EPSILON) { | |
nextZoomIndex = i; | |
} | |
} | |
//nextZoomIndex = getZoomIndex(zoomLevels, curZoom); | |
bool dirty = false; | |
if(keyCode == EventKeyboard::KeyCode::KEY_Q) { | |
if(nextZoomIndex > 0) { | |
nextZoomIndex--; | |
dirty = true; | |
} | |
} else if(keyCode == EventKeyboard::KeyCode::KEY_E) { | |
if(nextZoomIndex < _zoomLevelsNew.size() - 1) { | |
nextZoomIndex++; | |
dirty = true; | |
} | |
} | |
if(dirty) { | |
nextZoomIndex = CLAMP(nextZoomIndex, 0, (_zoomLevelsNew.size() - 1)); | |
auto newScale = _zoomLevelsNew[nextZoomIndex] * SCALE_LARGE; | |
CameraManager::get()->moveBy(Vec2::ZERO, true, newScale); | |
} | |
} | |
}; | |
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, this); | |
// Director::getInstance()->getEventDispatcher()->addEventListenerWithFixedPriority(listener, kFixedPriorityPanZoomKeyboard); | |
} | |
///////////////////////////////////////////////////// | |
// MARK: - | |
// FIXME - is this needed? yes for the action based camera movement it is | |
void STLayerPanZoom::updateCameraAction(float dt) | |
{ | |
// TODO: counter-effect the simulation speed | |
// float simulationTimeScale = Director::getInstance()->getScheduler()->getTimeScale(); | |
// CCASSERT(simulationTimeScale, "simulationTimeScale SHOULD NOT BE ZERO!"); | |
// _elapsed += (dt * 1.f/simulationTimeScale); | |
_elapsed += dt; | |
switch(_stage) { | |
case 0: { | |
// delay | |
if(_elapsed >= _targetDelay) { | |
++_stage; | |
_elapsed = 0; | |
} | |
} break; | |
case 1: { | |
// done? | |
if(_elapsed >= _targetTime) { | |
++_stage; | |
_elapsed = _targetTime; | |
//break; | |
} | |
// movement | |
#if CC_ENABLE_STACKABLE_ACTIONS | |
// easeing - ease in | |
float updateDt = MAX (0, // needed for rewind. elapsed could be negative | |
MIN(1, _elapsed / | |
MAX(_targetTime, FLT_EPSILON) // division by 0 | |
) | |
); | |
auto time = tweenfunc::easeIn(updateDt, _easeRate); | |
// //_targetPos | |
Vec2 currentPos = this->getPosition(); | |
Vec2 diff = currentPos - _previousPosition; | |
_startPosition = _startPosition + diff; | |
Vec2 newPos = _startPosition + (_deltaPosition * time); | |
this->setPosition(newPos); | |
_previousPosition = newPos; | |
// scaling | |
this->setScaleX(_startScaleX + _deltaScaleX * time); | |
this->setScaleY(_startScaleY + _deltaScaleY * time); | |
this->setScaleZ(1.f); | |
#else | |
#endif | |
} break; | |
case 2: { | |
_stage = -1; | |
_elapsed = 0; | |
_targetTime = 0; | |
if(_targetCallback) { | |
_targetCallback(); | |
} | |
} break; | |
default: { | |
// waiting | |
} | |
} | |
} | |
void STLayerPanZoom::runCameraAction(float delay, float duration, const Vec2& newPos, float newScale, float easingRate, const std::function<void()>& callback) | |
{ | |
_targetCallback = callback; | |
_stage = 0; | |
_elapsed = 0; | |
_targetTime = duration; | |
_easeRate = easingRate; | |
// moveto | |
_startPosition = this->getPosition(); | |
_previousPosition = _startPosition; | |
_previousScale = this->getScaleX(); | |
_targetDelay = delay; | |
_targetScale = newScale; | |
_targetPosition = newPos; | |
_deltaPosition = _targetPosition - _previousPosition; | |
// scaleto | |
_startScaleX = this->getScaleX(); | |
_startScaleY = this->getScaleY(); | |
_endScaleX = newScale; | |
_endScaleY = newScale; | |
_deltaScaleX = _endScaleX - _startScaleX; | |
_deltaScaleY = _endScaleY - _startScaleY; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment