Fixed directions being inverted. Now differentiating between screen and conventional direction vector

This commit is contained in:
Maximilian Giller 2023-05-14 22:23:25 +02:00
parent 1017fee4d3
commit f9c592b6e0
16 changed files with 261 additions and 47 deletions

View file

@ -18,8 +18,10 @@
#define ISOMETRIC_SKEW 0.3f
#define WORLD_TO_ISO_SCALE 10.0f
#define VIEW_RUBBER_FOLLOW_SPEED 0.5f
// Directions
#define DIRECTION_HARD_ACTIVATION_THRESHOLD 0.45f
#define DIRECTION_HARD_ACTIVATION_THRESHOLD 0.1f
// DEBUG

View file

@ -4,7 +4,7 @@
// Initialize matrix
const Eigen::Matrix<float, 3, 3> CoordinateTransformer::worldToIsometricMatrix =
(Eigen::Matrix<float, 3, 3>() <<
-1, 1, 0,
1, -1, 0,
-ISOMETRIC_SKEW, -ISOMETRIC_SKEW, -1,
1, 1, 0
).finished();
@ -22,3 +22,17 @@ IsometricCoordinates CoordinateTransformer::worldToIsometric(WorldCoordinates wo
isoCoordinatesVector.z() // depth
);
}
WorldCoordinates CoordinateTransformer::isometricToWorld(IsometricCoordinates isometricCoordinates)
{
Eigen::Vector3f isoCoordinatesVector;
isoCoordinatesVector << isometricCoordinates.x, isometricCoordinates.y, isometricCoordinates.depth;
Eigen::Vector3f worldCoordinatesVector = worldToIsometricMatrix.inverse() * isoCoordinatesVector / WORLD_TO_ISO_SCALE;
return {
worldCoordinatesVector.x(), // x
worldCoordinatesVector.y(), // y
worldCoordinatesVector.z() // z
};
}

View file

@ -11,6 +11,7 @@ private:
public:
static IsometricCoordinates worldToIsometric(WorldCoordinates worldCoordinates);
static WorldCoordinates isometricToWorld(IsometricCoordinates isometricCoordinates);
};

View file

@ -66,6 +66,14 @@ struct IsometricCoordinates
{
return {x, y};
}
IsometricCoordinates() = default;
IsometricCoordinates(float x, float y, float depth = 0) : x(x), y(y), depth(depth)
{}
explicit IsometricCoordinates (sf::Vector2f vector) : x(vector.x), y(vector.y), depth(0)
{}
};
struct GridCoordinates

View file

