-
I'm trying to implement projectiles, which for now is just a line (VertexArray).
I managed to create a line from the player to the mouse, and now I'm trying with a constant hypotenuse, projectileLength, but I'm having problems getting the correct position for the new x and y.
it sets the vertex strange position close to the top left corner of the window and moves around a circle like area as I move the mouse.
Github: https://github.com/Snizzlenose/2D-SFML-shooter-v2
The function and the functioncall:
main.cpp:
world.player.Shooting( sf::Mouse::getPosition( window ).x, sf::Mouse::getPosition( window ).y );
Player.cpp:
void Player::Shooting( double mouse_x, double mouse_y )
{
/* Calculate angle with restricted projectile length (hypotenuse) */
double delta_x = GetPosition( ).x - mouse_x;
double delta_y = GetPosition( ).y - mouse_y;
double radians = atan( delta_y / delta_x );
double new_x = cos( radians ) * _projectileLength;
double new_y = sin( radians ) * _projectileLength;
if( _shooting == true )
{
projectile[0].position = sf::Vector2f( GetPosition( ).x, GetPosition( ).y );
projectile[1].position = sf::Vector2f( new_x, new_y );
std::cout << "Shooting!\n";
}
else
{
projectile[0].position = sf::Vector2f( 9999, 9999 );
projectile[1].position = sf::Vector2f( 9999, 9999 );
}
}
-
Personally, you're doing it the hard way.
Try vector math! Makes stuff like this much easier.
For example:
// constants
const sf::Vector2f CENTER = static_cast<sf::Vector2f>(window.getSize() / 2u);
constexpr float LENGTH = 35.f;
constexpr float VELOCITY = 122.f;
// the projectile
sf::VertexArray line(sf::Lines, 2);
// calculate projectile direction based off: mouse press location and center of window
sf::Vector2f mousePos = {static_cast<float>(event.mouseButton.x), static_cast<float>(event.mouseButton.y)};
sf::Vector2f projEndNorm = unitVec(mousePos - CENTER);
line[0].position = CENTER + projEndNorm; // offset from the center slightly so: line[0].position - CENTER != {0, 0}
line[1].position = projEndNorm * LENGTH + CENTER;
// update
line[0].position += unitVec(line[0].position - CENTER) * VELOCITY * dt.asSeconds();
line[1].position += unitVec(line[1].position - CENTER) * VELOCITY * dt.asSeconds();
// the unitVec function:
sf::Vector2f unitVec(const sf::Vector2f& vec)
{
if(vec.x != 0 && vec.y != 0)
return vec / std::sqrt(vec.x * vec.x + vec.y * vec.y); // vector / length = unit vector of the vector
else
return {0, 0};
}
Unit Vector Definition (https://en.wikipedia.org/wiki/Unit_vector)
Minimal and Naive Example if you need it (https://gist.github.com/dabbertorres/c550b2dde17f95b0bafa)
-
Personally, you're doing it the hard way.
Try vector math! Makes stuff like this much easier.
For example:
// constants
const sf::Vector2f CENTER = static_cast<sf::Vector2f>(window.getSize() / 2u);
constexpr float LENGTH = 35.f;
constexpr float VELOCITY = 122.f;
// the projectile
sf::VertexArray line(sf::Lines, 2);
// calculate projectile direction based off: mouse press location and center of window
sf::Vector2f mousePos = {static_cast<float>(event.mouseButton.x), static_cast<float>(event.mouseButton.y)};
sf::Vector2f projEndNorm = vecNormal(mousePos - CENTER);
line[0].position = CENTER + projEndNorm; // offset from the center slightly so: line[0].position - CENTER != {0, 0}
line[1].position = projEndNorm * LENGTH + CENTER;
// update
line[0].position += vecNormal(line[0].position - CENTER) * VELOCITY * dt.asSeconds();
line[1].position += vecNormal(line[1].position - CENTER) * VELOCITY * dt.asSeconds();
// the vecNormal function:
sf::Vector2f vecNormal(const sf::Vector2f& vec)
{
if(vec.x != 0 && vec.y != 0)
return vec / std::sqrt(vec.x * vec.x + vec.y * vec.y); // vector / length = unit vector of the vector
else
return {0, 0};
}
Unit Vector Definition (https://en.wikipedia.org/wiki/Unit_vector)
Minimal and Naive Example if you need it (https://gist.github.com/dabbertorres/c550b2dde17f95b0bafa)
Awesome thanks! I'll try that tomorrow and use your naive guide as a reference to recreate it.
However I have some questions beforehand, for the things I haven't encountered yet:
const sf::Vector2f CENTER = static_cast<sf::Vector2f>(window.getSize() / 2u);
What is static_cast used for, it's a type of conversion from what I understand but what difference does it do to using ( sf::Vector2f ), or does that not work?
is 2u supposed to be something specific or just half the window size?
constexpr float LENGTH = 35.f;
From what I understand constexpr is like const used for objects and functions, does it have purpose for a simple data type?
sf::Vector2f mousePos = {static_cast<float>(event.mouseButton.x), static_cast<float>(event.mouseButton.y)};
This captures the position of the mouse as the mousebutton is pressed and doesn't update right?
As for using CENTER as a whole, what does it do? I assume that's why you don't need to calculate angles from the player.
I probably should just read up a lot more til I understand, but vectors, are they just positions for dots right, with x, y , z, etc and the normal is the product of two or more vectors, or does vectors always have a magnitude / normal include together with the position?
-
const sf::Vector2f CENTER = static_cast<sf::Vector2f>(window.getSize() / 2u);
What is static_cast used for, it's a type of conversion from what I understand but what difference does it do to using ( sf::Vector2f ), or does that not work?
I can't see anything wrong with just using the vector's constructor here:
e.g.
const sf::Vector2f CENTER = sf::Vector2f(window.getSize() / 2u);
or
const sf::Vector2f CENTER{ sf::Vector2f(window.getSize() / 2u) };
is 2u supposed to be something specific or just half the window size?
It just divides the window size in half. Since the vector that stores the result of window's getSize() function in unsigned ints, the scalar to multiply/divide it by (the single value) must be of the same type (2u is unsigned 2). Try it with just 2 instead and your compiler will probably complain.
For example, if you converted the window size to sf::Vector2f first, you'd divide by a float instead:
const sf::Vector2f CENTER = sf::Vector2f(window.getSize()) / 2.f;
constexpr float LENGTH = 35.f;
From what I understand constexpr is like const used for objects and functions, does it have purpose for a simple data type?
I don't suppose constexpr is required here. const should probably suffice.
This captures the position of the mouse as the mousebutton is pressed and doesn't update right?
Yes. event.mouseButton should only be used in the event loop when event.type == sf::Event::MousePressed or event.type == sf::Event::MouseReleased. If you required updating positions, you should update the value on mousemove, or use real-time mouse positions.
As for using CENTER as a whole, what does it do?
It stores the position of the centre of the window.
-
What is static_cast used for, it's a type of conversion from what I understand but what difference does it do to using ( sf::Vector2f ), or does that not work?
static_cast is the preferred way of casting, go read this stackoverflow answer (http://stackoverflow.com/a/1255015/1787213) on the differences.
I can't see anything wrong with just using the vector's constructor here:
I had forgotten about the conversion constructor (http://www.sfml-dev.org/documentation/2.3/classsf_1_1Vector2.php#a3da455e0ae3f8ff6d2fe36d10b332d10), hence the explicit cast.
is 2u supposed to be something specific or just half the window size?
Yes, just half the window size. The 'u' on the end explicitly makes the value an unsigned integer.
I did this because sf::Window::getSize() returns a sf::Vector2u, an unsigned integer vector, not a float vector.
From what I understand constexpr is like const used for objects and functions, does it have purpose for a simple data type?
Eh, I just like using it when I can, since it's sure to enforce compile-time constants, rather than possible run-time constants.
This captures the position of the mouse as the mousebutton is pressed and doesn't update right?
Correct, that will only update when a mouse button was pressed or released. I made the assumption that most projectiles don't change direction much after being fired.
As for using CENTER as a whole, what does it do?
I just used the center of the window as the starting point for the projectiles. You could just replace this with the position of your player or whatnot as needed, it will work the same. Though, if your player ends up changing position, that could pose an issue. So you'd have to store the starting point of the projectile. Or:
line[0].position += -unitVec(line[0].position - line[1].position) * VELOCITY * dt.asSeconds();
line[1].position += unitVec(line[1].position - line[0].position) * VELOCITY * dt.asSeconds();
This way, the projectile will just reuse it's current direction to calculate its movement. Don't forget the negative in front of the first "unitVec", line[0].position - line[1].position will be the opposite direction of the movement, so you have to negate the result to get the opposite of the opposite. Hehe.
I probably should just read up a lot more til I understand, but vectors, are they just positions for dots right, with x, y , z, etc and the normal is the product of two or more vectors, or does vectors always have a magnitude / normal include together with the position?
Sort of, yes. I like to think of them as: "a displacement from the origin". And oh, wow. My bad. I name the function "vecNormal" when it should have been named "unitVec" or something. (I'll edit that in)
So, the unit vector of a vector, is itself divided by its magnitude.