Slick Forums

Discuss the Slick 2D Library
It is currently Thu May 23, 2013 8:14 pm

All times are UTC




Post new topic Reply to topic  [ 21 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Thu Mar 03, 2011 6:51 pm 
Offline
Game Developer
User avatar

Joined: Thu Mar 03, 2011 6:22 pm
Posts: 534
Ohai! Well I'm new here so be nice!

Slick is awesome, working with it is really easy and smooth and all...
But! There is no collision class for images. (?)

So I made 2! One for none rotating(or to stretching) images which is also really fast and another for rotating (and stretching) images . But both are pixel precise!
Well you might ask: "WHY?" Because the second class it slower than the first one... This is because the second one uses the GPU to check collision (Stencil Buffer)
To use this second way you also must create a subclass of AppGameContainer, copy the methods setup and tryCreateDisplay and change the Display.create(new PixelFormat(8,8,0)) method to this:
Code:
Display.create(new PixelFormat(8,8,0));


I know this is kinda... stupid but I hope with this kev changes the AppGameContainer so that we can set some stencil bits :D (PLEASE kev :()

Here is the zip containing the .jar the source and the doc:
http://npshare.de/files/636c556f/Collision.zip

Usage of CollisionTest:
enter collision-test, check a set of images, exit collision-test!

Hope you like it :)
If you have some ideas how to make things better or faster... just say it!
(maybe some automatic bounds setting for the CollisionTest?)

R.D.

PS. sorry for my bad English... need some practice...

Edit: fixed small bug

Edit²:
- fixed the naming issue
- added the possibility to wait for the pixel count (Sometimes the GPU needs some time to send the pixel count to the CPU).

_________________
Current Projects:
Image Mr. Hat I
Image Vegan Vs. Zombies
Projects:
RadicalFish Engine - Build on top of Slick2D, Ideas, Bugs? Open an Issue ticket!


Last edited by R.D. on Tue Mar 15, 2011 6:26 am, edited 3 times in total.

Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 04, 2011 1:29 pm 
Offline

Joined: Fri Mar 04, 2011 12:34 pm
Posts: 24
I dont like that bitmask. So Ive created another version thats more straightforward and, I suppose, easier to use.
First of all what I dont like:
1.)The worst thing you can do, repeating code.
These lines appear like 4 times in the code:
Code:
for(int i = 0; i < this.bits[0].length; i++) {
   for(int j = 0; j < this.bits.length; j++) {
      if(!image.getColor(j, i).equals(color))
         this.bits[j][i] = true;
   }
}


2.)The class does 2 things. It creates the bitmask and checks for intersections. Classes and methods should always do one thing.

3.)To many senseless constructors. Why would you want to create an empty bitmask? And why should one always overwrite 2 existing Bitmasks for collision check? This would only obscure the code of the user. Nevermind the performance loss.

4.)It does look like you made this in a haste. Why else is this method named "intersets"?
Code:
public boolean intersets(BitMask bitmask) throws SlickException{...


5.)Too many comments...


So heres what I made.
The Bitmask:
Code:
public class Bitmask implements Serializable {
   private static final long serialVersionUID = -5909146496764863208L;
   private boolean collisionPixel[][];
   private int width, height;
   private FloatPoint coordinates;

   public Bitmask(Image image, Color nonCollisionColer, FloatPoint coordinates)
         throws Exception {
      this.createBitmaskFromImage(image, nonCollisionColer);
      this.coordinates = coordinates;
      width = collisionPixel.length;
      height = collisionPixel[0].length;
   }

   public static Bitmask createBitmaskWithTopLeftPixelAsNonCollisionColor(
         Image image, FloatPoint coordinates) throws Exception {
      return createBitmask(image, image.getColor(0, 0), coordinates);
   }

   public static Bitmask createBitmask(Image image, Color nonCollisionColer,
         FloatPoint coordinates) throws Exception {
      return new Bitmask(image, nonCollisionColer, coordinates);
   }

   private void createBitmaskFromImage(Image image, Color nonCollisionColor)
         throws Exception {
      if(image.getWidth() < 1 || image.getHeight() < 1)
         throw new Exception("Invalid image");
      collisionPixel = new boolean[image.getWidth()][image.getHeight()];
      for(int i = 0; i < collisionPixel.length; i++) {
         for(int j = 0; j < collisionPixel[0].length; j++) {
            if(image.getColor(i, j).equals(nonCollisionColor)) {
               collisionPixel[i][j] = false;
            } else {
               collisionPixel[i][j] = true;
            }
         }
      }
   }

   public boolean[][] getCollisionPixel() {
      return collisionPixel;
   }

   public void setCollisionPixel(boolean[][] collisionPixel) {
      this.collisionPixel = collisionPixel;
   }

   public FloatPoint getCoordinates() {
      return coordinates;
   }

   public void setCoordinates(FloatPoint coordinates) {
      this.coordinates = coordinates;
   }

   public int getWidth() {
      return width;
   }

   public int getHeight() {
      return height;
   }

   public String toString() {
      return this.getClass().getName() + "[width=" + collisionPixel.length
            + ", height=" + collisionPixel[0].length + ", coordinates="
            + coordinates + "]";
   }
}

This class just takes an image and creates the Bitmask.
I created a class FloatPoint which is, as the names says, a Point that holds floats instead of ints. So you can use the point to draw the image and to check for the intersection.
So here's the FloatPoint, nothing too fancy:
Code:
public class FloatPoint implements Serializable {
   private static final long serialVersionUID = -2999813791147929926L;
   protected float x, y;

   public FloatPoint() {
      this(0, 0);
   }

   public FloatPoint(float x, float y) {
      this.x = x;
      this.y = y;
   }

   public void translate(float dx, float dy) {
      this.x += dx;
      this.y += dy;
   }

   public float getX() {
      return x;
   }

   public void setX(float x) {
      this.x = x;
   }

   public float getY() {
      return y;
   }

   public void setY(float y) {
      this.y = y;
   }

   public String toString() {
      return this.getClass() + "[x=" + x + ", y=" + y + "]";
   }
}


And finally the intersectionhandler. I made it static so you can use it everywhere you want without making an instance.
Code:
public class BitmaskIntersectionHandler {
   private static int overlayTopLeftCornerX, overlayTopLeftCornerY, widthOfOverlap, heightOfOverlap, b1CoordX, b1CoordY, b2CoordX, b2CoordY;
   private static int b1StartOfWidthIteration,   b1StartOfHeightIteration;
   private static int b2StartOfWidthIteration,   b2StartOfHeightIteration;
   private static Bitmask b1, b2;
   
   public static boolean checkForIntersectionWithBitmask(Entity e1, Entity e2) {
      b1 = e1.getBitmask();
      b2 = e2.getBitmask();
      init();
      getOverlappingLeftTopCornerAndCalcOffset();
      widthOfOverlap = calcIntersectionWidth();
      heightOfOverlap = calcIntersectionHeight();
      return checkOverlappingAreaForIntersection();
   }

   private static void init() {
      b1CoordX = (int)b1.getCoordinates().getX();
      b1CoordY = (int)b1.getCoordinates().getY();
      b2CoordX = (int)b2.getCoordinates().getX();
      b2CoordY = (int)b2.getCoordinates().getY();
      b1StartOfWidthIteration = 0;
      b1StartOfHeightIteration = 0;
      b2StartOfWidthIteration = 0;
      b2StartOfHeightIteration = 0;
   }

   private static void getOverlappingLeftTopCornerAndCalcOffset() {
      if(b1CoordX > b2CoordX) {
         overlayTopLeftCornerX = b1CoordX;
         b2StartOfWidthIteration = Math.abs(b2CoordX - b1CoordX);
      } else {
         overlayTopLeftCornerX = b2CoordX;
         b1StartOfWidthIteration = Math.abs(b2CoordX - b1CoordX);
      }

      if(b1CoordY > b2CoordY) {
         overlayTopLeftCornerY = b1CoordY;
         b2StartOfHeightIteration = Math.abs(b2CoordY - b1CoordY);
      } else {
         overlayTopLeftCornerY = b2CoordY;
         b1StartOfHeightIteration = Math.abs(b2CoordY - b1CoordY);
      }
   }

