Skip to content

Instantly share code, notes, and snippets.

@stevetranby
Last active January 8, 2018 07:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stevetranby/32bf3f27b389e5497750b33edeb8c8cc to your computer and use it in GitHub Desktop.
Save stevetranby/32bf3f27b389e5497750b33edeb8c8cc to your computer and use it in GitHub Desktop.
Camera Management
//
// CameraManager.h
//
// Created by Steve Tranby on 4/21/13.
// Copyright (c) 2013 Steve Tranby. All rights reserved.
//
// License: MIT
//
#pragma once
#include "extensions/STLayerPanZoom.h"
typedef std::function<void()> popCallback;
class CameraManager
{
// Singleton Stuff
public:
static CameraManager* get();
private:
CameraManager() = default;
~CameraManager() = default;
CameraManager(const CameraManager&) = delete;
CameraManager& operator=(const CameraManager&) = delete;
CameraManager(CameraManager&&) = delete;
CameraManager& operator=(CameraManager&&) = delete;
public:
void update(float dt);
void setupWithPanZoomLayer(STLayerPanZoom* layer);
void resetManager();
/// Get's world coordinate of the camera's center point
cocos2d::Vec2 worldToScreen(const cocos2d::Vec2& worldCoord, float atScale);
cocos2d::Vec2 screenToWorld(const cocos2d::Vec2& screenCoord, float atScale);
cocos2d::Vec2 currentWorldCoord();
void pushState();
bool popStateWithAction(const popCallback& callback);
bool popState();
bool isMoving();
/// reposition camera's center to a given world coord
/// defaults: newScale = 0, dur = 0, delay = 0
void setViewpointCenter(const cocos2d::Vec2& newWorldCoord, bool animated, float newScale = 0, float dur = 0, float delay = 0, const popCallback& callback = [](){});
/// reposition camera's center to a given world coord
/// defaults: no animation (instant move), no change to zoom, dur = 0
void moveTo(const cocos2d::Vec2& position, bool animated = false, float newScale = 0, float dur = 0);
/// move camera's center by a given amount in world coord units
/// defaults: no animation (instant move), no change to zoom, dur = 0
void moveBy(const cocos2d::Vec2& position, bool animated = false, float newScale = 0, float dur = 0);
void scaleBy(float scaleFactor);
void setInputDisabled(bool disabled);
private:
//cocos2d::ValueVector
struct CameraState {
cocos2d::Vec2 worldCoord;
float scale;
};
std::vector<CameraState> stateStack;
STLayerPanZoom* panZoomLayer;
};
//
// CameraManager.cpp
//
// Created by Steve Tranby on 4/21/13.
// Copyright (c) 2013 Steve Tranby. All rights reserved.
//
// License: MIT
//
#include "stdafx.h"
#include "managers/CameraManager.h"
USING_NS_CC;
CameraManager* CameraManager::get()
{
static CameraManager instance;
return &instance;
}
void CameraManager::setInputDisabled(bool disabled) {
panZoomLayer->setInputDisabled(disabled);
}
void CameraManager::setupWithPanZoomLayer(STLayerPanZoom* layer)
{
panZoomLayer = layer;
}
void CameraManager::resetManager()
{
dinfo("Resetting CM Manager!!");
//setPanZoomLayer(nullptr);
panZoomLayer = nullptr;
stateStack.clear();
}
///////////////////////////////////////////
// MARK: -
void CameraManager::pushState()
{
if(! panZoomLayer) { return; }
panZoomLayer->setInputDisabled(true);
CameraState state;
state.worldCoord = currentWorldCoord();
state.scale = panZoomLayer->getScaleX();
stateStack.push_back(state);
dlog("pushstate: %s, scaleTo=%f", CStrFromPoint(state.worldCoord), state.scale);
}
bool CameraManager::popStateWithAction(const popCallback& callback)
{
if(! panZoomLayer) { return false; }
if(stateStack.empty()) { return false; }
panZoomLayer->stopAllActions();
// return camera to top state (pos,zoom)
auto state = stateStack.back();
float duration = 0.4f;
dlog("popstate: %s, scaleTo=%f, callback = %p", CStrFromPoint(state.worldCoord), state.scale, &callback);
setViewpointCenter(state.worldCoord, true, state.scale, duration, 0.f, callback);
stateStack.pop_back();
panZoomLayer->setInputDisabled(false);
panZoomLayer->resetTouches();
return true;
}
bool CameraManager::popState()
{
return popStateWithAction(nullptr);
}
// MARK: -
Vec2 CameraManager::worldToScreen(const cocos2d::Vec2& worldCoord, float atScale)
{
auto ws = Director::getInstance()->getWinSize();
Vec2 screenCenter { ws.width * .5f, ws.height * .5f };
Vec2 newOrigin = worldCoord * atScale;
Vec2 screenCoord = screenCenter - newOrigin;
dinfo2("screenCoored: %s", CStrFromPoint(screenCoord));
//return { truncf(screenCoord.x), truncf(screenCoord.y) };
return { screenCoord.x, screenCoord.y };
}
Vec2 CameraManager::screenToWorld(const cocos2d::Vec2& screenCoord, float atScale)
{
auto wsHalf = Director::getInstance()->getWinSize() * .5f;
Vec2 screenCenter { wsHalf.width, wsHalf.height };
Vec2 worldOrigin = screenCenter - screenCoord;
Vec2 worldCoord = worldOrigin * 1.f / atScale;
return worldCoord;
}
Vec2 CameraManager::currentWorldCoord()
{
if(! panZoomLayer) { return Vec2::ZERO; }
Vec2 p = panZoomLayer->getPosition();
float s = panZoomLayer->getScaleX();
// TODO: add to dlog macros header
// #define dlogVec2(x) dlog(#x ": %s", CStrFromPoint(x))
// dlogVec2(p);
// dlogFloat(s);
return screenToWorld(p, s);
}
// MARK: -
bool CameraManager::isMoving()
{
if(! panZoomLayer) { return false; }
auto action = panZoomLayer->getActionByTag(kTagActionCameraMoving);
if(action && ! action->isDone()) {
return true;
}
return false;
}
// MARK: -
//void CameraManager::setViewpointCenter(const Vec2& newWorldCoord, bool animated, float newScale /* = 0 */, float dur /* = 0 */, float delay /* = 0 */, FiniteTimeAction* callback /* = nullptr */)
void CameraManager::setViewpointCenter(const Vec2& newWorldCoord, bool animated, float newScale /* = 0 */, float dur /* = 0 */, float delay /* = 0 */, const std::function<void()>& callback)
{
if(! panZoomLayer) { return; }
if(newScale == 0) {
newScale = panZoomLayer->getScaleX();
}
// TODO: @profile @performance
float minScale = panZoomLayer->minScale();
float maxScale = panZoomLayer->maxScale();
newScale = clampf(newScale, minScale, maxScale);// .25f * SCALE_LARGE, 2.f * SCALE_LARGE);
// just find where panZoom should be if centering at new scale
Vec2 newPos = worldToScreen(newWorldCoord, newScale);
if(animated)
{
float easeRate = 1.5f;
dinfo2("panZoomLayer position by action => %s, scale: %f, easerate: %f\n\n", CStrFromPoint(newPos), newScale, easeRate);
panZoomLayer->runCameraAction(delay, dur, newPos, newScale, easeRate, callback);
}
else
{
panZoomLayer->setPosition(newPos);
panZoomLayer->setScale(newScale);
dinfo2("newWorldCoord: %s, newPos: %s, newScale: %f\n\n",
CStrFromPoint(newWorldCoord),
CStrFromPoint(newPos),
newScale);
}
}
// MARK: Move To -
void CameraManager::moveTo(const Vec2& position, bool animated /* = false */, float newScale /* = 0 */, float dur /* = 0 */)
{
if(! panZoomLayer) { return; }
if (newScale == 0) { newScale = panZoomLayer->getScaleX(); }
setViewpointCenter(position, animated, newScale, dur);
}
// MARK: Move By -
// position - ?? world coord ?? offset from current camera "center of screen" world coord
void CameraManager::moveBy(const Vec2& position, bool animated /* = false */, float newScale /* = 0 */, float dur /* = 0 */)
{
//dinfo("enter: moveby p = %s", CStrFromPoint(position));
if(! panZoomLayer) { return; }
if(position.isZero() && newScale == 0) { return; }
Vec2 oldWorldCoord = currentWorldCoord();
Vec2 newWorldCoord = oldWorldCoord + position;
if (animated && dur == 0) {
dur = 0.5f;
}
if (newScale == 0) {
newScale = panZoomLayer->getScaleX();
}
setViewpointCenter(newWorldCoord, animated, newScale, dur);
}
void CameraManager::scaleBy(float scaleFactor)
{
if(! panZoomLayer) { return; }
float newScale = panZoomLayer->getScaleX();
newScale *= scaleFactor;
moveBy(Vec2::ZERO, false, newScale, 0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment