Refined, improved and fixed tracking view

This commit is contained in:
Maximilian Giller 2023-05-25 22:40:26 +02:00
parent bbc74a8cef
commit 8ca3547306
7 changed files with 194 additions and 61 deletions

View file

@ -55,7 +55,7 @@ set(SOURCES
src/utilities/magic_enum.hpp src/utilities/magic_enum.hpp
src/game/player/player_spawner.cpp src/game/player/player_spawner.cpp
src/game/player/player_spawner.hpp src/game/player/player_spawner.hpp
src/game/camera/tracking_area.h) src/game/camera/tracking_area.h src/game/camera/tracking_view_options.hpp)
set(PHYSICS_00_SOURCES set(PHYSICS_00_SOURCES
src/prototypes/physics_00.cpp) src/prototypes/physics_00.cpp)

View file

@ -20,7 +20,13 @@
#define ISOMETRIC_SKEW 0.3f #define ISOMETRIC_SKEW 0.3f
#define WORLD_TO_ISO_SCALE 10.0f #define WORLD_TO_ISO_SCALE 10.0f
#define VIEW_DYNAMIC_FOLLOW_SPEED 2.f // Tracking view defaults
#define DEF_TV_FREE_MOVE_THRESHOLD 0.f
#define DEF_TV_SOFT_FOLLOW_SPEED 2.5f
#define DEF_TV_SOFT_RESIZE_SPEED 5.f
#define DEF_TV_MIN_VIEW_SIZE sf::Vector2f(300, 300)
#define DEF_TV_MAX_VIEW_SIZE sf::Vector2f(0, 0)
#define DEF_TV_VIEW_SIZE_PADDING sf::Vector2f(0.5f, 0.5f)
// Inputs // Inputs
#define JOYSTICK_DEADZONE 0.1f #define JOYSTICK_DEADZONE 0.1f

View file

@ -12,6 +12,11 @@ struct TrackingArea
{ {
return topLeft + (bottomRight - topLeft) / 2.f; return topLeft + (bottomRight - topLeft) / 2.f;
} }
sf::Vector2f getSize() const
{
return bottomRight - topLeft;
}
}; };

View file

@ -1,8 +1,7 @@
#include "tracking_view.h" #include "tracking_view.h"
#include "../../utilities/vector_utils.hpp" #include "../../utilities/vector_utils.hpp"
TrackingView::TrackingView(float freeMoveRadius, float dynamicFollowRadius) : freeMoveRadius(freeMoveRadius), TrackingView::TrackingView(TrackingViewOptions options) : options(options),
dynamicFollowRadius(dynamicFollowRadius),
view(nullptr), view(nullptr),
hasViewChanged(false) hasViewChanged(false)
{ {
@ -27,13 +26,10 @@ void TrackingView::lateUpdate()
processTrackableStates(); processTrackableStates();
// Update size
// TODO: Update size based on length of tracked objects
setSize(Game::getInstance()->window->getSize());
if (!trackables.empty()) if (!trackables.empty())
{ {
followTarget(); followTrackables();
adjustSizeToTrackables();
} }
// Update window if necessary // Update window if necessary
@ -51,18 +47,22 @@ void TrackingView::initializeView()
hasViewChanged = true; hasViewChanged = true;
} }
void TrackingView::setSize(sf::Vector2u windowSize) void TrackingView::setSize(sf::Vector2f newSize)
{ {
// TODO: Maybe listen to resize events instead of checking every frame?
// Is different? // Is different?
if (view->getSize().x == windowSize.x && view->getSize().y == windowSize.y) auto size = getSize();
if (size.x == newSize.x && size.y == newSize.y)
{ {
// Nothing to do // Nothing to do
return; return;
} }
sf::Vector2f viewSize = sf::Vector2f(windowSize.x, windowSize.y); if (options.softResizeSpeed != 0) {
view->setSize(viewSize); // Smooth out transition to new size
newSize = size + (newSize - size) * options.softResizeSpeed * FRAME_TIME.asSeconds();
}
view->setSize(newSize);
hasViewChanged = true; hasViewChanged = true;
} }
@ -76,7 +76,7 @@ sf::Vector2f TrackingView::getCenter() const
return view->getCenter(); return view->getCenter();
} }
void TrackingView::followTarget() void TrackingView::followTrackables()
{ {
auto trackingPoint = getTrackingArea().getCenter(); auto trackingPoint = getTrackingArea().getCenter();
if (DEVELOPER_MODE) if (DEVELOPER_MODE)
@ -87,32 +87,33 @@ void TrackingView::followTarget()
// Calculate distance to target to check how to handle it // Calculate distance to target to check how to handle it
auto currentCenter = view->getCenter(); auto currentCenter = view->getCenter();
auto vectorToTarget = trackingPoint - currentCenter; auto vectorToTarget = trackingPoint - currentCenter;
float distanceToTarget = length(vectorToTarget); auto distanceToTarget = length(vectorToTarget);
if (distanceToTarget <= freeMoveRadius) if (distanceToTarget <= getRadius(options.freeMoveThreshold))
{ {
// Nothing to do // Nothing to do
return; return;
} }
// Move view to place tracking-point at edge of area
auto deltaToDesiredView = normalize(vectorToTarget) * FRAME_TIME.asSeconds();
if (distanceToTarget <= dynamicFollowRadius)
{ // Targets are outside of free move radius, follow them
// Reduce delta to make it less jaring auto deltaToDesiredView = normalize(vectorToTarget);
deltaToDesiredView *= VIEW_DYNAMIC_FOLLOW_SPEED * (distanceToTarget - freeMoveRadius);
} else // Reduce delta to edge of free-move area to make it less jaring
{ deltaToDesiredView *= distanceToTarget - getRadius(options.freeMoveThreshold);
// Hard follow
deltaToDesiredView *= dynamicFollowRadius;
}
moveCenter(deltaToDesiredView); moveCenter(deltaToDesiredView);
} }
void TrackingView::moveCenter(sf::Vector2<float> delta) void TrackingView::moveCenter(sf::Vector2<float> delta)
{ {
if (options.softFollowSpeed != 0)
{
// Soft
delta *= options.softFollowSpeed * FRAME_TIME.asSeconds();
}
view->move(delta); view->move(delta);
hasViewChanged = true; hasViewChanged = true;
} }
@ -141,45 +142,118 @@ void TrackingView::processTrackableStates()
}); });
} }
void TrackingView::setCenter(sf::Vector2<float> newCenter)
{
view->setCenter(newCenter);
hasViewChanged = true;
}
TrackingArea TrackingView::getTrackingArea() const TrackingArea TrackingView::getTrackingArea() const
{ {
sf::Vector2f initialPoints = trackables[0]->getTrackablePosition(); auto initialTrackable = trackables[0];
TrackingArea area = {initialPoints, initialPoints}; 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 // Find min and max point coordinates for x- and y-axis over all trackables
for (auto trackable: trackables) for (auto trackable: trackables)
{ {
auto trackableCoordinates = trackable->getTrackablePosition(); auto trackableCoordinates = trackable->getTrackablePosition();
auto trackableSize = trackable->getTrackableSize(); auto trackableSize = trackable->getTrackableSize();
auto minPointX = trackableCoordinates.x - trackableSize.x / 2.f; auto topLeft = trackableCoordinates - trackableSize / 2.f;
auto maxPointX = trackableCoordinates.x + trackableSize.x / 2.f; auto bottomRight = trackableCoordinates + trackableSize / 2.f;
auto minPointY = trackableCoordinates.y - trackableSize.y / 2.f;
auto maxPointY = trackableCoordinates.y + trackableSize.y / 2.f;
if (minPointX < area.topLeft.x) if (topLeft.x < area.topLeft.x)
{ {
area.topLeft.x = minPointX; area.topLeft.x = topLeft.x;
} }
if (maxPointX > area.bottomRight.x) if (bottomRight.x > area.bottomRight.x)
{ {
area.bottomRight.x = maxPointX; area.bottomRight.x = bottomRight.x;
} }
if (minPointY < area.topLeft.y) if (topLeft.y < area.topLeft.y)
{ {
area.topLeft.y = minPointY; area.topLeft.y = topLeft.y;
} }
if (maxPointY > area.bottomRight.y) if (bottomRight.y > area.bottomRight.y)
{ {
area.bottomRight.y = maxPointY; area.bottomRight.y = bottomRight.y;
} }
} }
return area; 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 currentViewSize = getSize();
auto aspectRatio = currentViewSize.x / currentViewSize.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 viewSize = getSize();
float smallestSide = std::min(viewSize.x, viewSize.y);
// Only half of the side, since we are calculating radius
return smallestSide / 2.f * threshold;
}

View file

@ -6,14 +6,14 @@
#include "ITrackable.h" #include "ITrackable.h"
#include "../../primitives/circle_object.h" #include "../../primitives/circle_object.h"
#include "tracking_area.h" #include "tracking_area.h"
#include "tracking_view_options.hpp"
class CircleObject; class CircleObject;
class TrackingView : public GameObject class TrackingView : public GameObject
{ {
public: public:
explicit TrackingView(float freeMoveRadius = 0, explicit TrackingView(TrackingViewOptions options = TrackingViewOptions());
float dynamicFollowRadius = 300);
~TrackingView(); ~TrackingView();
@ -31,17 +31,14 @@ public:
private: private:
sf::View *view{}; sf::View *view{};
bool hasViewChanged{}; bool hasViewChanged{};
float freeMoveRadius; TrackingViewOptions options;
float dynamicFollowRadius;
std::vector<ITrackable *> trackables; std::vector<ITrackable *> trackables;
CircleObject *marker; CircleObject *marker;
void initializeView(); void initializeView();
void setSize(sf::Vector2u windowSize); void followTrackables();
void followTarget();
void moveCenter(sf::Vector2<float> delta); void moveCenter(sf::Vector2<float> delta);
@ -49,7 +46,15 @@ private:
void processTrackableStates(); void processTrackableStates();
void setCenter(sf::Vector2<float> newCenter); void adjustSizeToTrackables();
void setSize(sf::Vector2f newSize);
sf::Vector2f applyPadding(sf::Vector2f viewSize) const;
sf::Vector2f restrainToBounds(sf::Vector2f viewSize) const;
float getRadius(float threshold) const;
}; };

View file

@ -0,0 +1,42 @@
#ifndef HOLESOME_TRACKING_VIEW_OPTIONS_HPP
#define HOLESOME_TRACKING_VIEW_OPTIONS_HPP
#include <SFML/System/Vector2.hpp>
#include "../../config.h"
struct TrackingViewOptions
{
/**
* Value >1 to set pixel radius.
* Value between 0 and 1 to set relative radius based on smallest half-axis-size.
*/
float freeMoveThreshold = DEF_TV_FREE_MOVE_THRESHOLD;
/**
* 0 for instant follow.
*/
float softFollowSpeed = DEF_TV_SOFT_FOLLOW_SPEED;
/**
* 0 for instant resize.
*/
float softResizeSpeed = DEF_TV_SOFT_RESIZE_SPEED;
/**
* If set to 0, view will not be limited.
*/
sf::Vector2f minViewSize = DEF_TV_MIN_VIEW_SIZE;
/**
* If set to 0, view will not be limited.
*/
sf::Vector2f maxViewSize = DEF_TV_MAX_VIEW_SIZE;
/**
* Will be added to tracked area size twice, as padding for each side.
* Value >1 to set pixel padding.
* Value between 0 and 1 to set relative padding.
*/
sf::Vector2f viewSizePadding = DEF_TV_VIEW_SIZE_PADDING;
};
#endif //HOLESOME_TRACKING_VIEW_OPTIONS_HPP

View file

@ -10,12 +10,13 @@ sf::Vector2f Player::getTrackablePosition() const
sf::Vector2f Player::getTrackableSize() const sf::Vector2f Player::getTrackableSize() const
{ {
// TODO: Proper implementation // TODO: Proper implementation
return {static_cast<float>(circle->getRadius() * 2), static_cast<float>(circle->getRadius() * 2)}; return {circle->getRadius() * 2.f, circle->getRadius() * 2.f};
} }
void Player::update() void Player::update()
{ {
if (!input->isActive) { if (!input->isActive)
{
isActive = false; isActive = false;
return; return;
} }