-
Hello people,
I've been trying to implement drag & drop for my sprites for a while now, but can't say which line causes these following undesired effects, so for example only the last created one is movable (with some other strange undesired effects: the first created sprite doesn't react) See for yourself. Can you give me some advice, where I have to look for the glitch. Code seems to be very long, but it's as short as I can do it, to present the glitch :-)
Style looks strange, because I'm using a prog for indentation :-)
#include <SFML/Graphics.hpp>
#include <iostream>
#include <list>
int main() {
sf::RectangleShape maprect;
maprect.setSize(sf::Vector2f(500, 500));
maprect.setFillColor(sf::Color(0, 0, 255, 255));
sf::Sprite newSprite;
sf::FloatRect mapbound = maprect.getGlobalBounds();
sf::RenderWindow mMainWindow(sf::VideoMode(500, 500), "Map");
mMainWindow.setFramerateLimit(60);
sf::Texture unittexture;
unittexture.loadFromFile("warrior.png");
std::list<sf::Sprite> EnemyList;
std::list<sf::Sprite>::iterator EnemyIt;
bool dragging = false;
sf::Vector2f mouseRectOffset;
int mouseX = 0;
int mouseY = 0;
while (mMainWindow.isOpen()) {
sf::Event event;
while (mMainWindow.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
mMainWindow.close();
}
if (event.type == sf::Event::KeyPressed)
if (event.key.code == sf::Keyboard::A) {
newSprite.setTexture(unittexture);
newSprite.setPosition(mMainWindow.mapPixelToCoords(
sf::Vector2i(sf::Mouse::getPosition(mMainWindow).x,
sf::Mouse::getPosition(mMainWindow).y)));
EnemyList.push_back(newSprite);
std::cout << EnemyList.size() << std::endl;
}
}
{
if (event.type == sf::Event::MouseButtonPressed &&
event.mouseButton.button == sf::Mouse::Left)
for (auto &EnemyIt = EnemyList.rbegin(); EnemyIt != EnemyList.rend();
++EnemyIt)
if (EnemyIt->getGlobalBounds().contains(event.mouseButton.x,
event.mouseButton.y))
{
EnemyList.rbegin();
dragging = true;
mouseRectOffset.x = event.mouseButton.x -
EnemyIt->getGlobalBounds().left -
EnemyIt->getOrigin().x;
mouseRectOffset.y = event.mouseButton.y -
EnemyIt->getGlobalBounds().top -
EnemyIt->getOrigin().y;
break;
}
if (event.type == sf::Event::MouseButtonReleased &&
event.mouseButton.button == sf::Mouse::Left) {
dragging = false;
}
if (event.type == sf::Event::MouseMoved) {
mouseX = event.mouseMove.x;
mouseY = event.mouseMove.y;
}
}
if (dragging == true)
for (auto &EnemyIt = EnemyList.rbegin(); EnemyIt != EnemyList.rend();
++EnemyIt) {
EnemyList.rend();
EnemyIt->setPosition(mouseX - mouseRectOffset.x,
mouseY - mouseRectOffset.y);
break;
}
mMainWindow.clear();
mMainWindow.draw(maprect);
mMainWindow.draw(newSprite);
for (EnemyIt = EnemyList.begin(); EnemyIt != EnemyList.end(); ++EnemyIt) {
mMainWindow.draw(*EnemyIt);
}
mMainWindow.display();
}
return 0;
}
-
I have a strong idiosyncrasy for java-style indentation :(. Anyway, why are you declaring a list iterator out of a loop? What's its purpose?
Why do you mix real time input with event based input?
Regarding mouse::getposition, you could check if the movement has occurred in the same frame and save it.
-
Ok, I've further reduced the code and now everything is without java-style :-)
The list iterator is global, isn't it or do you want me to put it inside the while loop? As for the real/event based input, even after having read the corresponding tutorials, I don't get it, what I'm doing wrong. Everything seems just fine :-) Should I use "cases"?
#include <SFML/Graphics.hpp>
#include <iostream>
#include <list>
int main()
{
sf::RectangleShape maprect;
maprect.setSize(sf::Vector2f(500, 500));
maprect.setFillColor(sf::Color(0,0,255,255));
sf::Sprite newSprite;
sf::FloatRect mapbound = maprect.getGlobalBounds();
sf::RenderWindow mMainWindow(sf::VideoMode(500, 500), "Map");
mMainWindow.setFramerateLimit(60);
sf::Texture unittexture;
unittexture.loadFromFile("warrior.png");
std::list<sf::Sprite> EnemyList;
std::list<sf::Sprite>::iterator EnemyIt;
bool dragging = false ;
while (mMainWindow.isOpen())
{
sf::Event event;
while (mMainWindow.pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
mMainWindow.close();
}
if (event.type == sf::Event::KeyPressed)
if (event.key.code == sf::Keyboard::A)
{
newSprite.setTexture(unittexture);
newSprite.setPosition(mMainWindow.mapPixelToCoords(sf::Vector2i(sf::Mouse::getPosition(mMainWindow).x,sf::Mouse::getPosition(mMainWindow).y)));
EnemyList.push_back(newSprite);
std::cout << EnemyList.size() << std::endl;
}
}
{
if (event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Left)
for (auto &EnemyIt = EnemyList.rbegin(); EnemyIt != EnemyList.rend(); ++EnemyIt)
if (EnemyIt->getGlobalBounds().contains(event.mouseButton.x,event.mouseButton.y))
{
EnemyList.rbegin();
dragging = true;
}
if (event.type == sf::Event::MouseButtonReleased && event.mouseButton.button == sf::Mouse::Left)
{
dragging = false;
}
}
if (dragging == true)
for (auto &EnemyIt = EnemyList.rbegin(); EnemyIt != EnemyList.rend(); ++EnemyIt)
{
EnemyList.rend();
EnemyIt->setPosition((mMainWindow.mapPixelToCoords(sf::Vector2i(sf::Mouse::getPosition(mMainWindow).x,sf::Mouse::getPosition(mMainWindow).y))));
break;
}
mMainWindow.clear();
mMainWindow.draw(maprect);
mMainWindow.draw(newSprite);
for(EnemyIt = EnemyList.begin();EnemyIt != EnemyList.end();++EnemyIt)
{
mMainWindow.draw(*EnemyIt);
}
mMainWindow.display();
}
return 0;
}
-
Hi, there are some things I don't understand:
1., What's the point of calling EnemyList.rbegin() and EnemyList.rend() alone (without an assignment)?
2., In the for loop after if(dragging == true) you set every enemy's position to the same (not just the one's that you dragging).
3., If you create a new enemy then you add it to the EnemyList so it will be drawn in the last for loop as well, so you don't have to draw newSprite specifically.
Sorry, I know that this doesn't answer your question, but I couldn't figure the main cause of your problem yet.
-
I did some modifications but I still don't get the purpose of this code, can you walk me through it?
#include <SFML/Graphics.hpp>
#include <iostream>
#include <list>
int main()
{
/*window related stuff*/
sf::RenderWindow mMainWindow(sf::VideoMode(500, 500), "Map");
mMainWindow.setFramerateLimit(60);
/* */
/*graphic resources */
sf::RectangleShape maprect;
maprect.setSize(sf::Vector2f(500, 500));
//maprect.setFillColor(sf::Color(0,0,255,255)); useless
maprect.setFillColor(sf::Color::Blue);
sf::Sprite newSprite; //You must explain this
sf::FloatRect mapbound = maprect.getGlobalBounds(); // and this
sf::Texture unittexture;
unittexture.loadFromFile("cursor.png");
/* */
std::list<sf::Sprite> EnemyList;
while (mMainWindow.isOpen())
{
sf::Event event;
bool isPressedA = false;
bool dragging = false;
while (mMainWindow.pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
mMainWindow.close();
}
else if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::A)
{
newSprite.setTexture(unittexture);
//newSprite.setPosition(mMainWindow.mapPixelToCoords(sf::Vector2i(sf::Mouse::getPosition(mMainWindow).x, sf::Mouse::getPosition(mMainWindow).y)));
//this set position could be done without using real time input by checking (isPressedA) in a mousemovedevent, if you're in the same frame there is no difference
isPressedA = true; // this will be used in wherever you need if A has been pressed (will be TRUE for the rest of the FRAME)
EnemyList.push_back(newSprite); // you're calling the copy constructor, is that what you wanted? Then why is newSprite out of the loop?
std::cout << EnemyList.size() << std::endl;
}
else if (event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Left)
{
for (auto &EnemyIt : EnemyList)
{
if (EnemyIt.getGlobalBounds().contains(static_cast<float>(event.mouseButton.x), static_cast<float>(event.mouseButton.y))) // there was a implicit conversion int->float
{
//EnemyList.rbegin(); why this?
dragging = true;
}
}
}
else if (event.type == sf::Event::MouseButtonReleased && event.mouseButton.button == sf::Mouse::Left)
{
dragging = false;
}
}
if (dragging == true)
{
for (auto &EnemyIt : EnemyList)
{
//EnemyList.rend(); why again?
//EnemyIt.setPosition((mMainWindow.mapPixelToCoords(sf::Vector2i(sf::Mouse::getPosition(mMainWindow).x, sf::Mouse::getPosition(mMainWindow).y))));
//this set position could be done without using real time input by checking (dragging) in a mousemovedevent, if you're in the same frame there is no difference
break; //what???
}
}
mMainWindow.clear();
mMainWindow.draw(maprect);
mMainWindow.draw(newSprite);
for (auto &EnemyIt : EnemyList)
{
mMainWindow.draw(EnemyIt);
}
mMainWindow.display();
}
return 0;
}
-
Ok, here is the commented, unchanged code (had to translate it to my VS 2010 and than back again :-) )
What I would like, is being able to click on any sprite and drag&drop it. If there are multiple sprites on top of each other, than the topmost should be selected....and so on.
#include <SFML/Graphics.hpp>
#include <iostream>
#include <list>
int main()
{
/*window related stuff*/
sf::RenderWindow mMainWindow(sf::VideoMode(500, 500), "Map");
mMainWindow.setFramerateLimit(60);
/* */
/*graphic resources */
sf::RectangleShape maprect;
maprect.setSize(sf::Vector2f(500, 500));
//maprect.setFillColor(sf::Color(0,0,255,255)); useless --------->ok, in this case, its not the best solution
maprect.setFillColor(sf::Color::Blue);
sf::Sprite newSprite; //You must explain this Explanation: -------------->newSprite is a Sprite of the sfml drawable class and here it is initialized with the name "newSprite"
sf::FloatRect mapbound = maprect.getGlobalBounds(); // and this ------------>this was for a workaround, where I couldn't put "maprect", but now I see thats useless
sf::Texture unittexture;
unittexture.loadFromFile("cursor.png");
/* */
std::list<sf::Sprite> EnemyList;
while (mMainWindow.isOpen())
{
sf::Event event;
bool isPressedA = false;
bool dragging = false;
while (mMainWindow.pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
mMainWindow.close();
}
else if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::A) //----> I didn't use else if, because it didn't work for the most time
{
newSprite.setTexture(unittexture);
//newSprite.setPosition(mMainWindow.mapPixelToCoords(sf::Vector2i(sf::Mouse::getPosition(mMainWindow).x, sf::Mouse::getPosition(mMainWindow).y)));
//this set position could be done without using real time input by checking (isPressedA) in a mousemovedevent, if you're in the same frame there is no difference
isPressedA = true; // this will be used in wherever you need if A has been pressed (will be TRUE for the rest of the FRAME)
EnemyList.push_back(newSprite); // you're calling the copy constructor, is that what you wanted? Then why is newSprite out of the loop? ----------> Yes, I want to get copies of sprite
std::cout << EnemyList.size() << std::endl;
}
else if (event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Left)
{
for (auto &EnemyIt : EnemyList)
{
if (EnemyIt->getGlobalBounds().contains(static_cast<float>(event.mouseButton.x), static_cast<float>(event.mouseButton.y))) // there was a implicit conversion int->float
{
//EnemyList.rbegin(); why this? ---------------------> ok, this really is useless
dragging = true;
}
}
}
else if (event.type == sf::Event::MouseButtonReleased && event.mouseButton.button == sf::Mouse::Left)
{
dragging = false;
}
}
if (dragging == true)
{
for (auto &EnemyIt : EnemyList)
{
//EnemyList.rend(); why again? ---------------> I think, it should be rbegin instead, because I want being able to drag the topmost sprite (if there are multiple sprites at the same position)
//EnemyIt.setPosition((mMainWindow.mapPixelToCoords(sf::Vector2i(sf::Mouse::getPosition(mMainWindow).x, sf::Mouse::getPosition(mMainWindow).y))));
//this set position could be done without using real time input by checking (dragging) in a mousemovedevent, if you're in the same frame there is no difference
break; //what??? --------------------> this is to get sure, that he breaks out of the loop
}
}
mMainWindow.clear();
mMainWindow.draw(maprect);
mMainWindow.draw(newSprite); // this seems to be useless as well
for (auto &EnemyIt : EnemyList)
{
mMainWindow.draw(EnemyIt);
}
mMainWindow.display();
}
return 0;
}
-
- You should save a Z value for every sprite so that you know which one is on the top and so on.
- You must bear in mind that by setting the mouseposition like that while dragging a sprite might feel weird, the default origin of the sprite is on the top left,in the first frame the sprite will probably shift to have the cursor in 0,0
- List is slower than vector most of the time and you gain nothing out of it
I need to eat now, I will update the post soon
-
I might comment on the code later.
For now, the best advice I can offer is; read one (or more) of these books: http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list
You seem to stumble on basic algorithms, use of iterators and containers an just plain basic C++ - reading and learning a bit would probably be a good investment (and faster) rather than experimenting and guessing.
-
Hi there,
I know the basics of cpp, it's only a habit of collecting old/experimental code, that could become useful one day.....and lack of concentration, while writing/reading my code. Sorry that most of the time I produce such chaos code, it will improve. Promised! :-) Regarding the vectors: I know how to use them, but for now I'd like to keep my list :-)
Ok, now I´ve some reworked code (VS 2010 Standard), where at least I can move those sprites around, but only, if clicking inside of the corresponding unit, that I want to move. Another workaround would be to make the "invisible" background around the spritetexture bigger, but that would be problematic, when doing collisions.....Now, I only need being able to drag&drop them or at least "beam" them, if its less difficult (first click to select a unitsprite, second click for beaming it to a target position). Do I still need a Z value with the new code? What to do next? And what to do for telling him "Select THIS unit and stay focused on it, until I tell you to give it free" (if that could be done, than everything would be possible :-) )
#include <SFML/Graphics.hpp>
#include <iostream>
#include <list>
int main()
{
sf::RenderWindow mMainWindow(sf::VideoMode(500, 500), "Map");
mMainWindow.setFramerateLimit(60);
sf::RectangleShape maprect;
maprect.setSize(sf::Vector2f(500, 500));
maprect.setFillColor(sf::Color::Blue);
sf::Texture unittexture;
unittexture.loadFromFile("warrior.png");
std::list<sf::Sprite> EnemyList;
while (mMainWindow.isOpen())
{
sf::Event event;
bool isPressedA = false;
bool dragging = false;
while (mMainWindow.pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
mMainWindow.close();
}
else if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::A)
{
sf::Sprite newSprite;
newSprite.setTexture(unittexture);
newSprite.setPosition(mMainWindow.mapPixelToCoords(sf::Vector2i(sf::Mouse::getPosition(mMainWindow).x, sf::Mouse::getPosition(mMainWindow).y)));
//this set position could be done without using real time input by checking (isPressedA) in a mousemovedevent, if you're in the same frame there is no difference //dont understand-->a is only for unit creation
isPressedA = true;
EnemyList.push_back(newSprite);
std::cout << EnemyList.size() << std::endl;
}
else if (event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Left)
{
for (auto &EnemyIt = EnemyList.rbegin(); EnemyIt != EnemyList.rend(); ++EnemyIt)
{
{
dragging = true;
}
}
}
else if (event.type == sf::Event::MouseButtonReleased && event.mouseButton.button == sf::Mouse::Left)
{
dragging = false;
}
if (dragging == true)
{
for (auto &EnemyIt = EnemyList.rbegin(); EnemyIt != EnemyList.rend(); ++EnemyIt)
if (EnemyIt->getGlobalBounds().contains(static_cast<float>(event.mouseButton.x), static_cast<float>(event.mouseButton.y)))
{
EnemyIt->setPosition((mMainWindow.mapPixelToCoords(sf::Vector2i(sf::Mouse::getPosition(mMainWindow).x-50, sf::Mouse::getPosition(mMainWindow).y-50))));
break;
}
}
}
mMainWindow.clear();
mMainWindow.draw(maprect);
for (auto &EnemyIt = EnemyList.begin(); EnemyIt != EnemyList.end(); ++EnemyIt)
{
mMainWindow.draw(*EnemyIt);
}
mMainWindow.display();
}
return 0;
}
-
Give me some time to test my hypothesis
-
Ok, I don't really understand what you need but i tried anyway.
Note that:
- There are many ways to do this and this solution was the fastest(not the best) for me
- Having all code in main() has the sole purpose of not diverging too much from what you've asked (use classes!)
#include <SFML/Graphics.hpp>
#include <iostream>
#include <list>
#include <cassert>
int main()
{
sf::RenderWindow mMainWindow(sf::VideoMode(600,600), "Map", sf::Style::Close);
mMainWindow.setFramerateLimit(60);
mMainWindow.setKeyRepeatEnabled(false);
sf::Image image;
image.create(50, 50, sf::Color::Red);
sf::Texture texture;
texture.loadFromImage(image);
std::list<sf::Sprite> EnemyList;
sf::Sprite* focus = nullptr;
/* to check the mouse status we'll save if the leftbutton has been clicked */
/* holding is almost the same as clicked, but to hold you must have clicked */
/* on a sprite */
bool clicked = false;
bool holding = false;
while (mMainWindow.isOpen())
{
sf::Event event;
bool creating = false;
bool moving = false;
bool clicking = false;
bool dragging = false;
sf::Vector2i mousePos;
while (mMainWindow.pollEvent(event))
{
switch (event.type)
{
case sf::Event::Closed:
mMainWindow.close();
break;
case sf::Event::MouseMoved:
mousePos = sf::Vector2i(event.mouseMove.x, event.mouseMove.y);
moving = true;
break;
case sf::Event::KeyPressed:
creating = (event.key.code == sf::Keyboard::A);
break;
case sf::Event::MouseButtonPressed:
if (event.mouseButton.button == sf::Mouse::Left)
clicked = clicking = true;
mousePos = sf::Vector2i(event.mouseButton.x, event.mouseButton.y);
break;
case sf::Event::MouseButtonReleased:
if (event.mouseButton.button == sf::Mouse::Left)
clicked = clicking = false; //clicking doesn't make sense here but who gives a fetch()
mousePos = sf::Vector2i(event.mouseButton.x, event.mouseButton.y);
break;
}
}
holding = clicked && (focus != nullptr);
dragging = (holding && moving);
if (creating)
{
sf::Sprite sprite;
/* Mixing real time input and event based input is bad but */
/* but you might press A when the mouse is still and */
/* there won't be any position saved */
/* (you could use a lastknownpos variable) */
mousePos = (mousePos == sf::Vector2i(0, 0) ? sf::Mouse::getPosition(mMainWindow) : mousePos);
sprite.setTexture(texture);
sprite.setColor(sf::Color::Red);
/* just to make sure that the cursor will always be in the center */
/* we'll change the origin of the sprite */
/* (the first frame the sprite will jump to a position where the */
/* the cursor is in the middle) */
sprite.setOrigin(static_cast<float>(sprite.getTextureRect().width) / 2, static_cast<float>(sprite.getTextureRect().height) / 2);
sprite.setPosition(static_cast<float>(mousePos.x), static_cast<float>(mousePos.y));
EnemyList.push_front(sprite); // this way is the lamest way to have an implicit Z value: the newer you are, the topmost you are.
#if DEBUG
std::cout << "Size changed to " << EnemyList.size() << std::endl;
#endif
}
else
if (clicking)
{
for (auto& enemy = EnemyList.begin(); enemy != EnemyList.end(); ++enemy)
{
if (enemy->getGlobalBounds().contains(static_cast<float>(mousePos.x), static_cast<float>(mousePos.y)))
{
focus = &(*enemy);
std::clog << focus << std::endl;
break; //topmost will be moved
}
}
}
else
if (dragging)
{
assert(focus != nullptr);
focus->setPosition(static_cast<float>(mousePos.x), static_cast<float>(mousePos.y));
}
else if (!holding)
focus = nullptr; //I'm not doing anything so I can assume there's no sprite being focused
#if DEBUG
std::clog
<< (moving ? 'm' : ' ')
<< (clicking ? 'c' : ' ')
<< (clicked ? 'c' : ' ')
<< (holding ? 'h' : ' ')
<< (dragging ? 'd' : ' ')
<< std::endl;
#endif
mMainWindow.clear();
for (auto& enemy = EnemyList.rbegin(); enemy != EnemyList.rend(); ++enemy)
{
mMainWindow.draw(*enemy);
}
mMainWindow.display();
}
return 0;
}
-
Thanks for sharing this interesting code. By clicking as fast as possible inside the corresponding rect and moving the cursor, it even moves "somehow" :-)
-
Thanks for sharing this interesting code. By clicking as fast as possible inside the corresponding rect and moving the cursor, it even moves "somehow" :-)
That's because I forgot to save the focus when you're only holding and not dragging. Now it works as it should :P