Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Is there an easy way to deal with diagonal staircase movement?  (Read 2004 times)

0 Members and 1 Guest are viewing this topic.

Elias Daler

  • Hero Member
  • *****
  • Posts: 599
    • View Profile
    • Blog
    • Email
Is there an easy way to deal with diagonal staircase movement?
« on: February 03, 2018, 10:56:49 am »
Here's a short picture I drew to explain the problem


Previously I used std::round to round real coordinates to render coordinates. It worked pretty great, until I noticed that if something moves with 0.5 increments, the movement becomes quite jerky. The only solution I've found so far is to take velocity into account and do std::floor(pos.x) if v.x > 0 and std::ceil(pos.y) if v.x < 0 (same for v.y and y-coordinate)

Note that I only have to do it if coordinate is exactly between two pixels (e.g. 0.5, 1.5, 2.5, etc.). For other coordinates it works perfectly well with std::round.

Any easier solution to this? (Note that my game is very low-res, so I can't use bilinear filtering or something like this).
Tomb Painter, Re:creation dev (abandoned, doing other things) | edw.is | @EliasDaler

Elias Daler

  • Hero Member
  • *****
  • Posts: 599
    • View Profile
    • Blog
    • Email
Re: Is there an easy way to deal with diagonal staircase movement?
« Reply #1 on: February 03, 2018, 01:59:51 pm »
Examples!

1) Rounding position. Notice the staircase movement
#include <SFML/Graphics.hpp>
#include <cmath>

int main()
{
    sf::View view({0, 0, 128, 128});
    sf::RenderWindow window({ 512,512 }, "SFML works!");
    window.setFramerateLimit(1);
    window.setView(view);

    sf::RectangleShape shape({50.f, 50.f});
    shape.setFillColor(sf::Color::Green);

    sf::Clock clock;

    sf::Vector2f pos{32.f, 0.f};

    while (window.isOpen())
    {
        auto dt = clock.restart();

        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();
        }

        pos += { -0.5f, 0.5f };
        shape.setPosition({ std::round(pos.x + 0.25f), std::round(pos.y + 0.25f) });

        window.clear();
        window.draw(shape);
        window.display();
    }
}

2) Using my overly complicated solution which works fine! So, the only thing I'm hoping is for someone to make it easier. :)
#include <SFML/Graphics.hpp>
#include <cmath>

float roundCoordinate(float c, float v)
{
    float intPart;
    if (std::abs(std::modf(c, &intPart)) == 0.5f) {
        if (v <= 0.f) {
            c = std::ceil(c);
        } else {
            c = std::floor(c);
        }
    } else {
        c = std::round(c);
    }
    return c;
}

sf::Vector2f getRoundedPos(const sf::Vector2f& pos, const sf::Vector2f& v)
{
    sf::Vector2f roundedPos;
    roundedPos.x = roundCoordinate(pos.x, v.x);
    roundedPos.y = roundCoordinate(pos.y, v.y);

    return roundedPos;
}

int main()
{
    sf::View view({0, 0, 64, 64});
    sf::RenderWindow window({ 512,512 }, "SFML works!");
    window.setFramerateLimit(1);
    window.setView(view);

    sf::RectangleShape shape({16.f, 16.f});
    shape.setFillColor(sf::Color::White);

    sf::Clock clock;

    sf::Vector2f pos{32.f, 0.f};

    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();
        }

        sf::Vector2f v{ -0.5f, 0.5f };
        pos += v;
        shape.setPosition(getRoundedPos(pos, v));

        window.clear();
        window.draw(shape);
        window.display();
    }
}
 
Tomb Painter, Re:creation dev (abandoned, doing other things) | edw.is | @EliasDaler