312 lines
7.7 KiB
C++
312 lines
7.7 KiB
C++
#include "tracking_view.h"
|
|
#include "../../utilities/vector_utils.hpp"
|
|
#include "../player/player_collection.hpp"
|
|
|
|
TrackingView::TrackingView(TrackingViewOptions options) : options(options),
|
|
view(nullptr),
|
|
hasViewChanged(false),
|
|
trackables({})
|
|
{
|
|
;
|
|
marker = new CircleObject(DB_CIRCLE_RADIUS, sf::Color::Yellow);
|
|
Game::getInstance()->registerView(this);
|
|
}
|
|
|
|
TrackingView::~TrackingView()
|
|
{
|
|
delete view;
|
|
delete marker;
|
|
}
|
|
|
|
void TrackingView::lateUpdate()
|
|
{
|
|
// Initialize if necessary
|
|
if (view == nullptr)
|
|
{
|
|
initializeView();
|
|
}
|
|
|
|
processTrackableStates();
|
|
|
|
if (!trackables.empty())
|
|
{
|
|
followTrackables();
|
|
adjustSizeToTrackables();
|
|
}
|
|
|
|
// 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::Vector2f newSize)
|
|
{
|
|
// Is different?
|
|
auto size = getSize();
|
|
if (size.x == newSize.x && size.y == newSize.y)
|
|
{
|
|
// Nothing to do
|
|
return;
|
|
}
|
|
|
|
bool didAspectRationChange = false;
|
|
auto prevRatio = size.x / size.y;
|
|
auto newRatio = newSize.x / newSize.y;
|
|
if (abs(prevRatio - newRatio) > 0.001)
|
|
{
|
|
didAspectRationChange = true;
|
|
}
|
|
|
|
if (options.softResizeSpeed != 0 && !didAspectRationChange)
|
|
{
|
|
// Smooth out transition to new size
|
|
newSize = size + (newSize - size) * options.softResizeSpeed * FRAME_TIME.asSeconds();
|
|
}
|
|
|
|
view->setSize(newSize);
|
|
hasViewChanged = true;
|
|
}
|
|
|
|
sf::Vector2f TrackingView::getSize() const
|
|
{
|
|
return view->getSize();
|
|
}
|
|
|
|
sf::Vector2f TrackingView::getWindowSize() const
|
|
{
|
|
auto size = Game::getInstance()->window->getSize();
|
|
return {static_cast<float>(size.x), static_cast<float>(size.y)};
|
|
}
|
|
|
|
sf::Vector2f TrackingView::getCenter() const
|
|
{
|
|
return view->getCenter();
|
|
}
|
|
|
|
void TrackingView::followTrackables()
|
|
{
|
|
auto trackingPoint = getTrackingArea().getCenter();
|
|
if (DEVELOPER_MODE)
|
|
{
|
|
marker->coordinates->setIsometric(IsometricCoordinates(trackingPoint));
|
|
}
|
|
|
|
// Calculate distance to target to check how to handle it
|
|
auto currentCenter = view->getCenter();
|
|
auto vectorToTarget = trackingPoint - currentCenter;
|
|
auto distanceToTarget = length(vectorToTarget);
|
|
|
|
if (distanceToTarget <= getRadius(options.freeMoveThreshold))
|
|
{
|
|
// Nothing to do
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
// Targets are outside of free move radius, follow them
|
|
auto deltaToDesiredView = normalize(vectorToTarget);
|
|
|
|
// Reduce delta to edge of free-move area to make it less jaring
|
|
deltaToDesiredView *= distanceToTarget - getRadius(options.freeMoveThreshold);
|
|
|
|
moveCenter(deltaToDesiredView);
|
|
}
|
|
|
|
void TrackingView::moveCenter(sf::Vector2<float> delta)
|
|
{
|
|
if (options.softFollowSpeed != 0)
|
|
{
|
|
// Soft
|
|
delta *= options.softFollowSpeed * FRAME_TIME.asSeconds();
|
|
}
|
|
|
|
view->move(delta);
|
|
hasViewChanged = true;
|
|
}
|
|
|
|
void TrackingView::draw(sf::RenderWindow *window)
|
|
{
|
|
if (!DEVELOPER_MODE)
|
|
{
|
|
return;
|
|
}
|
|
|
|
marker->draw(window);
|
|
}
|
|
|
|
void TrackingView::addTrackable(const std::shared_ptr<ITrackable> &trackable)
|
|
{
|
|
trackables.push_back(trackable);
|
|
}
|
|
|
|
void TrackingView::processTrackableStates()
|
|
{
|
|
// Remove trackables that have ended tracking
|
|
std::remove_if(trackables.begin(), trackables.end(), [](std::shared_ptr<ITrackable> trackable)
|
|
{
|
|
return trackable->getTrackableState() == TrackableState::END_TRACKING;
|
|
});
|
|
}
|
|
|
|
TrackingArea TrackingView::getTrackingArea() const
|
|
{
|
|
auto initialTrackable = trackables[0];
|
|
TrackingArea area = {
|
|
initialTrackable->getTrackablePosition() - initialTrackable->getTrackableSize() / 2.f,
|
|
initialTrackable->getTrackablePosition() + initialTrackable->getTrackableSize() / 2.f
|
|
};
|
|
|
|
// 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 topLeft = trackableCoordinates - trackableSize / 2.f;
|
|
auto bottomRight = trackableCoordinates + trackableSize / 2.f;
|
|
|
|
if (topLeft.x < area.topLeft.x)
|
|
{
|
|
area.topLeft.x = topLeft.x;
|
|
}
|
|
if (bottomRight.x > area.bottomRight.x)
|
|
{
|
|
area.bottomRight.x = bottomRight.x;
|
|
}
|
|
if (topLeft.y < area.topLeft.y)
|
|
{
|
|
area.topLeft.y = topLeft.y;
|
|
}
|
|
if (bottomRight.y > area.bottomRight.y)
|
|
{
|
|
area.bottomRight.y = bottomRight.y;
|
|
}
|
|
}
|
|
|
|
return area;
|
|
}
|
|
|
|
void TrackingView::adjustSizeToTrackables()
|
|
{
|
|
// Calculate new view size
|
|
auto newViewSize = getTrackingArea().getSize();
|
|
|
|
newViewSize = applyPadding(newViewSize);
|
|
newViewSize = restrainToBounds(newViewSize);
|
|
|
|
// Extend view to match aspect ratio
|
|
auto windowSize = getWindowSize();
|
|
auto aspectRatio = windowSize.x / windowSize.y;
|
|
if (newViewSize.x / newViewSize.y > aspectRatio)
|
|
{
|
|
// Extend x-axis
|
|
newViewSize.y = newViewSize.x / aspectRatio;
|
|
} else
|
|
{
|
|
// Extend y-axis
|
|
newViewSize.x = newViewSize.y * aspectRatio;
|
|
}
|
|
|
|
setSize(newViewSize);
|
|
}
|
|
|
|
sf::Vector2f TrackingView::restrainToBounds(sf::Vector2f viewSize) const
|
|
{
|
|
// x-axis
|
|
if (options.minViewSize.x > 0 && viewSize.x < options.minViewSize.x)
|
|
{
|
|
viewSize.x = options.minViewSize.x;
|
|
} else if (options.maxViewSize.x > 0 && viewSize.x > options.maxViewSize.x)
|
|
{
|
|
viewSize.x = options.maxViewSize.x;
|
|
}
|
|
// y-axis
|
|
if (options.minViewSize.y > 0 && viewSize.y < options.minViewSize.y)
|
|
{
|
|
viewSize.y = options.minViewSize.y;
|
|
} else if (options.maxViewSize.y > 0 && viewSize.y > options.maxViewSize.y)
|
|
{
|
|
viewSize.y = options.maxViewSize.y;
|
|
}
|
|
return viewSize;
|
|
}
|
|
|
|
sf::Vector2f TrackingView::applyPadding(sf::Vector2f viewSize) const
|
|
{
|
|
auto padding = options.viewSizePadding;
|
|
if (padding.x <= 1)
|
|
{
|
|
padding.x *= viewSize.x;
|
|
}
|
|
if (padding.y <= 1)
|
|
{
|
|
padding.y *= viewSize.y;
|
|
}
|
|
return viewSize + padding;
|
|
}
|
|
|
|
float TrackingView::getRadius(float threshold) const
|
|
{
|
|
if (threshold <= 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (threshold > 1)
|
|
{
|
|
return threshold;
|
|
}
|
|
|
|
auto windowSize = getWindowSize();
|
|
float smallestSide = std::min(windowSize.x, windowSize.y);
|
|
|
|
// Only half of the side, since we are calculating radius
|
|
return smallestSide / 2.f * threshold;
|
|
}
|
|
|
|
void TrackingView::update()
|
|
{
|
|
if (options.addPlayersDynamically)
|
|
{
|
|
addPlayersDynamically();
|
|
}
|
|
}
|
|
|
|
void TrackingView::addPlayersDynamically()
|
|
{
|
|
// Update based on PlayerCollection
|
|
// Add new
|
|
for (const auto &player: PlayerCollection::getInstance()->getNewPlayers())
|
|
{
|
|
if (player->getTrackableState() == TrackableState::TRACKING)
|
|
{
|
|
addTrackable(player);
|
|
}
|
|
}
|
|
|
|
// Remove old
|
|
for (const auto &player: PlayerCollection::getInstance()->getRemovedPlayers())
|
|
{
|
|
removeTrackable(player);
|
|
}
|
|
}
|
|
|
|
void TrackingView::removeTrackable(const std::shared_ptr<ITrackable> &trackable)
|
|
{
|
|
trackables.erase(
|
|
std::remove_if(trackables.begin(), trackables.end(), [&trackable](const std::shared_ptr<ITrackable> &t)
|
|
{
|
|
return t == trackable;
|
|
}), trackables.end());
|
|
}
|