#include "tracking_view.h" #include "../../utilities/vector_utils.hpp" TrackingView::~TrackingView() { delete view; delete marker; } void TrackingView::lateUpdate(Game *game) { // Initialize if necessary if (view == nullptr) { initializeView(game); } // Update size setSize(game->window->getSize()); if (target != nullptr) { followTarget(); } // Update window if necessary if (hasViewChanged) { game->window->setView(*this->view); hasViewChanged = false; } } void TrackingView::initializeView(Game *game) { auto size = game->window->getView().getSize(); view = new sf::View({0, 0}, size); } void TrackingView::setSize(sf::Vector2u windowSize) { // TODO: Maybe listen to resize events instead of checking every frame? // Is different? if (view->getSize().x == windowSize.x && view->getSize().y == windowSize.y) { // Nothing to do return; } sf::Vector2f viewSize = sf::Vector2f(windowSize.x, windowSize.y); view->setSize(viewSize); hasViewChanged = true; } TrackingView::TrackingView(ITrackable *trackable, sf::Vector2f freeMoveArea, sf::Vector2f dynamicFollowArea) : target(trackable), freeMoveArea(freeMoveArea), dynamicFollowArea(dynamicFollowArea), view(nullptr), hasViewChanged(false) { marker = new CircleObject(2, sf::Color::Yellow); } sf::Vector2f TrackingView::getSize() const { return view->getSize(); } sf::Vector2f TrackingView::getCenter() const { return view->getCenter(); } void TrackingView::followTarget() { // TODO auto closestPositionInDynamic = getClosestPositionInArea(dynamicFollowArea); marker->coordinates.set(IsometricCoordinates(closestPositionInDynamic)); return; if (isTargetInArea(freeMoveArea)) { // Nothing to do return; } // performHardFollow(); // if (isTargetInArea(dynamicFollowArea)) // { // // Slowly push target back into free area // performDynamicFollow(); // } else // { // // Hard follow // performHardFollow(); // } } bool TrackingView::isTargetInArea(sf::Vector2f areaSize) { // Assuming target is not null // Target specifications auto targetSize = target->getTrackableSize(); auto targetTopLeft = target->getTrackablePosition() - targetSize / 2.f; auto targetBottomRight = targetTopLeft + targetSize; // Free Movement Area auto areaTopLeft = getCenter() - areaSize / 2.f; auto areaBottomRight = areaTopLeft + areaSize; // Check collision of all edges if (targetTopLeft.x < areaTopLeft.x || targetTopLeft.y < areaTopLeft.y || targetBottomRight.x > areaBottomRight.x || targetBottomRight.y > areaBottomRight.y) { return false; } return true; } sf::Vector2f TrackingView::getClosestPositionInArea(sf::Vector2f areaSize) const { // Reduce area to only consider target position, not size auto positionOnlyAreaSize = areaSize - target->getTrackableSize(); // Calculate max-length of rubber to land inside of area sf::Vector2f rubber = getRubber(); // Problem is symmetrical for all corners, so can simplify to use only absolute values and part of the area auto absRubber = abs(rubber); auto cornerArea = positionOnlyAreaSize / 2.f; // Calculate factor for each dimension, that would scale it to the border float xScale = INFINITY; if (absRubber.x > 0) { xScale = cornerArea.x / absRubber.x; } float yScale = INFINITY; if (absRubber.y > 0) { yScale = cornerArea.y / absRubber.y; } float length = std::min(xScale, yScale); return rubber * length; } sf::Vector2f TrackingView::getRubber() const { return normalize(target->getTrackablePosition() - getCenter()); } void TrackingView::performHardFollow() { auto closestPositionInDynamic = getClosestPositionInArea(dynamicFollowArea); auto delta = getCenter() - closestPositionInDynamic; moveCenter(-delta); } void TrackingView::moveCenter(sf::Vector2 delta) { view->move(delta); hasViewChanged = true; } void TrackingView::performDynamicFollow() { auto closestPositionInDynamic = getClosestPositionInArea(freeMoveArea); auto delta = getCenter() - closestPositionInDynamic; moveCenter(delta * VIEW_RUBBER_FOLLOW_SPEED * FRAME_TIME.asSeconds()); } void TrackingView::draw(sf::RenderWindow *window) const { marker->draw(window); }