• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Nie działa kod złap-przeciągnij-puść, C++ SFML

Object Storage Arubacloud
+1 głos
395 wizyt
pytanie zadane 25 kwietnia 2018 w C i C++ przez DarkEliat Bywalec (2,810 p.)

Witam.

Piszę kod, który po kliknięciu i przytrzymaniu elementu (karty) myszką przesuwa go w wybrane miejsce.

Może od razu przejdę do kodu:

// Clickable.h
#pragma once
#include <SFML\System.hpp>

using namespace sf;

class Clickable
{
public:
	Clickable();
	~Clickable() = default;

	bool isClickableNow();
	void enableClicking();
	void disableClicking();
	void toggleClicking();

	virtual bool wasClicked() = 0;

	// Sprawdza czy na danym obiekcie trzymany jest obecnie lewy przycisk myszy 
	virtual bool isClickedNow() = 0; 

protected:
	Vector2i savedMousePosition;
	// Jest równa 'true' jeśli nad danym obiektem kliknięty jest LPM
	bool clicked;

	// Ta zmienna jest równa 'true' jeśli jakiś inny obiekt
	// jest aktualnie klikany (kliknięty LPM); 
	// Blokuje ona możliwość kliknięcia innych obietków, ponieważ 
	// takowy może być tylko jeden na raz
	static bool blockedClickingAnotherObjects;

private:
	virtual bool isHover() = 0;

	bool clickableNow;
};

// Clickable.cpp
#include "Clickable.h"

Clickable::Clickable()
{
	clickableNow = true;
}

bool Clickable::isClickableNow()
{
	return clickableNow;
}

void Clickable::enableClicking()
{
	clickableNow = true;
}

void Clickable::disableClicking()
{
	clickableNow = false;
}

void Clickable::toggleClicking()
{
	// Skrócony zapis if'a
	clickableNow ? disableClicking() : enableClicking();
}
// Dragable.h
#pragma once
#include <SFML\Graphics.hpp>
#include "Clickable.h"

using namespace sf;

class Dragable : public Clickable
{
public:
	Dragable() = default;
	~Dragable() = default;

	virtual void drag() = 0;

protected:
	// Dystans pomiędzy punktem początkowym obiektu a myszą w poziomie 
	float distanceX;
	// Dystans pomiędzy punktem początkowym obiektu a myszą w pionie 
	float distanceY;
};

I teraz najważniejsza klasa - Card, tworzy ona obiekty, które mam zamiar przesuwać:

// Card.h
#pragma once
#include <SFML/Graphics.hpp>
#include "GraphicsController.h"
#include "Caption.h"
#include "Dragable.h"


using namespace sf;

class Card : public Sprite, public Dragable
{
public:
	Card();
	Card(string textureAndCardName);
	~Card();

	string getCardName();
	void setCard(string textureAndCardName);

	virtual bool wasClicked();
	virtual bool isClickedNow();

	virtual void drag();
	
	vector<string> neighboringCards;

private:
	virtual bool isHover();

	string textureAndCardName; // Nazwa karty i tekstury karty, który są takie same
	bool addedToDeck;
};

// Card.cpp
#include "Card.h"
#include "Deck.h"
#include "Controller.h"

bool Dragable::blockedClickingAnotherObjects;

Card::Card()
{
	addedToDeck = false;
}

Card::Card(string textureAndCardName)
{
	setCard(textureAndCardName);
}

Card::~Card()
{
	if(addedToDeck == true)
		Deck::removeCard(textureAndCardName);
}

string Card::getCardName()
{
	return textureAndCardName;
}

void Card::setCard(string textureAndCardNameToSet)
{
	// Przeszukiwanie talii w celu sprawdzenia możliwości dodania karty o podanej nazwie
	auto iterator = Deck::getDeck()->find(textureAndCardName);

	// Jeśli nie znalazł istniejącej karty o podanej nazwie, to można ją dodać
	if (iterator == Deck::getDeck()->end())
	{
		setImageToSprite(*this, textureAndCardNameToSet);

		this->textureAndCardName = textureAndCardNameToSet;

		Deck::addCard(*this, textureAndCardNameToSet);

		addedToDeck = true;
	}
	else
	{
		cout << convertToPolishChars("Karta o nazwie '" + textureAndCardNameToSet + "' już istnieje! Nie można jej utworzyć ponownie!") << endl;
	}
}

bool Card::wasClicked()
{
	clicked = false;

	bool mouseButtonClicked = Mouse::isButtonPressed(Mouse::Button::Left);

	// Jeśli nie jest kliknięty LPM, odblokuj możliwość klikania
	if (!mouseButtonClicked)
	{
		blockedClickingAnotherObjects = false;
		return false;
	}

	if (blockedClickingAnotherObjects) return false;
	
	// Jeśli kliknięto LPM
	if (mouseButtonClicked && !blockedClickingAnotherObjects)
	{
		blockedClickingAnotherObjects = true;

		// Jeśli mysz znajduje się nad danym obiektem
		if (isHover())
		{
			// Pobranie wskaźnika do okna gry w celu uzyskania poprawnego położenia myszy
			RenderWindow *window = Controller::getReference().getRenderWindow();

			savedMousePosition = Mouse::getPosition(*window);
			clicked = true;

			distanceX = this->getPosition().x - savedMousePosition.x;
			distanceY = this->getPosition().y - savedMousePosition.y;

			return true;
		}
		else
			return false;
	}

	return false;
}

bool Card::isClickedNow()
{
	// Jest jeszcze obiekt nie jest kliknięty to sprawdź
	// czy przypadkiem to się przed chwilą nie stało
	if (!clicked) wasClicked();

	// Jeśli jeszcze przed chwilą obiekt był kliknięty
	if (clicked)
	{
		// Jeśli obiekt został już puszczony (Puszczono LPM)
		if (!Mouse::isButtonPressed(Mouse::Button::Left))
		{
			clicked = false;
			return false;
		}
		// Jeśli dalej nad obiektem jest kliknięty LPM
		else return true;
	}
	else
		return false;
}

void Card::drag()
{
	if (isClickedNow())
	{
		Vector2f objectPosition(this->getPosition());

		// Pobranie wskaźnika do okna gry w celu uzyskania poprawnego położenia myszy
		RenderWindow *window = Controller::getReference().getRenderWindow();

		Vector2f currentMousePosition(Mouse::getPosition(*window));
		this->setPosition(currentMousePosition.x + distanceX, currentMousePosition.y + distanceY);
	}
}

bool Card::isHover()
{
	// Pobranie wskaźnika do okna gry w celu uzyskania poprawnego położenia myszy
	RenderWindow *window = Controller::getReference().getRenderWindow();
	// Pobieranie położenia myszy z uwzględnieniem okna gry
	Vector2i currentMousePosition = Mouse::getPosition(*window);

	IntRect objectRect(this->getGlobalBounds());

	//cout << textureAndCardName << endl;
	//cout << "x:" << objectRect.left << "y:" << objectRect.top << "w:" << objectRect.width << "h:" << objectRect.height << endl;
	//cout << "x:" << currentMousePosition.x << "y:" << currentMousePosition.y << endl << endl;

	// Sprawdzenie czy mysz znajduje się obecnie nad obiektem
	if (objectRect.contains(currentMousePosition)) return true;
	else return false;
}

 

Cały problem siedzi moim zdaniem w metodzie bool Card::wasClicked();

W momencie kiedy kod wygląda tak:

bool Card::wasClicked()
{
	clicked = false;

	bool mouseButtonClicked = Mouse::isButtonPressed(Mouse::Button::Left);

	// Jeśli nie jest kliknięty LPM, odblokuj możliwość klikania
	if (!mouseButtonClicked)
	{
		blockedClickingAnotherObjects = false;
		return false;
	}

        // DO TEGO MOMENTU KAŻDY OBIEKT DOCHODZI        

	if (blockedClickingAnotherObjects) return false;

        // POWYŻSZY TEST PRZECHODZI TYLKO JEDEN OBIEKT,
        // KTÓRY JEST PIERWSZYM W TALII (INNA KLASA)
	
	// Jeśli kliknięto LPM
	if (mouseButtonClicked && !blockedClickingAnotherObjects)
	{
                // ISTOTNA JEST TA LINIJKA
		blockedClickingAnotherObjects = true;

		// Jeśli mysz znajduje się nad danym obiektem
		if (isHover())
		{
			// Pobranie wskaźnika do okna gry w celu uzyskania poprawnego położenia myszy
			RenderWindow *window = Controller::getReference().getRenderWindow();

			savedMousePosition = Mouse::getPosition(*window);
			clicked = true;

			distanceX = this->getPosition().x - savedMousePosition.x;
			distanceY = this->getPosition().y - savedMousePosition.y;

			return true;
		}
		else
			return false;
	}

	return false;
}

To przesuwalny jest tylko pierwszy obiekt w talii,

jeśli jednak kod wygląda tak:

bool Card::wasClicked()
{
	clicked = false;

	bool mouseButtonClicked = Mouse::isButtonPressed(Mouse::Button::Left);

	// Jeśli nie jest kliknięty LPM, odblokuj możliwość klikania
	if (!mouseButtonClicked)
	{
		blockedClickingAnotherObjects = false;
		return false;
	}     

	if (blockedClickingAnotherObjects) return false;

        // WYDAJE SIĘ, ŻE JEST OK, KAŻDY OBIEKT SIĘ PRZESUWA PRAWIDŁOWO,
        // ALE KLIKAJĄC W TŁO (TRZYMAJĄC LPM)
        // I NAJEŻDŻAJĄC NA KARTĘ, TO ONA SIĘ PRZYKLEJA (A NIE POWINNA)
	
	// Jeśli kliknięto LPM
	if (mouseButtonClicked && !blockedClickingAnotherObjects)
	{
		// Jeśli mysz znajduje się nad danym obiektem
		if (isHover())
		{
                        // TA LINIJKA ZMIENIŁA MIEJSCE
		        blockedClickingAnotherObjects = true;

			// Pobranie wskaźnika do okna gry w celu uzyskania poprawnego położenia myszy
			RenderWindow *window = Controller::getReference().getRenderWindow();

			savedMousePosition = Mouse::getPosition(*window);
			clicked = true;

			distanceX = this->getPosition().x - savedMousePosition.x;
			distanceY = this->getPosition().y - savedMousePosition.y;

			return true;
		}
		else
			return false;
	}

	return false;
}

Wtedy wszystkie karty przesuwają się prawidłowo, jednak klikając w tło i dojeżdżając myszką nad kartę, to wtedy ona się przykleja. Oczywiste jest, że taki efekt jest niepożądany i powinno to działać jak przesuwanie ikonek na pulpicie.

Jakby ktoś chciał zobaczyć jak działa Talia i kiedy wywołuję metodę drag() to proszę pisać.

Z góry dziękuję za pomoc!

1
komentarz 25 kwietnia 2018 przez j23 Mędrzec (194,920 p.)

Nie jestem specjalistą od SFML-a, ale dlaczego nie użyjesz zdarzeń MouseButtonPressed, MouseButtonReleased, MouseMoved i nie podzielisz przeciągania na trzy etapy: begin-drag, drag i drop?

 

IMO trochę dziwne, że karta implementuje przeciąganie samej siebie.

komentarz 25 kwietnia 2018 przez DarkEliat Bywalec (2,810 p.)
No teoretycznie można by tak zrobić (średnio znam się na zdarzeniach).

A odnośnie implementacji przeciągania, to w jaki inny sposób proponujesz to zrobić?
komentarz 26 kwietnia 2018 przez j23 Mędrzec (194,920 p.)
edycja 26 kwietnia 2018 przez j23

Jakoś tak to widzę:

sf::Vector2i	pos;
bool 		lmb_is_pressed = false;
bool 		dragging = false;
...

sf::Event  event;

while (window.pollEvent(event))
{
	...

	switch(event.type)
	{
	case sf::Event::MouseButtonPressed:
		if(event.mouseButton.button == sf::Mouse::Left)
		{
			pos = sf::Vector2i(event.mouseButton.x, event.mouseButton.y);
			lmb_is_pressed = true;
		}
		break;

	case sf::Event::MouseButtonReleased:
		if(event.mouseButton.button == sf::Mouse::Left)
		{
			if(dragging)
			{
				onDrop(sf::Vector2i(event.mouseButton.x, event.mouseButton.y))
				dragging = false;
			}
			lmb_is_pressed = false;
		}
		break;

	case sf::Event::MouseMoved:
		if(lmb_is_pressed) 
		{
			sf::Vector2i pos2(event.mouseMove.x, event.mouseMove.y);

			if(!dragging)
			{
				pos2 -= pos;
				if(sqrt(pos2.x * pos2.x + pos2.y * pos2.y) > 4)
				{
					lmb_is_pressed = dragging = onBeginDrag(pos);
				}
			}
			else 
			{
				onDrag(pos2);
			}
		}
		break;
	};

	...
}



bool onBeginDrag(const sf::Vector2i &pos)
{
	...
	return // zwraca true, jeśli przeciąganie ma się rozpocząć
}

void onDrag(const sf::Vector2i &pos)
{
	...
}

void onDrop(const sf::Vector2i &pos)
{
	...
}

 

komentarz 26 kwietnia 2018 przez DarkEliat Bywalec (2,810 p.)
Wow, ten kod wygląda świetnie! Nie jestem pewien czy to zadziała, ale zaimplementuję to do gry i Ci napiszę, może uda się wyeliminować te błędy. Dzięki wielkie za poświęcony czas ;)
komentarz 26 kwietnia 2018 przez j23 Mędrzec (194,920 p.)
Potraktuj go jako sugestię, bo sam nie wiem, czy działa ;)

Zaloguj lub zarejestruj się, aby odpowiedzieć na to pytanie.

Podobne pytania

0 głosów
1 odpowiedź 151 wizyt
pytanie zadane 9 lutego 2016 w JavaScript przez Bartess Gaduła (3,630 p.)
0 głosów
1 odpowiedź 256 wizyt
pytanie zadane 14 listopada 2020 w C i C++ przez Empire Nowicjusz (140 p.)
0 głosów
0 odpowiedzi 133 wizyt

92,568 zapytań

141,420 odpowiedzi

319,622 komentarzy

61,954 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...