Slick Forums

Discuss the Slick 2D Library
It is currently Wed May 22, 2013 12:20 pm

All times are UTC




Post new topic Reply to topic  [ 6 posts ] 
Author Message
PostPosted: Fri Jun 01, 2012 7:57 pm 
Offline

Joined: Fri Jun 01, 2012 7:46 pm
Posts: 3
Hello, I am making a card game using Slick2D and I am having trouble programming how to pickup the cards. What I want to do is to position the mouse over the card and then have the user left click on the card in order to select that they are going to use that particular card. However the player can have 3 or more cards in their hand at any given moment in time, so I am wondering if I can make the images their own Shapes or Entities so that their coordinates and hit-boxes will stay with their current position on the screen. The images are all rectangular shaped playing cards so I was wondering if I could just create a new Rectangle shape using the image. Thank you for your time and help! :D


Top
 Profile  
 
PostPosted: Sat Jun 02, 2012 4:45 pm 
Offline
Slick Zombie

Joined: Sat Jan 27, 2007 7:10 pm
Posts: 1469
What you're looking for is basic collision. Here's two ideas:

1. For pixel-perfect collision, you can use a "mask" image. For example:
Image

On the left is the sprite the player would see. On the right is the "mask" which we'll use for collision (using Image.getColor). If the pixel under the mouse is white, then we consider it a "hit" i.e. the mouse is over the sprite.

2. We can use Slick's geometry for hit testing. Create a shape that matches the shape of your playing card. RoundedRectangle will probably be your best bet. Then use Shape.contains to see if the point is within the shape.


Top
 Profile  
 
PostPosted: Mon Jun 04, 2012 8:15 pm 
Offline

Joined: Fri Jun 01, 2012 7:46 pm
Posts: 3
Thank you for replying! :D For masking, would you have to create the mask in Slick for use of it? Also wouldn't the mask have to follow the original image everywhere on the screen? And would I have to create a separate mask for each card on screen?

You're second option was actually what I was thinking about doing. There's no way to create a Shape from an image is there? Like:

Code:
Image aceofspades = new Image("Data\aceofspades.jpg");

Shape aceofspadesshape = new Shape(aceofspades, 20, 20);

if(mouse.intersects(aceofspadesshape))
{
cardselected(aceofspadesshape);
}


Of course that being Pseudo-code. The RoundedRectangle Method doesn't have a parameter for image does it? :oops:


Top
 Profile  
 
PostPosted: Tue Jun 05, 2012 2:05 am 
Offline
Slick Zombie

Joined: Sat Jan 27, 2007 7:10 pm
Posts: 1469
Quote:
There's no way to create a Shape from an image is there?

Nope. How do you suppose that would work? ;) An image is a set of pixels, a shape is a set of points.

If you want to use Shape.contains, you'll need to create a polygon (i.e. Rectangle or RoundedRectangle) that lines up with whatever collision area you want in your image. You only need one polygon, and you don't need to move it around along with the card. Instead, just translate the mouse positions. If you rotate or scale the card, then yes, you'll need to also rotate/scale the polygon. (Since changing the polygon requires re-triangulation, which may be expensive, you should try to limit the amount of polygons you create and the number of times you rotate/scale/translate them per frame.)

The bitmap mask is faster and probably easier, but it also has drawbacks (i.e. no rotation/scaling). It might look like this:
Code:
... init ...
    //ideally both of these would be in the same sprite sheet to reduce texture binds!

    //an image of the playing card shape
    cardImage = new Image("card.png");

    //the mask
    maskImage = new Image("card_mask.png");

... when it comes time to do collision handling ...
boolean collision(flota mouseX, float mouseY, float cardX, float cardY) {
    float x = (int)(mouseX - cardX);
    float y = (int)(mouseY - cardY);
    if (x >= 0 && y >= 0 && x < maskImage.getWidth() && y < maskImage.getHeight()) {
        Color c = maskImage.getColor(x, y);
        return c == COLLISION_COLOR; //e.g. Color.white
    }
}


I'm also curious how you're rendering your cards? Using a single PNG per card is not very efficient -- it will lead to long loading times and a lot of unnecessary texture swapping. (In OpenGL, it's relatively expensive to switch textures, which is why it's often suggested to use sprite sheets backed by the same internal texture.) If you're interested in better performance and just generally better practice, here's some considerations:

  • Have a sprite sheet containing the "parts" to a card: a white rounded rectangle, and then a variety of transparent glyphs/images (numbers 2-10, jack/ace/king/queen). You then position each part appropriately, making it look like a single sprite. This is very efficient, requires almost no loading time overhead, etc. (You could take it a step further and use a 9-slice images to save texture space.)
  • Alternatively, you could do the above but rely on a Slick Rectangle/RoundedRectangle to render the white card. This would allow you to then use the shape for collision-checking.
  • If you're lazy, you could save each of the 52 cards as a separate image, and then pack them into a large (i.e. 1024x1024) texture atlas with an image packing tool. This is not an ideal solution as you'll be limited by the size of your texture atlas.
  • If you're really lazy, you could save each of the 52 cards as a separate PNG file, and then just use Image for each. This is a really horrible solution; it will cause extremely long loading times, bad performance due to texture swapping, filesize overhead in distribution, etc.


Top
 Profile  
 
PostPosted: Tue Jun 05, 2012 2:52 am 
Offline

Joined: Fri Jun 01, 2012 7:46 pm
Posts: 3
I really like the idea of using a roundedRectangle for collision checking, but wouldn't I have to make a polygon of each card for each card that has a possibility to be selected? Or at least translate the polygon to each card that has the potential to be selected on each round of the update loop? If that doesn't slow performance I feel like that's a pretty easy solution. :D.

The masking makes sense though. Then could I just set the Alpha value to 0 for the mask image and use that to check for mouse clicks? So would it look like:

Code:
g.drawImage(kingofhearts, 20, 20);
g.drawImage(aceofspades, 70, 20);

public void update(GameContainer gc, int delta) throws SlickException     
    {
Input input = gc.getInput();

if(collision(input.getMouseX(),input.getMouseY(),20,20) == true)
{
//Collision stuff
}

if(collision(input.getMouseX(),input.getMouseY(),70,20) == true)
{
//Collision stuff
}
}


Yea I was just really lazy and didn't want to figure all the coordinates of each card :P . The original image is a spritesheet of all the cards so i'll definitely do that in the future. :D .

Thank you for all your help! :D


Top
 Profile  
 
PostPosted: Tue Jun 05, 2012 3:12 am 
Offline
Slick Zombie

Joined: Sat Jan 27, 2007 7:10 pm
Posts: 1469
Since this sounds like a very simple game, you can get away with many RoundedRectangle objects even if a number of them are moving at the same time. (Java is pretty fast :)) For now, just do whatever feels comfortable, and optimize later.

If you want to use a single instance of RoundedRectangle, the idea is: Create the rounded rectangle at "origin" (0, 0), and then translate the mouse coordinates to origin before testing with Shape.contains.

For example, the card image is at (30, 30). You've created a RoundedRectangle at location (0, 0) which has the same size as your card image. Say the mouse is moved to (32, 40) -- you subtract the card's location from the mouse position, leaving you with (2, 10), and then you test that location using Shape.contains.

Rotation will make this a little trickier, and requires transforming the rounded rectangle shape before testing. Like I said, just use whatever feels best for now.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 6 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 4 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group