Skip to content

Instantly share code, notes, and snippets.

@fallahn
Created July 10, 2020 18:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fallahn/8a1dfe8c7a480dbdb9c314c90f8ea919 to your computer and use it in GitHub Desktop.
Save fallahn/8a1dfe8c7a480dbdb9c314c90f8ea919 to your computer and use it in GitHub Desktop.
Following a path with SFML
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <SFML/System.hpp>
#include <array>
#include <cassert>
//dot product calculation
//https://relativity.net.au/gaming/java/DotProduct.html
float dot(const sf::Vector2f& lv, const sf::Vector2f& rv)
{
return lv.x * rv.x + lv.y * rv.y;
}
//normalises a vector to unit length (length of 1)
sf::Vector2f normalise(sf::Vector2f source)
{
float length = std::sqrt(dot(source, source));
assert(length != 0);
return source /= length;
}
//positions which make up the path to travel
const std::array<sf::Vector2f, 4u> path =
{
sf::Vector2f(200.f, 400.f),
sf::Vector2f(400.f, 100.f),
sf::Vector2f(300.f, 500.f),
sf::Vector2f(200.f, 300.f)
};
//this is our game entity
struct Ship final
{
sf::CircleShape circleShape;
sf::Vector2f direction;
sf::Vector2f target;
std::size_t nextTarget = 0;
static constexpr float Speed = 200.f;
Ship()
{
//set initial properties
circleShape.setRadius(5.f);
circleShape.setFillColor(sf::Color::Red);
setTarget(path[0]);
}
void setTarget(sf::Vector2f targetPoint)
{
//set the travel direction. this only needs to
//be done once when setting the target point as we'll
//be travelling in the same direction up until we
//receive a new target
direction = targetPoint - circleShape.getPosition();
//by normalising the value we can make sure to travel
//at a constant speed, whilst also making use of the dot product
direction = normalise(direction);
//make sure to remember the world coords of our current target
target = targetPoint;
}
void update(float dt)
{
//move by our fixed speed in the direction towards
//the current target (multiplied by frame time)
circleShape.move(direction * Speed * dt);
//now check where we are in relation to the current target.
//if we take the dot product of the direction we're moving
//with the vector created with the current position relative
//to the current target, we can tell if we've overshot yet
//if both vectors are pointing in the same direction (ie the
//target is still in front of us) the dot product will be
//greater than zero. If it is less than zero then the target
//vector points in the opposite direction to the movement
//vector, and is therefore behind us.
//https://betterexplained.com/articles/vector-calculus-understanding-the-dot-product/
auto targetDirection = target - circleShape.getPosition();
if (dot(direction, targetDirection) < 0)
{
//correct for the overshoot by placing the entity at the
//target position, although this is not strictly necessary
//as the direction will automatically be corrected when
//setting the next target point.
circleShape.setPosition(target);
//get the next target id
nextTarget = (nextTarget + 1) % path.size();
//and set the next target.
setTarget(path[nextTarget]);
}
}
};
int main()
{
sf::RenderWindow window;
window.create(sf::VideoMode(800, 600), "Window");
const float frameTime = 1.f / 60.f;
float accumulator = 0.f;
sf::Clock frameClock;
Ship ship;
while (window.isOpen())
{
sf::Event evt;
while (window.pollEvent(evt))
{
if (evt.type == sf::Event::Closed)
{
window.close();
}
}
accumulator += frameClock.restart().asSeconds();
while (accumulator > frameTime)
{
accumulator -= frameTime;
ship.update(frameTime);
}
window.clear();
window.draw(ship.circleShape);
window.display();
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment