#include "tracking_view.h" #include "../../utilities/vector_utils.hpp" TrackingView::TrackingView(float freeMoveRadius, float dynamicFollowRadius) : freeMoveRadius(freeMoveRadius), dynamicFollowRadius(dynamicFollowRadius), view(nullptr), hasViewChanged(false) { trackables = std::vector(); marker = new CircleObject(2, sf::Color::Yellow); Game::getInstance()->registerView(this); } TrackingView::~TrackingView() { delete view; delete marker; } void TrackingView::lateUpdate() { // Initialize if necessary if (view == nullptr) { initializeView(); } processTrackableStates(); // Update size // TODO: Update size based on length of tracked objects setSize(Game::getInstance()->window->getSize()); if (!trackables.empty()) { followTarget(); } // Update window if necessary if (hasViewChanged) { Game::getInstance()->window->setView(*this->view); hasViewChanged = false; } } void TrackingView::initializeView() { auto size = Game::getInstance()->window->getView().getSize(); view = new sf::View({0, 0}, size); hasViewChanged = true; } 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; } sf::Vector2f TrackingView::getSize() const { return view->getSize(); } sf::Vector2f TrackingView::getCenter() const { return view->getCenter(); } void TrackingView::followTarget() { auto trackingPoint = getCollectiveTrackingPoint(); if (DEVELOPER_MODE) { marker->coordinates.set(IsometricCoordinates(trackingPoint)); } // Calculate distance to target to check how to handle it auto currentCenter = view->getCenter(); auto vectorToTarget = trackingPoint - currentCenter; float distanceToTarget = length(vectorToTarget); if (distanceToTarget <= freeMoveRadius) { // Nothing to do return; } // Move view to place tracking-point at edge of area auto deltaToDesiredView = normalize(vectorToTarget); if (distanceToTarget <= dynamicFollowRadius) { // Reduce delta to make it less jaring deltaToDesiredView *= VIEW_DYNAMIC_FOLLOW_SPEED * (distanceToTarget - freeMoveRadius); } else { // Hard follow deltaToDesiredView *= dynamicFollowRadius; } moveCenter(deltaToDesiredView); } void TrackingView::moveCenter(sf::Vector2 delta) { view->move(delta); hasViewChanged = true; } void TrackingView::draw(sf::RenderWindow *window) const { if (!DEVELOPER_MODE) { return; } marker->draw(window); } void TrackingView::addTrackable(ITrackable *trackable) { trackables.push_back(trackable); } sf::Vector2f TrackingView::getCollectiveTrackingPoint() const { sf::Vector2f minPoint = trackables[0]->getTrackablePosition(); sf::Vector2f maxPoint = minPoint; // Find min and max point coordinates for x and y axis over all trackables for (auto trackable : trackables) { auto trackableCoordinates = trackable->getTrackablePosition(); auto trackableSize = trackable->getTrackableSize(); auto minPointX = trackableCoordinates.x - trackableSize.x / 2.f; auto maxPointX = trackableCoordinates.x + trackableSize.x / 2.f; auto minPointY = trackableCoordinates.y - trackableSize.y / 2.f; auto maxPointY = trackableCoordinates.y + trackableSize.y / 2.f; if (minPointX < minPoint.x) { minPoint.x = minPointX; } if (maxPointX > maxPoint.x) { maxPoint.x = maxPointX; } if (minPointY < minPoint.y) { minPoint.y = minPointY; } if (maxPointY > maxPoint.y) { maxPoint.y = maxPointY; } } return minPoint + (maxPoint - minPoint) / 2.f; } void TrackingView::processTrackableStates() { // Remove trackables that have ended tracking std::remove_if(trackables.begin(), trackables.end(), [](ITrackable *trackable) { return trackable->getTrackableState() == TrackableState::END_TRACKING; }); } void TrackingView::setCenter(sf::Vector2 newCenter) { view->setCenter(newCenter); hasViewChanged = true; }