okay this is driving me crazy. I want to rotate a sprite so that it follows the mouse cursor. I know there's quite a few posts on the subject ( I've looked through all that I could find) and my code seems correct so I don't know what to do.
The issue is when I implement the code all inside of main() it runs the way it should. When I put the code inside of a class though, it does this http://youtu.be/03r05NjPoTA
the rotation doesn't follow the cursor all the way around the screen, nor does it track the angle at a consistent rate.
here's the original code that works:
#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
#include <math.h>
#include <iostream>
int main()
{
sf::Texture playerTexture;
playerTexture.loadFromFile("StickFig.png");
sf::Sprite playerSprite;
playerSprite.setTexture(playerTexture);
playerSprite.setOrigin(50,50);
playerSprite.setPosition(400,300);
playerSprite.setRotation(0);
sf::Vector2i mouse;
float angle;
sf::Vector2f playerPosition;
double a, b;
sf::RenderWindow window(sf::VideoMode(800, 600), "SFML works!");
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
playerPosition = playerSprite.getPosition();
a = mouse.x - playerPosition.x;
b = mouse.y - playerPosition.y;
mouse = sf::Mouse::getPosition(window);
angle = -atan2( a , b) * 180 / 3.14;
playerSprite.setRotation(angle);
window.clear(sf::Color::White);
window.draw(playerSprite);
window.display();
}
return 0;
}
and here's the code using the class that is messing up:
main()
#include <SFML/Graphics.hpp>
#include "player.h"
int main()
{
sf::RenderWindow window(sf::VideoMode(800, 600), "SFML works!");
while (window.isOpen())
{
player playerOne;
playerOne.initialize();
playerOne.loadContent();
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
window.clear(sf::Color::White);
playerOne.draw(window);
window.display();
}
return 0;
}
player.cpp
#include "player.h"
#include <iostream>
player::player()
{
//ctor
}
player::~player()
{
//dtor
}
void player::initialize()
{
playerSprite.setOrigin(50,50);
playerSprite.setPosition(400,300);
}
void player::loadContent()
{
playerTexture.loadFromFile("player.png");
playerSprite.setTexture(playerTexture);
}
void player::draw(sf::RenderWindow &window)
{
this->trackMouse(true, playerSprite, window);
window.draw(playerSprite);
}
void player::update(sf::RenderWindow &window)
{
}
void player::trackMouse(bool value, sf::Sprite &sprite, sf::RenderWindow &window)
{
if(value == true)
{
window.convertCoords(mouse);
//gets sprite origin coordinates and mouse coordinates
this->spritePosition = sprite.getPosition();
this->mouse = sf::Mouse::getPosition();
mouseAngle = -atan2( mouse.x - spritePosition.x , mouse.y - spritePosition.y) * 180 / 3.14159; //angle in degrees of rotation for sprite
playerSprite.setRotation(mouseAngle);
}
}
any help would just be fantasmical
It's a small but important mistake:
this->mouse = sf::Mouse::getPosition();
// turns into
this->mouse = sf::Mouse::getPosition(window);
So instead of getting the absolute coordinates you get the relative ones.
As for your code ;)
- The usage of this-> isn't needed. For member variables it's common practice to use a prefix, e.g. m_ and thus you'd already see that the variable comes from the class and there won't be any name clashes with arguments.
- You should probably use a better naming scheme, because currently it would be very hard to differentiate classes from variables. Personally I always capitalize the first letter of the class name, e.g. Player instead of player.
- Checking a boolean against true is a silly thing, it's not only redundant, but it can also be a source of hard to spot errors (i.e. if(boolean = false))
- Passing a member variable from a member function to another (i.e. your sprite) doesn't make any sense. Both member functions have access to the sprite already.
- The constructor's job is to initialize a class, so use it and don't use your own init or initialize function. It will only lead to errors, if you forget to call it by accident...
- Only use a dtor if you actually need one.
Take into account that rotation of sprites doesn't use their center as an origin, but the default origin (0, 0) or any origin you have set before. I assume you want the sprite to go to the mouse current location while rotating. In that case the fastest way without setting a new origin every time is to move the sprite to it's "center" point, rotate it and then move it to the position you want it to be in.
Also note that sprites calculate their position not by their center, but by the top-left point of the quad they represent. As you need to have the texture alive you can perfectly make a locate center function that uses the texture size or the current texture rect in order to locate your sprite in a center-wise location around the origin so it can rotate around itself.
///Something like this:
Sprite.setPosition(texture.getSize().x/2.f, texture.getSize().y/2.f);
Sprite.rotate(whatever);
Sprite.setPosition(previousPosition);
///In case you are not using a sprite sheet.
///else you need to use the current texture rect that complicates things slightly.
Also take into account that SFML uses inverted coordinates, so a 90 degrees angle is actually a -90 angle in usual coordinates. I personally prefer to use the default origin and just move things to it, make rotations (the transformation that is usually the most confusing at first) and then move back to the previous position before drawing, rather than shifting the origin as I go, since it may cause some unexpected behavior.
Edit: You should also not try to call functions like initialize() within the loop unless you really need to. It may break what your program does and it tends to become wasted processing. There's no need to load everything every frame just to have it destroyed the next iteration when you are still using it. Put it outside of the loop, just like your window is.