SDL dragging the correct image from a list of images

Started by
6 comments, last by rn216 15 years, 8 months ago
Hi there, I am new to this forum and I have some questions. Eventually, I would like to build a 500 rummy game in C++ and using the SDL library. For now, however, I am stuck when it comes to knowing which card is being dragged and to see if it is in a draggable stack pile. I have came up with somewhat an idea in my head that maybe I should create a one big event handling engine. The engine would then call the appropriate onMouseDown function, (sort of like &#106avascript), but I have no idea where to start. Can any one give me some ideas on this. Should I be looking at tutorials on building an Event Manager?
Advertisement
Quote:Original post by rn216
Hi there,
I am new to this forum and I have some questions. Eventually, I would like to build a 500 rummy game in C++ and using the SDL library. For now, however, I am stuck when it comes to knowing which card is being dragged and to see if it is in a draggable stack pile. I have came up with somewhat an idea in my head that maybe I should create a one big event handling engine. The engine would then call the appropriate onMouseDown function, (sort of like &#106avascript), but I have no idea where to start. Can any one give me some ideas on this. Should I be looking at tutorials on building an Event Manager?

SDL has it's events laid out in a pretty clean way already. If you need help on SDL in particular, read this tutorial.

If you are familiar with Classes, I'd do it like this:

enum CARD_TYPE{CARD_TWO, CARD_THREE, CARD_FOUR, ..., CARD_NINE, CARD_TEN, CARD_JACK, CARD_QUEEN, CARD_KING, CARD_ACE};enum CARD_SUIT{CARD_DIAMOND = 0, CARD_CLUB, CARD_HEART, CARD_SPADE};enum CARD_STATUS{CARD_PLAYED, CARD_IN_DECK, CARD_PLAYER1_HAND, CARD_PLAYER2_HAND, ..., CARD_DISCARD_PILE};class PlayingCard{   private:   CARD_TYPE cardType; //Store the information of the card. It's value, and suit.    CARD_SUIT cardSuit;       CARD_STATUS cardStatus; //Where the card currently is. (In the deck, in the discard pile, in a player's hand, on the board played)      public:      void SetType(CARD_TYPE type){cardType = type;}   CARD_TYPE GetType(){return cardType;}      void SetSuit(CARD_SUIT suit){cardSuit = suit;}   CARD_SUIT GetSuit(){return cardSuit;}      void SetStatus(CARD_STATUS status){cardStatus = status;}   CARD_STATUS GetStatus(){return cardStatus;}      void Event(SDL_Event *event); //Pass events into the playing card, so the playing card can deal with only the events it needs to.   void Draw(int x, int y, SDL_Surface *screen); //Let the card draw itself, based on it's suit and value.};


52 cards would be created at the start of the game, you'd point a pointer to each of the 52 cards, and shuffle the pointers. You'd then 'deal' the pointers to each of the players in turn, until they each have 7 cards. 'Player' would be a class too, and they'd have a vector or something simular, that's their 'hand'. That vector would be filled with the Card pointers that that player has. After dealing the cards, the leftover cards would be put into a 'deck' class, except the very first card which would be placed into a 'DiscardPile' class.
class DiscardPile{   private:      std::vector <Card*> cards;      public:      void AddCardToPile(Card *newCard);   void RemoveCard(); //Removes the top card.      Card *GetTopCard();      void Event(SDL_Event *event);   void Draw(SDL_Surface *screen); //This calls Draw() for each of the cards in the DiscardPile,                                   //and draws them next to each other, slightly overlapping.};


class Deck{   private:      std::vector <Card*> cards;      public:      void AddCardToDeck(Card *newCard); //Used for filling the deck at the beginning of the round.      void RemoveCard(); //Removes the top card.   Card *GetTopCard();      void Event(SDL_Event *event);    void Draw(SDL_Surface *screen); //The deck can draw itself. Just draw one upside-down card for each                                   //card in the deck, drawing each card one or two pixels to the                                   //left/right/up/down of the previous card, so it looks like the cards are stacked.};


Each class would handle it's own events, having no need for some central event handling engine, and would actually be easier to make, manage, add more features to, and easier to read and understand. At least, in my opinion it would be.
A couple of problems I misunderstanding.

In your DiscardPile and Deck class, both of them are creating a vector variable like this...

std::vector<Card*> cards. However, there is no data type called Card. I am assuming you mean to say this...

std::vector<PlayingCard*> cards?

2. I am not quite understanding the event function.

For one, if the deck and deckpile class has their own event handling, then why would you need to do another event check inside of the PlayingCard class?


Lastly, I am not too positive how the event functions would be called. I am assuming you are saying to do something like this...

void Game::Play(){     bool quit = false;     While(!quit) {          While(SDL_PollEvent(&event)) {               switch(event.type) {                    case SDL_QUIT;                         quit = true;                         break;                    case Default:                         discardPile.event(&event);                         deck.event(&event);                         handPile1.event(&event);                         break;          }     }}


... if so, isn't this redundant?
Quote:Original post by rn216

In your DiscardPile and Deck class, both of them are creating a vector variable like this...

std::vector<Card*> cards. However, there is no data type called Card. I am assuming you mean to say this...

std::vector<PlayingCard*> cards?

Yes, sorry. It wasn't real code, but me showing an example of how I'd do it, so yeah, I made a mistake. [smile]
Quote:2. I am not quite understanding the event function.

For one, if the deck and deckpile class has their own event handling, then why would you need to do another event check inside of the PlayingCard class?

I showed two different ways of doing it, but failed to draw the line where one method started and the other stopped. I apologize for the confusion. I was trying to do two things at once, and must've lost my train of thought.

Method 1 was having each card manage it's own events, with a state being stored, of where the card currently is. This is the first code example, with 'PlayingCard' having it's location stored in the 'CARD_STATUS' enum. (CARD_PLAYED, CARD_IN_DECK, CARD_PLAYER1_HAND, CARD_PLAYER2_HAND, ..., CARD_DISCARD_PILE)

The second method, and probably the better if the two, was having classes manage the card, depending on where the card is. The cards themselves wouldn't know their own usage, but would be handled by the classes 'DiscardPile', 'Deck', 4 (or more) instances of 'Player' or 'PlayerHand', and ect... This was the second two code examples.

I'd have it so DiscardPile->Event() only handles events regarding to the DiscardPile itself. The discarding pile would be complicated enough itself, because you can't draw cards from the discard pile unless you can actually use those cards, you cannot draw cards unless it's the beginning of your turn, you must discard a card at the end of your turn, if you draw a card that's not at the top of the pile - you must draw every card between that card and the top, and etc... Making DiscardPile->Event() also handle individual events for each card over complicates the function.

The class I named 'Deck' might confuse you, because I don't mean the full 52 cards of the deck, I mean the stack of cards upside down next to the discard pile. I've always called this the 'Deck' while playing, but Wikipedia tells me the proper name is called the 'Stock', so you might prefer to name it that.

Quote:Lastly, I am not too positive how the event functions would be called.

*** Code Snippet Removed ***

Basically that's correct, minor personal preferences aside.
Quote:... if so, isn't this redundant?

Not really. It'd be more redundant to have 'Game' manage another class that manages the classes that manage the cards, and it'd be more ugly to have 'PlayingCard' handle every single event that's needed to manage itself.

By splitting the management of individual cards, based on how the cards are being used (Players' hands, on the playing board, in the discard pile, in the deck/stock), you simplify the code into manageable chunks that's easier to read and expand upon.

[Edited by - Servant of the Lord on July 26, 2008 10:51:18 PM]
Hi,
I do not mean to ignore you, and I really do appreciate your help. However, I am having some real troubles trying to put this together. I think the biggest problem I am having is that I have never created a drag image system with SDL before.

Also,
Quote:
... if so, isn't this redundant?


Quote:Not really. It'd be more redundant to have 'Game' manage another class that manages the classes that manage the cards, and it'd be more ugly to have 'PlayingCard' handle every single event that's needed to manage itself.


Well, I guess the word redundant is not what I really meant. What I mean to ask is if the user clicked on a discardPile card, for example, then why would it make sense to call the other class event handling when the system already knows that the mouse down event happened through the discardPile class. IMHO, the class event handlers should return a boolean or maybe specifying which card was clicked on. Wouldn't that make more sense?

For example...
instead of this...
void handleDeckPileEvent(SDL_Event *event);

do this...

bool handleDeckPileEvent(SDL_Event *event);

or

Card * handleDeckPileEvent(SDL_Event *event);
I cannot believe this. No one ever needed to drag images with the mouse using SDL? I have just spent 6 hours on google looking for keywords like SDL drag image, SDL mouse drag etc., and I got nothing that fits with-in my topic. Can someone help me?
Quote:Original post by rn216
instead of this...
void handleDeckPileEvent(SDL_Event *event);

do this...

bool handleDeckPileEvent(SDL_Event *event);

That's actually what I do with my current project, although I'm not dragging around anything. I only decided to do that because I have 14-15 different classes requiring events, and I didn't want objects under each other to recieve mouse clicks.
Quote:I cannot believe this. No one ever needed to drag images with the mouse using SDL? I have just spent 6 hours on google looking for keywords like SDL drag image, SDL mouse drag etc., and I got nothing that fits with-in my topic. Can someone help me?

What is it exactly that you are having trouble with? The dragging itself?

If so, I just knocked up a small application allowing you to drag around a deck of cards. It's not organized in any manner for a card game (no classes managing the cards), but it shows how to drag cards around.

[img 1] [img 2] [img 3]

Here's a link to the executable, with card images, and with the source: [download]

Here's the code:
//DragCard.h#include "SDL\SDL.h"#include "SDL\SDL_image.h"#ifndef DRAGCARD_H#define DRAGCARD_Henum CARD_TYPE{CARD_TWO = 0, CARD_THREE, CARD_FOUR, CARD_FIVE, CARD_SIX, CARD_SEVEN, CARD_EIGHT,               CARD_NINE, CARD_TEN, CARD_JACK, CARD_QUEEN, CARD_KING, CARD_ACE};enum CARD_SUIT{CARD_DIAMOND = 0, CARD_CLUB, CARD_HEART, CARD_SPADE};const int NUM_CARDS_IN_DECK = 52;const int NUM_CARDS_PER_SUIT = 13;const int CARD_IMAGE_WIDTH = 75;const int CARD_IMAGE_HEIGHT = 125;class MyCard{	private:	int locX, locY; //The location of the card on the board.	int clickX, clickY; //The location the mouse clicked, on the card itself.	bool dragging; //True if the card is being dragged		SDL_Surface *cardImage;	SDL_Surface *highlight; //Used to draw a outline around a card.		//The reason I don't stick all the enums in a single 'CARD_FACE' or 'CARD_VALUE' set,	//is to make it easier to find matches. 3-of-a-kinds, 4-of-a-kinds, etc...	CARD_TYPE cardType;	CARD_SUIT cardSuit;		public:	MyCard::MyCard(CARD_TYPE type, CARD_SUIT suit);	MyCard::~MyCard();		void Draw(SDL_Surface *screen);	bool Event(const SDL_Event &event);};#endif /* DRAGCARD_H */


//DragCard.cpp#include "DragCard.h"#include <iostream>std::string CardTypeAsString[] = {"two", "three", "four", "five", "six", "seven", "eight",               					  "nine", "ten", "jack", "queen", "king", "ace"};std::string CardSuitAsString[] = {"diamond", "club", "heart", "spade"};MyCard::MyCard(CARD_TYPE type, CARD_SUIT suit){	cardType = type;	cardSuit = suit;		locX = 25; //Starting loc. In the corner of the screen.	locY = 25;	clickX = 0;	clickY = 0;	dragging = false;		cardImage = NULL;	highlight = NULL;		//Load image here.	std::string filename = ".\\cards\\card_"+CardSuitAsString[suit]+"_"+CardTypeAsString[type]+".png";		std::cout << "Loading card: [" << CardTypeAsString[type] << " of " << CardSuitAsString[suit] << "s]" << std::endl;		cardImage = IMG_Load(filename.c_str());	if(!cardImage)		std::cout << "Error loading the card's image.\n\tReason: " << SDL_GetError() << std::endl;	else		SDL_SetColorKey(cardImage, SDL_SRCCOLORKEY, 0x00);		highlight = IMG_Load(".\\card_highlight.png");	if(!highlight)		std::cout << "Error loading the highlight image.\n\tReason: " << SDL_GetError() << std::endl;	else		SDL_SetColorKey(highlight, SDL_SRCCOLORKEY, 0x00);}MyCard::~MyCard(){	SDL_FreeSurface(highlight);	SDL_FreeSurface(cardImage);}void MyCard::Draw(SDL_Surface *screen){	SDL_Rect rect;	rect.x = locX;	rect.y = locY;		//Draw the card at it's 'locX' and 'locY' location.	SDL_BlitSurface(cardImage, NULL, screen, &rect);		//If we are dragging the card, highlight the card also.	if(dragging)	{		rect.x -= 2;		rect.y -= 2;		SDL_BlitSurface(highlight, NULL, screen, &rect);	}}bool MyCard::Event(const SDL_Event &event){	switch(event.type)	{		case SDL_MOUSEMOTION:		{			//If we are moving the mouse, move the card with it.						if(!dragging)				return false;						//We use 'clickX' and 'clickY', to move the card from the point we grabbed it.			locX = event.motion.x - clickX;			locY = event.motion.y - clickY;		}		break;		case SDL_MOUSEBUTTONDOWN:		{			//If we are clicking, check to see if we are clicking on a card.						int mouseX = event.motion.x;			int mouseY = event.motion.y;						//Check if the mouse is over this card.			if(mouseX > locX && mouseX < (locX+CARD_IMAGE_WIDTH)			&& mouseY > locY && mouseY < (locY+CARD_IMAGE_HEIGHT))			{				clickX = mouseX - locX;				clickY = mouseY - locY;								dragging = true;								return true;			}		}		break;		case SDL_MOUSEBUTTONUP:		{			//If we are releasing of mouse key, let go of the card.						if(!dragging)				return false;						dragging = false;		}		break;		/*		case :			//This is in a switch statement, because you might want to add key-commands as well.			//Maybe hitting 'ESC' cancels the movement.		break;		*/		default: break;	}		return false;}


//main.cpp#include "DragCard.h"#include <iostream>#include <vector>int main(int argc, char *argv[]){	//Setup.		SDL_Init(SDL_INIT_EVERYTHING);		SDL_Surface *screen = SDL_SetVideoMode(800, 600, 32, SDL_SWSURFACE);	if(!screen)	{		std::cout << "Error setting up the window.\n\tReason: " << SDL_GetError() << std::endl;		return 0;	}		SDL_WM_SetCaption("Card Dragging Example in SDL", NULL);		SDL_Surface *background = IMG_Load("background.png");	if(!background)	{		std::cout << "Error loading the background.\n\tReason: " << SDL_GetError() << std::endl;		return 0;	}		std::vector <MyCard*> cardArray;		for(int i = 0; i < NUM_CARDS_PER_SUIT; i++)		cardArray.push_back(new MyCard((CARD_TYPE) i, CARD_DIAMOND));	for(int i = 0; i < NUM_CARDS_PER_SUIT; i++)		cardArray.push_back(new MyCard((CARD_TYPE) i, CARD_CLUB));	for(int i = 0; i < NUM_CARDS_PER_SUIT; i++)		cardArray.push_back(new MyCard((CARD_TYPE) i, CARD_HEART));	for(int i = 0; i < NUM_CARDS_PER_SUIT; i++)		cardArray.push_back(new MyCard((CARD_TYPE) i, CARD_SPADE));		//Running.		bool quit = false;		while(!quit)	{		SDL_Event event;		while(SDL_PollEvent(&event))		{			if(event.type == SDL_QUIT)				quit = true;			else			{				//Cycle through the cards, running the events, until one returns true, or we're out of cards.				for(int i = 0; i < NUM_CARDS_IN_DECK; i++)					if(cardArray[(NUM_CARDS_IN_DECK-1)-i]->Event(event)) break;			}		}				//Draw the background.		SDL_BlitSurface(background, NULL, screen, NULL);				//Draw the cards.		for(int i = 0; i < NUM_CARDS_IN_DECK; i++)			cardArray->Draw(screen);				SDL_Flip(screen);				SDL_Delay(20);	}		//Cleanup.		SDL_FreeSurface(background);		cardArray.clear();		SDL_Quit();		return 0;}


There's some minor lag time between the cards and the mouse. If you want to get rid of that, disable the mouse with SDL_ShowMouse(SDL_DISABLE), and draw your own mouse pointer. MicroSoft Windows updates the mouse on-screen before SDL gets the messages and has a chance to redraw, so the mouse is usually ahead of the cards, but not by much.
I am sorry about the delay, but I wanted to thank you. This is much appreciated.

This topic is closed to new replies.

Advertisement