First of all, the coordinate system that most graphics libraries like SFML, SDL, etc. had been using is the Cartesian coordinates, where the
origin (0, 0) sits at the top left corner of the rendering window. For example, if you were to draw an object and didn't specify its specific location, then its default location will be at the top left corner (unless you've specified its origin), and not at the center of the screen as you would expect. Refer to the attachment.
So here's the code overview
#include <SFML/Graphics.hpp>
enum class Direction
{
Up, Down, Left, Right, Static
};
bool checkCollision(const sf::FloatRect& rectA,
const sf::FloatRect& rectB)
{
return rectB.intersects(rectA);
}
void shiftSprite(sf::Sprite& sprite,
const sf::Vector2f& speed,
Direction dir,
const sf::Time& deltaTime)
{
sf::Vector2f offset;
switch (dir)
{
case Direction::Left:
offset.x = -speed.x;
break;
case Direction::Right:
offset.x = speed.x;
break;
case Direction::Up:
offset.y = -speed.y;
break;
case Direction::Down:
offset.y = speed.y;
break;
}
sprite.move(offset.x * deltaTime.asMilliseconds(),
offset.y * deltaTime.asMilliseconds());
}
void main()
{
sf::RenderWindow window;
window.create(sf::VideoMode(800, 600), "test", sf::Style::Default);
window.setFramerateLimit(60);
sf::Texture texture;
texture.loadFromFile("protagonist.png");
sf::Sprite sprite(texture);
sf::Sprite sprite2(texture);
sprite.setPosition(sf::Vector2f(0, 250));
sprite.setOrigin(sprite.getLocalBounds().width / 2,
sprite.getLocalBounds().height / 2);
sprite2.setOrigin(-200, -200);
sf::Vector2f speed(0.4f, 0.4f);
sf::Clock timer;
while (window.isOpen())
{
sf::Event event;
sf::Time deltaTime = timer.restart();
while (window.pollEvent(event))
if (event.type == sf::Event::Closed)
window.close();
sf::Vector2f offset;
Direction dir = Direction::Static;
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
{
offset.x = -speed.x;
dir = Direction::Left;
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
{
offset.x = speed.x;
dir = Direction::Right;
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
{
offset.y = -speed.y;
dir = Direction::Up;
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
{
offset.y = speed.y;
dir = Direction::Down;
}
sprite.move(offset.x * deltaTime.asMilliseconds(),
offset.y * deltaTime.asMilliseconds());
if (checkCollision(sprite.getGlobalBounds(), sprite2.getGlobalBounds()))
shiftSprite(sprite2, speed, dir, deltaTime);
window.clear(sf::Color(255, 255, 255));
window.draw(sprite);
window.draw(sprite2);
window.display();
}
}
Here's the explanation:
enum class Direction
{
Up, Down, Left, Right, Static
};
Basically, this specifies the sprites' directions. The "Static" just says that the object is at rest.
bool checkCollision(const sf::FloatRect& rectA, const sf::FloatRect& rectB);
This function checks if any collision between the two rectangles happens, where
rectA is the collider, and
rectB is the bounding box.
void shiftSprite(sf::Sprite& sprite,
const sf::Vector2f& velocity,
Direction dir,
const sf::Time& deltaTime)
This function just basically moves the sprite. It takes the
sprite object; the
velocity (because it has a corresponding direction - the
dir parameter); and the frame time or the delta time. The delta time makes your sprite to move smoothly real time.
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
{
offset.x = -speed.x;
dir = Direction::Left;
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
{
offset.x = speed.x;
dir = Direction::Right;
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
{
offset.y = -speed.y;
dir = Direction::Up;
}
else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
{
offset.y = speed.y;
dir = Direction::Down;
}
You can't do this piece of code inside the poll event loop because
sf::Keyboard::isKeyPressed checks if any key is being pressed not had been pressed.
Reference:
https://en.sfml-dev.org/forums/index.php?topic=14350.0Typically, there's a big difference
if (sf::Keyboard::isKeyPressed(sf::Keyboard::isKeyPressed(KEY)))
// Checks if the key is being pressed
while (window.pollEvent(event))
if (event.type == sf::Event::KeyReleased &&
event.key.code == sf::Keyboard::KEY)
// Checks if the key has been pressed
sprite.move(offset.x * deltaTime.asMilliseconds(), offset.y * deltaTime.asMilliseconds());
if (checkCollision(sprite.getGlobalBounds(), sprite2.getGlobalBounds()))
shiftSprite(sprite2, speed, dir, deltaTime);
The code should not be interchanged as the
checkCollision function checks the updated position of the sprite (collider).
The code above uses independent frame rate
window.setFramerateLimit(60);
.....
sprite.move(offset.x * deltaTime.asMilliseconds(),
offset.y * deltaTime.asMilliseconds());
Reference:
https://maksimdan.gitbooks.io/sfml-and-gamedevelopement/content/frame_rate.html