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/game/player/player_spawner.cpp
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
src/prototypes/physics_00.cpp)

View file

@ -20,7 +20,13 @@
#define ISOMETRIC_SKEW 0.3f
#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
#define JOYSTICK_DEADZONE 0.1f

View file

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

View file

@ -1,8 +1,7 @@
#include "tracking_view.h"
#include "../../utilities/vector_utils.hpp"
TrackingView::TrackingView(float freeMoveRadius, float dynamicFollowRadius) : freeMoveRadius(freeMoveRadius),
dynamicFollowRadius(dynamicFollowRadius),
TrackingView::TrackingView(TrackingViewOptions options) : options(options),
view(nullptr),
hasViewChanged(false)
{
@ -27,13 +26,10 @@ void TrackingView::lateUpdate()
processTrackableStates();
// Update size
// TODO: Update size based on length of tracked objects
setSize(Game::getInstance()->window->getSize());
if (!trackables.empty())
{
followTarget();
followTrackables();
adjustSizeToTrackables();
}
// Update window if necessary
@ -51,18 +47,22 @@ void TrackingView::initializeView()
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?
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
return;
}
sf::Vector2f viewSize = sf::Vector2f(windowSize.x, windowSize.y);
view->setSize(viewSize);
if (options.softResizeSpeed != 0) {
// Smooth out transition to new size
newSize = size + (newSize - size) * options.softResizeSpeed * FRAME_TIME.asSeconds();
}
view->setSize(newSize);
hasViewChanged = true;
}
@ -76,7 +76,7 @@ sf::Vector2f TrackingView::getCenter() const
return view->getCenter();
}
void TrackingView::followTarget()
void TrackingView::followTrackables()
{
auto trackingPoint = getTrackingArea().getCenter();
if (DEVELOPER_MODE)
@ -87,32 +87,33 @@ void TrackingView::followTarget()
// Calculate distance to target to check how to handle it
auto currentCenter = view->getCenter();
auto vectorToTarget = trackingPoint - currentCenter;
float distanceToTarget = length(vectorToTarget);
auto distanceToTarget = length(vectorToTarget);
if (distanceToTarget <= freeMoveRadius)
if (distanceToTarget <= getRadius(options.freeMoveThreshold))
{
// Nothing to do
return;
}
// Move view to place tracking-point at edge of area
auto deltaToDesiredView = normalize(vectorToTarget) * FRAME_TIME.asSeconds();
if (distanceToTarget <= dynamicFollowRadius)
{
// Reduce delta to make it less jaring
deltaToDesiredView *= VIEW_DYNAMIC_FOLLOW_SPEED * (distanceToTarget - freeMoveRadius);
} else
{
// Hard follow
deltaToDesiredView *= dynamicFollowRadius;
}
// 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;
}
@ -141,45 +142,118 @@ void TrackingView::processTrackableStates()
});
}
void TrackingView::setCenter(sf::Vector2<float> newCenter)
{
view->setCenter(newCenter);
hasViewChanged = true;
}
TrackingArea TrackingView::getTrackingArea() const
{
sf::Vector2f initialPoints = trackables[0]->getTrackablePosition();
TrackingArea area = {initialPoints, initialPoints};
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
// 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;
auto topLeft = trackableCoordinates - trackableSize / 2.f;
auto bottomRight = trackableCoordinates + trackableSize / 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;
}
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 "../../primitives/circle_object.h"
#include "tracking_area.h"
#include "tracking_view_options.hpp"
class CircleObject;
class TrackingView : public GameObject
{
public:
explicit TrackingView(float freeMoveRadius = 0,
float dynamicFollowRadius = 300);
explicit TrackingView(TrackingViewOptions options = TrackingViewOptions());
~TrackingView();
@ -31,17 +31,14 @@ public:
private:
sf::View *view{};
bool hasViewChanged{};
float freeMoveRadius;
float dynamicFollowRadius;
TrackingViewOptions options;
std::vector<ITrackable *> trackables;
CircleObject *marker;
void initializeView();
void setSize(sf::Vector2u windowSize);
void followTarget();
void followTrackables();
void moveCenter(sf::Vector2<float> delta);
@ -49,7 +46,15 @@ private:
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
{
// 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()
{
if (!input->isActive) {
if (!input->isActive)
{
isActive = false;
return;
}