Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Selecting sprites by click.  (Read 4321 times)

0 Members and 1 Guest are viewing this topic.

Bogdan

  • Jr. Member
  • **
  • Posts: 93
    • View Profile
Selecting sprites by click.
« on: July 17, 2014, 04:12:54 pm »
Hi there, I couldn't find anything on the whole web about this topic, so I would like to ask the pros here (SFML 2.1 with VS 2010): How to select sprites individually (from a list) by clicking on them? I know that I need a for loop for this, that cycles through the whole list and that "newSprite" is not correct, because its only the sprite that was created as last. Any hints appreciated. But what to take instead of this. Every tutorial is only about a single sprite, but not multiple during runtime created sprites.

                if (event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Middle)
                                {
                for(EnemyIt = EnemyList.begin();EnemyIt != EnemyList.end();EnemyIt++)
                                        if (newSprite.getGlobalBounds().contains(mMainWindow.mapPixelToCoords(sf::Mouse::getPosition(mMainWindow))))
                                        {
                                                //EnemyList.reverse();           For later use.
                                                std::cout << "Unit selected" << std::endl;
                                        }
                                }

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Selecting sprites by click.
« Reply #1 on: July 17, 2014, 04:50:40 pm »
Obviously, "newSprite" should be replaced with something coming from "EnemyIt". But since we don't know what's inside the Enemy class, it's hard to help you more ;)
Laurent Gomila - SFML developer

Bogdan

  • Jr. Member
  • **
  • Posts: 93
    • View Profile
Re: Selecting sprites by click.
« Reply #2 on: July 17, 2014, 04:53:19 pm »
ok, here is the code:

#include <SFML/Graphics.hpp>
#include <iostream>
#include <list>

int main()
{
    sf::RectangleShape rectangle;
    rectangle.setSize(sf::Vector2f(1000, 600));
    rectangle.setFillColor(sf::Color(100,80,100,100));
    rectangle.setPosition(0, 0);

    sf::Sprite newSprite;

    sf::FloatRect rightBound = rectangle.getGlobalBounds();

    sf::RenderWindow mMainWindow(sf::VideoMode(1000, 600), "Map");
    mMainWindow.setFramerateLimit(60);
    sf::Texture unittexture;
    unittexture.loadFromFile("warrior.png");

    std::list<sf::Sprite> EnemyList;
    std::list<sf::Sprite>::iterator EnemyIt;

    while (mMainWindow.isOpen())
    {
        sf::Event event;

        while (mMainWindow.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
            {
                mMainWindow.close();
            }
            if (event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Middle)
                                {
                for(EnemyIt = EnemyList.begin();EnemyIt != EnemyList.end();EnemyIt++)
                                        if (newSprite.getGlobalBounds().contains(mMainWindow.mapPixelToCoords(sf::Mouse::getPosition(mMainWindow))))
                                        {
                                                //EnemyList.reverse();
                                                std::cout << "Unit selected" << std::endl;
                                        }
                                }
            if(event.type == sf::Event::MouseButtonPressed)
                if(event.mouseButton.button == sf::Mouse::Right)
                {
                    sf::Vector2f mousecoords(mMainWindow.mapPixelToCoords(sf::Vector2i(event.mouseButton.x, event.mouseButton.y)));
                    EnemyList.remove_if([=](sf::Sprite unitsprite){return unitsprite.getGlobalBounds().contains(mousecoords); });
                }
            if(event.type == sf::Event::MouseButtonPressed)
                if(event.mouseButton.button == sf::Mouse::Left)
                    if (rectangle.getGlobalBounds().contains(mMainWindow.mapPixelToCoords(sf::Mouse::getPosition(mMainWindow))))        
                    {
                        newSprite.setTexture(unittexture);
                        newSprite.setPosition(sf::Mouse::getPosition(mMainWindow).x,sf::Mouse::getPosition(mMainWindow).y);
                        EnemyList.push_back(newSprite);
                    }
        }

        for(EnemyIt = EnemyList.begin();EnemyIt != EnemyList.end();EnemyIt++)
        {
            mMainWindow.draw(*EnemyIt);
        }
        mMainWindow.display();
        mMainWindow.clear(sf::Color(0, 200, 0, 255));
    }

    return 0;
}

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Selecting sprites by click.
« Reply #3 on: July 17, 2014, 05:07:29 pm »
So it's just EnemyIt->getGlobalBounds().contains(...), since EnemyIt is an iterator that points directly to a sf::Sprite. I'm surprised you got your drawing loop working but couldn't figure out this one, it's exactly the same thing.