@ -1,7 +1,3 @@
//
// Created by max on 27.04.23.
//
#include "translated_coordinates.h"
WorldCoordinates TranslatedCoordinates::world() const {
@ -9,7 +5,7 @@ WorldCoordinates TranslatedCoordinates::world() const {
}
IsometricCoordinates TranslatedCoordinates::isometric() const {
return isoCoordTransformer->worldToIsometric(worldCoordinates);
return coordTransformer->worldToIsometric(worldCoordinates);
}
GridCoordinates TranslatedCoordinates::grid() const {
@ -21,7 +17,11 @@ void TranslatedCoordinates::set(WorldCoordinates newWorldCoordinates) {
this->worldCoordinates = newWorldCoordinates;
}
void TranslatedCoordinates::set(TranslatedCoordinates newCoordinates) {
void TranslatedCoordinates::set(IsometricCoordinates newIsometricCoordinates) {
this->worldCoordinates = coordTransformer->isometricToWorld(newIsometricCoordinates);
}
void TranslatedCoordinates::set(const TranslatedCoordinates& newCoordinates) {
this->worldCoordinates = newCoordinates.world();
}

View file

@ -24,7 +24,9 @@ public:
void set(WorldCoordinates newWorldCoordinates);
void set(TranslatedCoordinates newCoordinates);
void set(const TranslatedCoordinates& newCoordinates);
void set(IsometricCoordinates newIsometricCoordinates);
void move(WorldCoordinates deltaWorldCoordinates);
@ -33,7 +35,7 @@ public:
private:
WorldCoordinates worldCoordinates;
const float worldToGridFactor = INITIAL_WORLD_TO_GRID_FACTOR;
const std::shared_ptr<CoordinateTransformer> isoCoordTransformer = std::make_shared<CoordinateTransformer>();
const std::shared_ptr<CoordinateTransformer> coordTransformer = std::make_shared<CoordinateTransformer>();
};

View file

@ -9,7 +9,20 @@ GridDebugLayer::GridDebugLayer(int minX, int maxX, int minY, int maxY)
{
for (int y = minY; y <= maxY; y++)
{
auto *gameObject = new CircleObject(DB_ISOPLANE_CORNER_RADIUS, sf::Color::Red);
auto color = sf::Color(128, 128, 128);
if (x == 0 && y != 0)
{
// Y-axis
color = sf::Color::Red;
}
else if (y == 0)
{
// X-axis
color = sf::Color::Blue;
}
auto *gameObject = new CircleObject(DB_ISOPLANE_CORNER_RADIUS, color);
gameObject->coordinates.set(WorldCoordinates(x, y, 0));
marker.push_back(gameObject);
}

View file

@ -63,8 +63,15 @@ void Game::addGameObject(GameObject *gameObject)
void Game::update()
{
// Basic Updates
for (auto &gameObject: gameObjects)
{
gameObject->update(this);
}
// Late updates
for (auto &gameObject: gameObjects)
{
gameObject->lateUpdate(this);
}
}

View file

@ -10,12 +10,19 @@
class Game;
class GameObject {
class GameObject
{
public:
GameObject();
virtual void draw(sf::RenderWindow *window) const = 0;
virtual void update(Game *game) = 0;
virtual void draw(sf::RenderWindow *window) const
{}
virtual void update(Game *game)
{}
virtual void lateUpdate(Game *game)
{}
TranslatedCoordinates coordinates;
};

View file

@ -21,6 +21,13 @@ HardDirection Direction::getKeyDirection(sf::Keyboard::Key key)
return map[key];
}
sf::Vector2f Direction::getScreenVector(HardDirection direction)
{
auto vector = getVector(direction);
vector.y *= -1;
return vector;
}
sf::Vector2f Direction::getVector(HardDirection direction)
{
auto vector = sf::Vector2f(0.0f, 0.0f);
@ -33,11 +40,11 @@ sf::Vector2f Direction::getVector(HardDirection direction)
// Combine all relevant directions into one vector
if (direction & HardDirection::UP)
{
vector.y -= 1;
vector.y += 1;
}
if (direction & HardDirection::DOWN)
{
vector.y += 1;
vector.y -= 1;
}
if (direction & HardDirection::LEFT)
{
@ -51,7 +58,7 @@ sf::Vector2f Direction::getVector(HardDirection direction)
return normalize(vector);
}
HardDirection Direction::getHardDirection(sf::Vector2f vector)
HardDirection Direction::getHardDirectionFromVector(sf::Vector2f vector)
{
HardDirection direction = HardDirection::NONE;
vector = normalize(vector);
@ -91,7 +98,7 @@ sf::Vector2f Direction::asVector() const
HardDirection Direction::asHardDirection() const
{
return getHardDirection(directionVector);
return getHardDirectionFromVector(directionVector);
}
Direction::Direction(sf::Vector2f directionVector)
@ -103,3 +110,10 @@ Direction::Direction(HardDirection direction)
{
this->directionVector = getVector(direction);
}
sf::Vector2f Direction::asScreenVector() const
{
auto screenVector = directionVector;
screenVector.y *= -1;
return screenVector;
}

View file

@ -23,11 +23,13 @@ public:
static HardDirection getKeyDirection(sf::Keyboard::Key key);
static sf::Vector2f getVector(HardDirection direction);
static sf::Vector2f getScreenVector(HardDirection direction);
static HardDirection getHardDirection(sf::Vector2f vector);
static HardDirection getHardDirectionFromVector(sf::Vector2f vector);
public:
sf::Vector2f asVector() const;
sf::Vector2f asScreenVector() const;
HardDirection asHardDirection() const;

View file

@ -14,7 +14,7 @@ sf::Vector2f Player::getTrackableSize() const
void Player::update(Game *game)
{
auto moveDirection = InputMapper::getInputDirection().asVector();
auto moveDelta = moveDirection * 10.0f * FRAME_TIME.asSeconds();
auto moveDelta = moveDirection * 30.0f * FRAME_TIME.asSeconds();
coordinates.move(moveDelta);
circle->coordinates.set(coordinates);
}
@ -33,6 +33,6 @@ Player::Player(const sf::Color color, WorldCoordinates initCoordinates)
{
coordinates.set(initCoordinates);
circle = new CircleObject(50, color);
circle = new CircleObject(10, color);
circle->coordinates.set(coordinates);
}

View file

@ -1,19 +1,13 @@
#include "world_view.h"
WorldView::WorldView() : view(nullptr), hasViewChanged(false)
{
}
#include "../../utilities/vector_utils.hpp"
WorldView::~WorldView()
{
delete view;
delete marker;
}
void WorldView::draw(sf::RenderWindow *window) const
{
}
void WorldView::update(Game *game)
void WorldView::lateUpdate(Game *game)
{
// Initialize if necessary
if (view == nullptr)
@ -24,8 +18,10 @@ void WorldView::update(Game *game)
// Update size
setSize(game->window->getSize());
// Update position
moveViewByControls();
if (target != nullptr)
{
followTarget();
}
// Update window if necessary
if (hasViewChanged)
@ -37,28 +33,147 @@ void WorldView::update(Game *game)
void WorldView::initializeView(Game *game)
{
auto center = game->window->getView().getCenter();
auto size = game->window->getView().getSize();
view = new sf::View(center, size);
view = new sf::View({0, 0}, size);
}
void WorldView::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;
}
void WorldView::moveViewByControls()
WorldView::WorldView(ITrackable *trackable, sf::Vector2f freeMoveArea, sf::Vector2f dynamicFollowArea)
: target(trackable), freeMoveArea(freeMoveArea), dynamicFollowArea(dynamicFollowArea), view(nullptr),
hasViewChanged(false)
{
auto moveDirection = InputMapper::getInputDirection().asHardDirection();
if (moveDirection == HardDirection::NONE)
marker = new CircleObject(2, sf::Color::Yellow);
}
sf::Vector2f WorldView::getSize() const
{
return view->getSize();
}
sf::Vector2f WorldView::getCenter() const
{
return view->getCenter();
}
void WorldView::followTarget()
{
auto closestPositionInDynamic = getClosestPositionInArea(dynamicFollowArea);
marker->coordinates.set(IsometricCoordinates(closestPositionInDynamic));
return;
if (isTargetInArea(freeMoveArea))
{
// Nothing to do
return;
}
float stepSize = 100.0f * FRAME_TIME.asSeconds();
auto delta = stepSize * Direction::getVector(moveDirection);
this->view->move(delta);
// performHardFollow();
// if (isTargetInArea(dynamicFollowArea))
// {
// // Slowly push target back into free area
// performDynamicFollow();
// } else
// {
// // Hard follow
// performHardFollow();
// }
}
bool WorldView::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 WorldView::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 WorldView::getRubber() const
{
return normalize(target->getTrackablePosition() - getCenter());
}
void WorldView::performHardFollow()
{
auto closestPositionInDynamic = getClosestPositionInArea(dynamicFollowArea);
auto delta = getCenter() - closestPositionInDynamic;
moveCenter(-delta);
}
void WorldView::moveCenter(sf::Vector2<float> delta)
{
view->move(delta);
hasViewChanged = true;
}
void WorldView::performDynamicFollow()
{
auto closestPositionInDynamic = getClosestPositionInArea(freeMoveArea);
auto delta = getCenter() - closestPositionInDynamic;
moveCenter(delta * VIEW_RUBBER_FOLLOW_SPEED * FRAME_TIME.asSeconds());
}
void WorldView::draw(sf::RenderWindow *window) const
{
marker->draw(window);
}

View file

@ -4,27 +4,53 @@
#include "../game_object.h"
#include "../game.h"
#include "ITrackable.h"
#include "../../primitives/circle_object.h"
class WorldView : public GameObject
{
public:
explicit WorldView();
explicit WorldView(ITrackable *trackable = nullptr,
sf::Vector2f freeMoveArea = {200, 200},
sf::Vector2f dynamicFollowArea = {500, 500});
~WorldView();
void draw(sf::RenderWindow *window) const override;
void update(Game *game) override;
void draw(sf::RenderWindow *window) const override;
void lateUpdate(Game *game) override;
sf::Vector2f getSize() const;
sf::Vector2f getCenter() const;
private:
sf::View *view;
bool hasViewChanged;
sf::View *view{};
bool hasViewChanged{};
sf::Vector2f freeMoveArea;
sf::Vector2f dynamicFollowArea;
ITrackable *target;
CircleObject *marker;
void initializeView(Game *game);
void setSize(sf::Vector2u windowSize);
void moveViewByControls();
void followTarget();
bool isTargetInArea(sf::Vector2f areaSize);
sf::Vector2f getClosestPositionInArea(sf::Vector2f areaSize) const;
/// Calculates the direction the target should be pulled back in.
/// \return Normalized vector, pointing from center to target.
sf::Vector2f getRubber() const;
void performHardFollow();
void moveCenter(sf::Vector2<float> delta);
void performDynamicFollow();
};

View file

@ -14,9 +14,11 @@ int main(int argc, char *argv[])
auto game = GameFactory::createWindowed("Holesome");
auto player = new Player(sf::Color::Green, {0, 0});
game->addGameObject(new GridDebugLayer(0, 50, 0, 50));
game->addGameObject(player);
game->addGameObject(new WorldView());
game->addGameObject(new Player(sf::Color::Blue, WorldCoordinates(0, 0)));
game->run();
}

View file

@ -8,6 +8,7 @@ template<typename T>
sf::Vector2<T> normalize(sf::Vector2<T> v)
{
auto length = std::sqrt(v.x * v.x + v.y * v.y);
if (length != 0)
{
v.x /= length;