   private static int calcIntersectionWidth() {
      int width1 = b1CoordX + b1.getWidth();
      int width2 = b2CoordX + b2.getWidth();
      return width1 < width2 ? width1 : width2;
   }

   private static int calcIntersectionHeight() {
      int height1 = b1CoordY + b1.getHeight();
      int height2 = b2CoordY + b2.getHeight();
      return height1 < height2 ? height1 : height2;
   }

   private static boolean checkOverlappingAreaForIntersection() {
      for(int i = overlayTopLeftCornerX, i1 = b1StartOfWidthIteration, i2 = b2StartOfWidthIteration; i < widthOfOverlap; i++, i1++, i2++) {
         for(int j = overlayTopLeftCornerY, j1 = b1StartOfHeightIteration, j2 = b2StartOfHeightIteration; j < heightOfOverlap; j++, j1++, j2++) {
            if(b1.getCollisionPixel()[i1][j1]
                  && b2.getCollisionPixel()[i2][j2]) {
               return true;
            }
         }
      }
      return false;
   }
}

Notice the Entity class I used in checkForIntersectionWithBitmask.
Entity should be the superclass of all your objects that you want to check the intersection on.
Should look something like this:
Code:
public class Entity {
FloatPoint coordinates;
Bitmask bitmask;
Image image;

public Entity() {
coordinates = new FloatPoint();
image = //whatever
bitmask = Bitmask.createBitmaskWithTopLeftPixelAsNonCollisionColor(image, coordinates);
}
}

The FloatPoint is the connection between the Entity and its Bitmask, so when you translate the point of the entity, the bitmask moves right along.

Feel free to use/alter the code i posted.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 04, 2011 2:51 pm 
Offline
Game Developer

Joined: Sun Nov 12, 2006 11:18 pm
Posts: 890
Location: Germany
Nice stuff :D

@shiroto: you can use the already existing Vector2f class from Slick instead of your own FloatPoint class. Just to avoid repeating code :wink:
And your constructor parameter nonCollisionColer was made in haste, right?
Code:
   public Bitmask(Image image, Color nonCollisionColer, FloatPoint coordinates)

Sorry shiroto, couldn't resist :wink:

Great to have both of you (R.D. and shiroto) new in the forums and thanks for your input already :D

_________________
Right Angle Games | Marte Engine
Back to the past | Star Cleaner | SpiderTrap


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 04, 2011 3:10 pm 
Offline

Joined: Fri Mar 04, 2011 12:34 pm
Posts: 24
Tommy wrote:
Nice stuff :D

@shiroto: you can use the already existing Vector2f class from Slick instead of your own FloatPoint class. Just to avoid repeating code :wink:
And your constructor parameter nonCollisionColer was made in haste, right?
Code:
   public Bitmask(Image image, Color nonCollisionColer, FloatPoint coordinates)

Sorry shiroto, couldn't resist :wink:

Great to have both of you (R.D. and shiroto) new in the forums and thanks for your input already :D

Oh so there is a class for it. And thanks for pointing out my spelling fail ^^" I'll go change that right away.


Top
 Profile  
 
 Post subject:
PostPosted: Sat Mar 05, 2011 6:45 pm 
Offline
Game Developer
User avatar

Joined: Thu Mar 03, 2011 6:22 pm
Posts: 534
Everyone has is own opinion... I don't want do argue about that...
If you want to use the Object-Orientated way than that's okay :)

(btw what is the problem with the comments?)

