Refactor CollisionChecker for cleaner boundary handling

Streamlined collision logic by introducing helper methods for boundary calculations and movement validation. Replaced commented-out code with efficient implementations, improving readability and maintainability. Added clear documentation for new methods to better convey their purpose and usage.
This commit is contained in:
Urban Modig
2025-09-03 00:18:27 +02:00
parent 328098bbe7
commit 4638484b97

View File

@ -2,105 +2,93 @@ package se.urmo.game.collision;
import lombok.extern.slf4j.Slf4j;
import se.urmo.game.util.MyPoint;
import se.urmo.game.main.GamePanel;
import se.urmo.game.util.Direction;
import se.urmo.game.map.GameMap;
import se.urmo.game.util.Direction;
import se.urmo.game.util.MyPoint;
import java.util.Collections;
import java.util.List;
@Slf4j
public class CollisionChecker {
private GameMap map;
private final GameMap map;
public CollisionChecker(GameMap map) {
this.map = map;
}
// public Point getValidDestination(Direction direction, Point position, int agent_width, int agent_height) {
// List<Point> boundaries = switch (direction) {
// case NONE -> Collections.emptyList();
// case RIGHT, LEFT -> List.of(
// new Point(position.x + (direction.dx * agent_width/2), position.y - agent_height/2),
// new Point(position.x + (direction.dx * agent_width/2), position.y + agent_height/2)
// );
// case UP, DOWN -> List.of(
// new Point(position.x - agent_width/2, position.y + (direction.dy * agent_height/2)),
// new Point(position.x + agent_width/2, position.y + (direction.dy * agent_height/2))
// );
// };
//
// List<Pair> bs = boundaries.stream().map(p -> new Pair(p.x, p.y, GameMap.screenToRow(p.y), GameMap.screenToCol(p.x))).toList();
// log.debug("{} boundaries for {} are {}", direction, position, bs);
//
// List<Point> normalized = boundaries.stream()
// .map(p -> normalizePosition(direction, p, agent_width, agent_height))
// .toList();
//
// if (map.isPassable(normalized)) {
// return normalizePosition(direction, position, agent_width, agent_height);
// }
// return null; // Blocked
// }
public MyPoint getValidDestination(Direction direction, MyPoint position, int agentWidth, int agentHeight) {
List<MyPoint> boundaries = getBoundariesForDirection(direction, position, agentWidth / 2, agentHeight / 2);
// /**
// * Applies specific rules to movement
// * This, for instance, makes sure the tunnel left/right works.
// *
// * @param dir
// * @param pos
// * @param agent_width
// * @param agent_height
// * @return
// */
// public Point normalizePosition(Direction dir, Point pos, int agent_width, int agent_height) {
// int x = pos.x;
// int y = pos.y;
// int width = GamePanel.SCREEN_WIDTH;
// int height = GamePanel.SCREEN_HEIGHT;
//
// // tunnel
// if (x < GameMap.OFFSET_X) x = width - agent_width/2 - GameMap.OFFSET_X; // right
// if (x >= (width - GameMap.OFFSET_X)) x = GameMap.OFFSET_X; // left
//
// return new Point(x, y);
// }
return canMoveInDirection(agentWidth, boundaries)
? normalizePosition(position, agentWidth)
: null; // Blocked
}
public MyPoint getValidDestination(Direction direction, MyPoint position, int agent_width, int agent_height) {
List<MyPoint> boundaries = switch (direction) {
/**
* Determines whether boundaries (corners) are in a passible i.e., not blocked, position
*
* @param agentWidth The width of the entity attempting to move.
* @param boundaries The boundary points representing the edges of the area occupied by the entity.
* @return {@code true} if movement in the specified direction is possible; {@code false} otherwise.
*/
private boolean canMoveInDirection(int agentWidth, List<MyPoint> boundaries) {
return boundaries.stream()
.map(boundary -> normalizePosition(boundary, agentWidth))
.allMatch(myPoint -> map.isPassable((int) myPoint.x, (int) myPoint.y));
}
/**
* Calculates the boundary points of a given position based on a specified direction
* and horizontal and vertical offsets. The resulting boundaries represent the edges
* of the area occupied by the entity moving in the specified direction.
*
* @param direction The direction of movement (e.g., UP, DOWN, LEFT, RIGHT, or NONE).
* @param position The current position represented as a {@code MyPoint}.
* @param horizontalOffset The horizontal offset to determine the boundary width.
* @param verticalOffset The vertical offset to determine the boundary height.
* @return A list of boundary points in the form of {@code MyPoint} objects. If the
* direction is {@code NONE}, an empty list is returned.
*/
private static List<MyPoint> getBoundariesForDirection(Direction direction, MyPoint position, int horizontalOffset, int verticalOffset) {
return switch (direction) {
case NONE -> Collections.emptyList();
case RIGHT, LEFT -> List.of(
new MyPoint(position.x + (direction.dx * agent_width/2), position.y - agent_height/2),
new MyPoint(position.x + (direction.dx * agent_width/2), position.y + agent_height/2)
new MyPoint(position.x + ((double) (direction.dx * horizontalOffset) / 2), position.y - (double) verticalOffset / 2),
new MyPoint(position.x + ((double) (direction.dx * horizontalOffset) / 2), position.y + (double) verticalOffset / 2)
);
case UP, DOWN -> List.of(
new MyPoint(position.x - agent_width/2, position.y + (direction.dy * agent_height/2)),
new MyPoint(position.x + agent_width/2, position.y + (direction.dy * agent_height/2))
new MyPoint(position.x - (double) horizontalOffset / 2, position.y + ((double) (direction.dy * verticalOffset) / 2)),
new MyPoint(position.x + (double) horizontalOffset / 2, position.y + ((double) (direction.dy * verticalOffset) / 2))
);
};
}
List<MyPoint> normalized = boundaries.stream()
.map(p -> normalizePosition(direction, p.x, p.y, agent_width, agent_height))
.toList();
boolean passable = normalized.stream().allMatch(myPoint -> map.isPassable((int) myPoint.x, (int) myPoint.y));
if (passable) {
return normalizePosition(direction, position.x, position.y, agent_width, agent_height);
/**
* Normalizes the position of an agent when it reaches screen boundaries,
* implementing tunnel-like behavior when crossing horizontal boundaries.
*
* @param position The current position to normalize
* @param agentWidth The width of the agent
* @return Normalized position as MyPoint
*/
private MyPoint normalizePosition(MyPoint position, int agentWidth) {
if (isLeftBoundary(position.x)) {
return new MyPoint(GamePanel.SCREEN_WIDTH - (double) agentWidth / 2 - GameMap.OFFSET_X, position.y);
}
return null; // Blocked
if (isRightBoundary(position.x)) {
return new MyPoint(GameMap.OFFSET_X, position.y);
}
return position;
}
private MyPoint normalizePosition(Direction direction, double x, double y, int agent_width, int agent_height) {
double x1 = x;
double y1 = y;
int width = GamePanel.SCREEN_WIDTH;
int height = GamePanel.SCREEN_HEIGHT;
// tunnel
if (x < GameMap.OFFSET_X) x1 = width - agent_width/2 - GameMap.OFFSET_X; // right
if (x>= (width - GameMap.OFFSET_X)) x1 = GameMap.OFFSET_X; // left
return new MyPoint(x1, y1);
private boolean isLeftBoundary(double x) {
return x < GameMap.OFFSET_X;
}
private boolean isRightBoundary(double x) {
return x >= (GamePanel.SCREEN_WIDTH - GameMap.OFFSET_X);
}
}