Fixed directions being inverted. Now differentiating between screen and conventional direction vector
This commit is contained in:
parent
1017fee4d3
commit
f9c592b6e0
16 changed files with 261 additions and 47 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ private:
|
|||
|
||||
public:
|
||||
static IsometricCoordinates worldToIsometric(WorldCoordinates worldCoordinates);
|
||||
static WorldCoordinates isometricToWorld(IsometricCoordinates isometricCoordinates);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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>();
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue