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.