Reputation: 1403
i'm making a 2D tile based game and i was working with AABB collision algorithm when i got stucked in this problem, i have a matrix of tile:
Tile[][] matrix = new Tile[WIDTH][HEIGHT];
interface Tile {
public boolean isSolid();
}
based on this matrix i want calculate the AABB pool that is a simple list, defined here:
List<AABB> aabbPool = new ArrayList<AABB>();
class AABB {
private int x;
private int y;
private int w;
private int h;
// Getter and Setter //
}
i'm looking for an algorithm that is capable of iterate the tile matrix and find the largest possible AABB-Rectangle of solid attached tiles in the matrix, let me explain:
Grid = The matrix
White = Unsolid tile
Black = Solid Tile
Given this matrix the algorithm will create the aabb pool, like this
Red outline = Y preferred aabb
Green outline = X preferred aabb (Y is not possible)
Blue outline = XY group
At the end, i created this script for debug the algorithm
public class AABBDebugger {
// Public field just for debug
static class AABB {
public int xPosition;
public int yPosition;
public int width;
public int height;
}
// Public field just for debug
static class Tile {
public static final int SIZE = 10;
public boolean solid;
}
public static void main(String[] args) {
// Matrix size
int WIDTH = 50;
int HEIGHT = 50;
// Declaration of matrix and random initialization
Tile[][] matrix = new Tile[WIDTH][HEIGHT];
for (int xCoord = 0; xCoord < WIDTH; xCoord++) {
for (int yCoord = 0; yCoord < HEIGHT; yCoord++) {
matrix[xCoord][yCoord] = new Tile();
matrix[xCoord][yCoord].solid = Math.random() > 0.5;
}
}
// Inizialization of the collission pool
List<AABB> aabbPool = new ArrayList<AABB>();
// Magic method that create the collision pool
magicMethod(matrix, WIDTH, HEIGHT, aabbPool);
// Rendering of result
Canvas canvas = new Canvas();
canvas.setPreferredSize(new Dimension(WIDTH * Tile.SIZE, HEIGHT * Tile.SIZE));
JFrame frame = new JFrame();
frame.add(canvas);
frame.pack();
frame.setVisible(true);
while (!Thread.interrupted()) {
BufferStrategy bufferStrategy;
while ((bufferStrategy = canvas.getBufferStrategy()) == null) {
canvas.createBufferStrategy(2);
}
Graphics graphics = bufferStrategy.getDrawGraphics();
for (int xCoord = 0; xCoord < WIDTH; xCoord++) {
for (int yCoord = 0; yCoord < HEIGHT; yCoord++) {
graphics.setColor(matrix[xCoord][yCoord].solid ? Color.BLACK : Color.WHITE);
graphics.fillRect(xCoord * Tile.SIZE, yCoord * Tile.SIZE, Tile.SIZE, Tile.SIZE);
}
}
for (AABB aabb : aabbPool) {
graphics.setColor(Color.RED);
graphics.drawRect(aabb.xPosition, aabb.yPosition, aabb.width, aabb.height);
}
bufferStrategy.show();
graphics.dispose();
}
System.exit(0);
}
/*
* The algorithm that i'm looking for
* for cycle start from Y rather then X
*/
private static void magicMethod(Tile[][] matrix, int WIDTH, int HEIGHT, List<AABB> aabbPool) {
for (int yCoord = 0; yCoord < HEIGHT; yCoord++) {
AABB aabb = null;
for (int xCoord = 0; xCoord < WIDTH; xCoord++) {
if (matrix[xCoord][yCoord].solid) {
if (aabb == null) {
aabb = new AABB();
aabb.yPosition = yCoord * Tile.SIZE;
aabb.xPosition = xCoord * Tile.SIZE;
aabb.height = Tile.SIZE;
aabb.width = Tile.SIZE;
} else {
aabb.width += Tile.SIZE;
}
} else if (aabb != null) {
aabbPool.add(aabb);
aabb = null;
}
}
if (aabb != null) {
aabbPool.add(aabb);
aabb = null;
}
}
}
}
The script produce this result:
but isn't what i expect because this algorithm group the tiles by y and is ok, but not by x when i can, like there
Finally (sorry for the long post) the algorithm must respect this rules:
Upvotes: 2
Views: 240
Reputation: 4111
You can still use your method with a slight change: Do not directly store single-tile AABB rectangles grouped by Y (those with width==Tile.SIZE
); but store them in a temp HashMap: Map<Integer, List<Integer>>
, where the key is the xCoord
of each tile and the value yCoord is added to the list). This way you can track which tiles per xCoord have not been grouped already by Y. So, check if width!=Tile.SIZE
before adding:
if (aabb.width != Tile.SIZE) {
aabbPool.add(aabb);
} else {
addToMap(xCoord,yCoord);
}
aabb = null;
The method addToMap can be implemented as follows:
private static void addToMap(Integer x, Integer y, Map<Integer, List<Integer>> xMap) {
if (xMap.containsKey(x)) {
xMap.get(x).add(y);
} else {
List<Integer> yList = new ArrayList<Integer>();
yList.add(y);
xMap.put(x, yList);
}
}
Then, call the following method as a last step of your magicMethod
; it will either add single-tile AABB rectangles (not possible to be grouped anyway), or grouped by X (that are not grouped by Y already).
private static void groupByX(Map<Integer, List<Integer>> xMap, List<AABB> aabbPool) {
//for each X
for (Entry<Integer, List<Integer>> entry : xMap.entrySet()) {
List<Integer> curList = entry.getValue();
if (curList != null) {
int curY = curList.get(0); //current yCoord
int curH = 1; //current height
//for each Y
for (int i = 1; i < curList.size(); i++) {
if (curY + curH == curList.get(i)) {
curH++;
} else {
aabbPool.add(new AABB(entry.getKey()*Tile.SIZE, curY*Tile.SIZE, Tile.SIZE, curH*Tile.SIZE)); // *Tile.SIZE()
//reset
curY = curList.get(i);
curH = 1;
}
}
//add the last AABB
aabbPool.add(new AABB(entry.getKey()*Tile.SIZE, curY*Tile.SIZE, Tile.SIZE, curH*Tile.SIZE));
}
}
}
This is what you get:
Upvotes: 1