And you don't have to call sf::Mouse::getPosition(mMainWindow), since you're processing a MouseButtonPressed event you can simply use event.mouseButton.x and event.mouseButton.y.
« Last Edit: July 17, 2014, 05:09:32 pm by Laurent »
Laurent Gomila - SFML developer

Bogdan

  • Jr. Member
  • **
  • Posts: 93
    • View Profile
Re: Selecting sprites by click.
« Reply #4 on: July 17, 2014, 06:20:42 pm »
Ok, everything worked out just well, thank you!
In this context I have another question: Is it possible to select only one (the topmost) unit at a time, if there are multiple units at the same position?

Jesper Juhl

  • Hero Member
  • *****
  • Posts: 1405
    • View Profile
    • Email
Re: Selecting sprites by click.
« Reply #5 on: July 17, 2014, 06:32:13 pm »
Of course it is - if you write the code to manage that.
If you are simply drawing stuff in a container from first to last, then it follows that any stuff later in the container will be on top of earlier stuff, so to find the one "on top" would be a simple matter of searching the container in reverse order and the first one to match the coordinates will be the top one.
More complicated scheemes can easily be conceived if you need something more complicated.

Edit: for the simple solution, look into rbegin(), rend().
« Last Edit: July 17, 2014, 06:34:40 pm by Jesper Juhl »

zsbzsb

  • Hero Member
  • *****
  • Posts: 1409
  • Active Maintainer of CSFML/SFML.NET
    • View Profile
    • My little corner...
    • Email
Re: Selecting sprites by click.
« Reply #6 on: July 17, 2014, 06:32:26 pm »
The most brain dead way would be to sort your enemy container by front to back, then iterate from the beginning to the end and when you get a hit break the loop.
Motion / MotionNET - Complete video / audio playback for SFML / SFML.NET

NetEXT - An SFML.NET Extension Library based on Thor

Bogdan

  • Jr. Member
  • **
  • Posts: 93
    • View Profile
Re: Selecting sprites by click.
« Reply #7 on: July 18, 2014, 03:52:26 am »
Ok, I' ve got it :-)

Code looks like this now:

   
std::list<sf::Sprite>::reverse_iterator EnemyRevIt;


         
   if (event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Middle)
                                {
                                for(EnemyRevIt=EnemyList.rbegin(); EnemyRevIt!=EnemyList.rend(); ++EnemyRevIt)    // go forward in reverse order (from end to begin) through list
                                        if (EnemyRevIt->getGlobalBounds().contains(mMainWindow.mapPixelToCoords(sf::Vector2i(event.mouseButton.x, event.mouseButton.y))))
                                        {

                                                EnemyList.rbegin();                
                                                EnemyRevIt->move(50,50);                                                                        //do some stuff on the sprite: only the topmost unit should be affected!
                                                std::cout << "unit selected" << std::endl;                     //print, whenever a unit was selected
                                                break;
                                        }
                                }

zsbzsb

  • Hero Member
  • *****
  • Posts: 1409
  • Active Maintainer of CSFML/SFML.NET
    • View Profile
    • My little corner...
    • Email
Re: Selecting sprites by click.
« Reply #8 on: July 18, 2014, 04:25:10 am »
FYI you don't need to have a separate iterator variable.

for (auto& enemy = EnemyList.rbegin(); enemy != EnemyList.rend(); ++enemy)
{
   .....
}

And you don't need this in the middle of your loop.

EnemyList.rbegin();
Motion / MotionNET - Complete video / audio playback for SFML / SFML.NET

NetEXT - An SFML.NET Extension Library based on Thor

Bogdan

  • Jr. Member
  • **
  • Posts: 93
    • View Profile
Re: Selecting sprites by click.
« Reply #9 on: July 18, 2014, 03:18:41 pm »
Ok, it worked, thx.