@Tommy
I will only stay if you enable stencil bits :( NOW you have to do it, muha!
(You from Germany? 8D good for me!)

Oh and I will change the name of the method right away!

Edit:
btw shiroto, you should use Math.round(); instead of casting. For OpenGl a x.5 value is already the next pixel. This could sometimes lead to wrong results.

I have changed the jar now :A

_________________
Current Projects:
Image Mr. Hat I
Image Vegan Vs. Zombies
Projects:
RadicalFish Engine - Build on top of Slick2D, Ideas, Bugs? Open an Issue ticket!


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 07, 2011 7:30 pm 
Offline
Regular

Joined: Sun Dec 07, 2008 5:22 am
Posts: 238
Location: Vancouver, BC, Canada
This looks really interesting, but I got to be honest, I don't really care how it's implemented, I care how I would use it. Can you post some example code of how this collision lib might be used?

_________________
If at first quads don't succeed tri tri again.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 07, 2011 9:11 pm 
Offline
Game Developer
User avatar

Joined: Thu Mar 03, 2011 6:22 pm
Posts: 534
Well the BitMask has it doc for this:

Quote:
This class serves as an image mask for an image. This is useful for pixel precise collision (without rotation, but I'm working on that). Note that this class needs the Slick2D library to work properly. The class itself is very small to ensure fast usage. BitMask is very easy to use. Just generate a BitMask from an image and when it comes to check if it intersects with another, set the Rectangle of the image to the BitMask and call intersects(BitMask bitmask); (of course you should set the rectangle for the other rectangle as well) :

Code:
   Image image1 = new Image("res/image1.png");
   Image image2 = new Image("res/image2.png");

   BitMask bitmask1 = new BitMask(image1);
   BitMask bitmask2 = new BitMask(image2);

   ...

   public void update(GameContainer container, StateBasedGame game, int delta) throws SlickException {
      //set the bitmasks rectangle to the current position of the image
      bitmask1.setRect(image1X, image1Y, image1.getWidth(), image1.getHeight());
      bitmask2.setRect(image2X, image2Y, image2.getWidth(), image2.getHeight());
         
      if(bitmask1.intersects(bitmask2)) {
         System.out.println("HIT");
      }
   }


Oh, and you can set the bits to another image, so you only need 2 BitMasks for collisions between 2 Images. IMPORTANT! When using alpha transparency, use the constructor BitMask(Image image, int x, int y) to determine the color to take (or just BitMask(Image image) for the upper-left corner). This must be done because otherwise, the BitMask will probably consist of collision bits only.


And the CollisionTest too (+ enable stencil bit in the opening post):

Quote:
Tests 2 images on collision. Using this class is very simple. First of all you have to enable stencil bits. Look here how to do it: http://slick.javaunlimited.net/viewtopic.php?t=3084

Then in your update method, initialize the test with
Code:
initCollisionTest(int screenWidth, int screenHeight);.

Then use the
Code:
intersects(Image image1, float x1, float y1, Image image2, float x2, float y2);

method to check if the given images collide.
After this you call
Code:
exitCollisionTest().

That's it.

_________________
Current Projects:
Image Mr. Hat I
Image Vegan Vs. Zombies
Projects:
RadicalFish Engine - Build on top of Slick2D, Ideas, Bugs? Open an Issue ticket!


Top
 Profile  
 
 Post subject:
PostPosted: Tue Mar 08, 2011 9:50 pm 
Offline
Slick Zombie

Joined: Wed Apr 02, 2008 1:32 pm
Posts: 1313
Location: Italy
R.D., if you agree, we can try to use your Bitmask into MarteEngine?

_________________
Blog | Last game Gravity Duck tribute | In progress Gravity Duck tribute


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 09, 2011 12:38 pm 
Offline
Game Developer
User avatar

Joined: Thu Mar 03, 2011 6:22 pm
Posts: 534
Sure why not o.o (as long as my author tag remains :D)

_________________
Current Projects:
Image Mr. Hat I
Image Vegan Vs. Zombies
Projects:
RadicalFish Engine - Build on top of Slick2D, Ideas, Bugs? Open an Issue ticket!


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 14, 2011 3:47 am 
Offline
Regular

Joined: Sun Dec 07, 2008 5:22 am
Posts: 238
Location: Vancouver, BC, Canada
That looks pretty good. I may be using this in one of my projects especially if you get rotations in there.
Two thoughts came to mind when a took a closer look at this.

1. I could see it as being really handy to create a class that extends Image and implements the BitMask interface. Maybe called BitMaskedImage.

2. Have you considered using quad trees instead of large arrays to do the pixel collision detection? My gut instinct is that this would only speed up the worst case scenario and leave average performance relatively unchanged, but I thought I'd bring it up.

_________________
If at first quads don't succeed tri tri again.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 14, 2011 10:19 am 
Offline
Game Developer
User avatar

Joined: Thu Mar 03, 2011 6:22 pm
Posts: 534
Yay, would be awesome if someone would use it!

1. Mh, can you point out some advantages? I wanted to keep the class smal and not bound to the image class (only as local value) since you don't need it for collision^^ The BitMask class may needs some improvement anyway because I don't like the way you need to use it now.
Now you have to set the rectangle of the image on screen... but i would be better to have something like this:

2. As you say it would only help in the worst case scenario (But even then the performance would only increase some nanoseconds). Also large arrays are not the problem. Java iterates very fast through them :D (next to the fact that the largest array would be screensize [800x600 = ca. 300 KB])

_________________
Current Projects:
Image Mr. Hat I
Image Vegan Vs. Zombies
Projects:
RadicalFish Engine - Build on top of Slick2D, Ideas, Bugs? Open an Issue ticket!


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 14, 2011 7:39 pm 
Offline
Regular

Joined: Sun Dec 07, 2008 5:22 am
Posts: 238
Location: Vancouver, BC, Canada
well I have say 50 sprites that use all the same images and therefore would use all the same bitmasks. Obviously I only have one copy and all the sprite instances share it but it would be nice if my sprites could have just
Code:
private BitMaskImage bmi;

instead of
Code:
private BitMask bm;
private Image img;
I'm not suggesting you change the class just add a new class that is essentially a wrapper for BitMask and Image. One advantage would be that if/when you do incorporate rotation. You can then share the rotation value used by image so that users don't have to manually keep the image and bitmask rotation values synced.

But my basic reasoning is that in 99% of use cases there will be 1 BitMask object for every 1 unique Image, so it makes sense to be able to have one object to control both.

If I was to really go to town I would suggest, a BitMask interface and DefaultBitMask and ImageBitMask implementations so that I can do this:
Code:
public void <T extends Image & Bitmask>addImage(T bitImage){
  //...
}

_________________
If at first quads don't succeed tri tri again.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Mar 15, 2011 6:24 am 
Offline
Game Developer
User avatar

Joined: Thu Mar 03, 2011 6:22 pm
Posts: 534
No bad! Maybe I will do that... But not now ( 4. Term just started and I have do make my Camera Object).

Btw you can't sue roations on the BitMask yet. CollisionTest can do that since it uses the GPU for detection :)

Edit:
Changed the topic name.

_________________
Current Projects:
Image Mr. Hat I
Image Vegan Vs. Zombies
Projects:
RadicalFish Engine - Build on top of Slick2D, Ideas, Bugs? Open an Issue ticket!


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jun 24, 2011 1:51 pm 
Offline

Joined: Wed Apr 27, 2011 3:21 am
Posts: 7
Looks really neat, R.D. - the only qualm I have with it is that you can't use basic "org.newdawn.slick.geom.Shape"s with this. Then I realized you can - simply create a Shape, use an ImageBuffer as a canvas to draw the shape upon, then get the Image from the ImageBuffer and use it as the bitmask. So I made a method that creates that image for you:
Code:
@param shape The shape to create an image with.
@param width The width of the entire image. Doesn't have to be the shape's width.
@param height The height of the entire image. Doesn't have to be the shape's height.
@return Returns an Image with the passed Shape imprinted.
public Image createBitMask( Shape shape, int width, int height )
{
   ImageBuffer buff = new ImageBuffer( width, height );
   for ( int y = 0; y < height; y++ )
   {
      for ( int x = 0; x < width; x++ )
      {
         buff.setRGBA( x, y, 0, 0, 0, shape.contains( x + 1, y + 1 ) ? 255 : 0 );
      }
   }
   return buff.getImage( Image.FILTER_NEAREST );
}

Note: You may want to change the returned image's filter before using it, because I defaulted it to FILTER_NEAREST.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 15, 2011 3:20 pm 
Offline

Joined: Tue Dec 28, 2010 11:46 am
Posts: 8
Hi all !
I'm very interesting with your class, but the files are unavailable.

Is Somebody can upload the files again ?

Thanks to all.

Greetings

Gouwi

_________________
Roger ?


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 21 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 0 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