4

I am currently working on collision for my 2D game. I did some research and deducted that I should use the method of storing alpha value pixels into a "mask" of the image of the entity, and the same for the other. Then, I take both entitys' x & y co-ords, as well as height and width, and make a Rectangle object and use the method Rectangle.intersects(Rectangle r) to check if they do infact collide inorder to make it more efficent instead of going through 2 for loops.

IF they intersect, I then make a new Array with the dimensions :

 int maxLengthY = Math.max(thisEntity.getMask().length, e.getMask().length);
 int maxLengthX = Math.max(thisEntity.getMask()[0].length, thisEntity.getMask()[0].length);

 int minX = Math.min(thisEntity.getX(), e.getX());
 int minY = Math.min(thisEntity.getY(), e.getY());

 int[][] map = new int[maxLengthX + minX][maxLengthY + minY];

and then add the other two masks onto this one with their corresponding y & x "boundaries", like so:

for(int curX = 0; curX < maxLengthX + minX; curX++) { //only loop through the co-ords that area affected                                                              
for(int curY = 0; curY < maxLengthY + minY; curY++) {                                                                                                             

    int this_x = thisEntity.getX();                                                                                                                               
    int this_width = thisEntity.getImage().getWidth();                                                                                                            
    int this_y = thisEntity.getY();                                                                                                                               
    int this_height = thisEntity.getImage().getHeight();                                                                                                          
    int[][] this_mask = thisEntity.getMask();                                                                                                                     
    if(curX < (this_x + this_width) && curX < this_x) {//check that the co-ords used are relevant for thisEntity's mask                                           

        if(curY < (this_y + this_height) && curY < this_y) {                                                                                                      
            map[curX][curY] = this_mask[Math.abs(curX - this_x)][Math.abs(curY - this_y)]; // store data from mask to map                                         
        }                                                                                                                                                         
    }                                                                                                                                                             

    int other_x = e.getX();                                                                                        
    int other_width = e.getImage().getWidth();                                                                     
    int other_y = e.getY();                                                                                        
    int other_height = e.getImage().getHeight();                                                                   
    int[][] other_mask = e.getMask();                                                                              

    if(curX < (other_x + other_width) && curX > other_x) { //check that the co-ords used are relevant for e's mask 
        if(curY < (other_y + other_height) && curY > other_y) {                                                    
            if(map[curX][curY] == 1) { //check if this segment is already written by thisEntity                    
                map[curX][curY] = 2;   //if yes, set to 2 instead of e's value to show collision                   
            } else {                                                                                               
                map[curX][curY] = other_mask[curX][curY]; // the minus to nullify minX and minY "requirements"     
            }                                                                                                      
        }                                                                                                          
    }                                                                                                              
}                                                                                                                  
}            

resulting in the Array "map" looking like SO:

img (excuse my 1337 paint skills)

This is the code in all it's beauty:

public Entity[] collisions(Entity thisEntity) {                                                                                                              
ArrayList<Entity> list = new ArrayList<Entity>();                                                                                                        
try {                                                                                                                                                    
    for (Entity e : getLevel().getEntities()) {                                                                                                          

        System.out.println("rect contains = "+thisEntity.getRect().contains(e.getRect()));                                                               

        if (!thisEntity.equals(e)) {                                                                                                                     
            Rectangle r = e.getRect();                                                                                                                   
            r = thisEntity.getRect();                                                                                                                    

            if (thisEntity.getRect().intersects(e.getRect())) {                                                                                          

                //get variables to create a space designated for the intersection areas involved                                                         
                int maxLengthY = Math.max(thisEntity.getMask().length, e.getMask().length);                                                              
                int maxLengthX = Math.max(thisEntity.getMask()[0].length, thisEntity.getMask()[0].length);                                               

                int minX = Math.min(thisEntity.getX(), e.getX());                                                                                        
                int minY = Math.min(thisEntity.getY(), e.getY());                                                                                        

                int[][] map = new int[maxLengthX + minX][maxLengthY + minY]; //create a matrix which merges both Entity's mask's to compare                                                                                                                                                                                                                                                                                                                                       

                for(int curX = 0; curX < maxLengthX + minX; curX++) { //only loop through the co-ords that area affected                                 
                    for(int curY = 0; curY < maxLengthY + minY; curY++) {                                                                                

                        int this_x = thisEntity.getX();                                                                                                  
                        int this_width = thisEntity.getImage().getWidth();                                                                               
                        int this_y = thisEntity.getY();                                                                                                  
                        int this_height = thisEntity.getImage().getHeight();                                                                             
                        int[][] this_mask = thisEntity.getMask();                                                                                        
                        if(curX < (this_x + this_width) && curX > this_x) {//check that the co-ords used are relevant for thisEntity's mask              

                            if(curY < (this_y + this_height) && curY > this_y) {                                                                         
                                map[curX][curY] = this_mask[Math.abs(curX - this_x)][Math.abs(curY - this_y)]; // store data from mask to map            
                            }                                                                                                                            
                        }                                                                                                                                

                        int other_x = e.getX();                                                                                                          
                        int other_width = e.getImage().getWidth();                                                                                       
                        int other_y = e.getY();                                                                                                          
                        int other_height = e.getImage().getHeight();                                                                                     
                        int[][] other_mask = e.getMask();                                                                                                

                        if(curX < (other_x + other_width) && curX > other_x) { //check that the co-ords used are relevant for e's mask                   
                            if(curY < (other_y + other_height) && curY > other_y) {                                                                      
                                if(map[curX][curY] == 1) { //check if this segment is already written by thisEntity                                      
                                    map[curX][curY] = 2;   //if yes, set to 2 instead of e's value to show collision                                     
                                } else {                                                                                                                 
                                    map[curX][curY] = other_mask[curX][curY]; // the minus to nullify minX and minY "requirements"                       
                                }                                                                                                                        
                            }                                                                                                                            
                        }                                                                                                                                
                    }                                                                                                                                    
                }                                                                                                                                                                                                                                                                             
            }                                                                                                                                            
        }                                                                                                                                                
    }                                                                                                                                                    
} catch (Exception excp) {                                                                                                                               
    excp.printStackTrace();                                                                                                                              
}                                                                                                                                                        
return list.toArray(new Entity[1]);                                                                                                                      
}                                                                                                                                                            

Also, here is the method getMask() :

    public int[][] getMask() {
        return mask;
    }

    ...

    private void createMask(BufferedImage image) {

    final int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
    final int width = image.getWidth();
    final int height = image.getHeight();
    final boolean hasAlphaChannel = image.getAlphaRaster() != null;

    int[][] result = new int[height][width];
    if (hasAlphaChannel) {
        for (int pixel = 0, row = 0, col = 0; pixel < pixels.length; pixel += 4) {
            int alpha = pixels[pixel];
            if(alpha != 0) {
                result[row][col] = 1;
            } else {
                result[row][col] = 0;
            }

            if (col == width) {
                col = 0;
                row++;
            }
        }
    }
    mask = result;

}

However... this code does not work as intended, and in some cases at all as when adding the individual masks to the map I get IndexOutOfBounds even though it should work so it's probably just me overlooking something...

So, to conclude, I need help with my code:

  • What is wrong with it?
  • How can I fix it?
  • Is there a more efficent way of doing this type of collision?
  • Do you recommend other types of collision? If so, what are they?

1 Answer 1

1

When you create Entities, do you create their masks from images of the exact same size? Because otherwise entity's mask and image map would be using different coordinate systems (entity.mask[0][0] might be is at its corner, when map[0][0] is at the corner of the "world"), and you're comparing same indices at the line:

map[curX][curY] = other_mask[curX][curY];

(above in the code you're actually getting those to the same coordinate system with Math.abs(a-b))

As for more efficient ways to detect collisions, you can look into binary space partitioning and more on collision detection in general on Wikipedia.

Sign up to request clarification or add additional context in comments.

1 Comment

I use this to create that world's map : ` int[][] map = new int[minX][minY]` so it takes both of the images x and y values and takes the lowest one to create a relevant rectangle for both entity's masks in relevance to their area in the level. However I agree with you that the code in all is flawed and you know what they say, if it's not worth repairing a leak in a boat, get a new one'. Probably just me being lazy :P. Thanks for the links, I'll just probably build a new boat :-).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.