How to do drag and drop sprites?

I’ve been wracking my brain on this for a few days now. I am trying to make an open source Solitaire clone to teach myself MonoGame.

Basically I want to render a sprite that is dynamically sized (so that if I maximize the game the sprites resize based on the screen width). Then I want to be able to click on the sprite and drag it around, and when I’m dragging it I want the z-order to be on top. I then want to fire an event if I let go of my mouse when the sprite is dragged on top of another sprite object.

The only example code I found that worked with MonoGame was this:
http://xnafan.net/2013/07/simplified-xna-drag-and-drop/

Unfortunately, that code doesn’t let me dynamically resize anything, it just arbitrarily renders the object based on the size of the texture itself. It also will drag objects underneath other objects, which isn’t useful.

As you can see, the cards are rendering much larger than the card slots at the top (based on the full texture width rather than the screen width), and are stacked on top of each other wrong. The cards should be the same size and placement as the card slots at the top just one row down:

If someone could help me figure this out, it’d be greatly appreciated. The source code for what I’ve done so far is on GitHub:

Firstly, 500x726 is huge for cards. I would resize the cards to match the texture you use for the card slot (cardslot2), but that texture is not in GitHub (neither is back_purple).

The cards are rendered in the order that they are in the _dragDropController.Items list. To make a card appear on top, it needs to be removed from it’s current location in the list and added to the end of the list when the user clicks on the card.

1 Like

Thanks Konaju. I appreciate you taking a look at my code.

I did check in the textures, they’re just in the Content/assets folder inside the project instead of the root folder. I don’t know how to fix github since I’m new to git and am just using the built in team explorer.

The textures for the back_purple is the same exact size as the textures for the cardSlot array which is using rectangles to render, and those render comfortably with a resizable width and height. I can’t figure out how to render a drag and drop item with a width I define on the fly though. When I try this, it keeps the texture at the same size and position, and just drags around the visible area of the texture. While I could just batch resize all my asset files, that really seems like the wrong approach.

I’ve done a lot of manual resizing stuff so let me chime in here.

SpriteBatch can scale textures itself if you get the textures width from it then alter it. float w = (MyImage.Width *.5f); pass w into the spritebatch.draw(…,…,…, w,…) for the width parameter.

You should consider thinking of you’re card when it is in a slot or in hand or on the table as being in a STATE or state of being and have you’re controller track that state and act on it for drawing as well, for each state you should make a DrawCard method for that state and each possible state. Example DrawCardOnTable() DrawCardInHand() DrawCardInSlot(). These hold a separate spritebatch draw call any sort of with or height pre adjusting or postion-ing should be done in them that will be passed as parameters to you’re spritebatch draw call additionally you can allow you’re DrawCard methods to have the same parameters as spritebatch or less or even more.

Whenever you’re controller draws a card it should call the corresponding DrawTheCard method depending on the state the card is in. That way you are decoupling the specifics of drawing from the controller such as manually scaling textures into a separate code block, but not the drawing order.
At any time you draw a card even on the fly whatever you send to spritebatch it will draw it as such at least in immediate mode which im guessing you are using.

IF you want to dynamically resize you’re cards manually without using 3d or getting into matrices as you’re users shrink or enlarge the game window.
What you do is basically use a scale that you have designed you’re game in against the current window size to get a coefficient to multiply against the cards width and height as well as most things that have width and height in your game.

E.G.

public static float ScaledWidth( float width){
return currentwindow.Width / 1080;
}
public static float ScaledHeight( float height){
return currentwindow.Height / 720;
}
While the game window is the same size as you’re game was designed in at home, these return values will of course evaluate 1.0 and no change would occur when you alter the game windows size different to what you see yourself.
When different simply multiplying as a final value the cards width and height by these will alter there size before passing to spritebatch, note this also applys to click collision logic as well.

1 Like

I’m looking at the method here, I take it that you mean that I pass in the “scale” Vector2 variable to tell it how to resize the texture?

public void Draw(
Texture2D texture,
Nullable position,
Nullable destinationRectangle,
Nullable sourceRectangle,
Nullable origin,
float rotation,
Nullable scale,
Nullable color,
SpriteEffects effects,
float layerDepth
)

The idea you have of holding different methods for different card states makes a lot of sense. I think I understand what you’re saying, I will play with the code to see if I can get something working. This is becoming a bit less confusing to me so I definitely appreciate the help.

I’m a total n00b to gamedev/MonoDevelop and my c# is pretty rusty, so thanks for being patient with me. I’ll play with the code a bit more and see if I can get something working.

Hey guys my code is now working as expected now, yay :slight_smile:

My cards now resize and reposition as expected when I adjust the window size, and the drag and drop code is now working. Now I just have to start working on the logic to make it work how I want, which will require a lot of tweaking. But at least I’m not stuck anymore. My code is a bit sloppy but I’ll refactor it later once I’ve gotten some of the basic functionality working.

I really appreciate the guidance, thanks!