I finally found a edge detection system I like...
Basically, the magic happens here...
// Detect if we collided with any one (collision is the rectangle, bounds is us)
if (collision.intersects(bounds)) {
// Determine the intersect of the collision...
insect = collision.intersection(bounds);
// Flags...
boolean vertical = false;
boolean horizontal = false;
boolean isLeft = false;
boolean isTop = false;
// Left side...
if (insect.x == collision.x) {
horizontal = true;
isLeft = true;
// Right side
} else if (insect.x + insect.width == collision.x + collision.width) {
horizontal = true;
}
// Top
if (insect.y == collision.y) {
vertical = true;
isTop = true;
// Bottom
} else if (insect.y + insect.height == collision.y + collision.height) {
vertical = true;
}
// Technically, we can really only collide with a single edge...more or less
if (horizontal && vertical) {
// Basically, we try and give precedence to the longer edge...
if (insect.width > insect.height) {
horizontal = false;
} else {
vertical = false;
}
}
// We collided with a horizontal side...
if (horizontal) {
dx *= -1;
// Move the ball to the approriate edge so we don't get caught...
if (isLeft) {
bounds.x = collision.x - bounds.width;
} else {
bounds.x = collision.x + collision.width;
}
// We collided with a vertical side...
} else if (vertical) {
dy *= -1;
// Move the ball to the approriate edge so we don't get caught...
if (isTop) {
bounds.y = collision.y - bounds.height;
} else {
bounds.y = collision.y + collision.height;
}
}
}
Now, I only have a single obstacle, but I doubt it would take much effort to get it working with a series of obstacles... ;)
public class TestBouncingBall {
private Rectangle insect;
public static void main(String[] args) {
new TestBouncingBall();
}
public TestBouncingBall() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new BallPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class BallPane extends JLayeredPane {
private Ball ball;
private Timer timer;
private Rectangle world;
public BallPane() {
// world = new Rectangle(random(400), random(400), random(100), random(100));
world = new Rectangle(100, 100, 200, 200);
ball = new Ball();
ball.setSize(ball.getPreferredSize());
ball.setLocation(10, 10);
add(ball);
timer = new Timer(16, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
ball.move(getBounds(), world);
invalidate();
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
}
protected int random(int max) {
return (int) Math.round(Math.random() * max);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.GRAY);
g2d.fill(world);
if (insect != null) {
g2d.setColor(Color.RED);
g2d.fill(insect);
}
g2d.dispose();
}
}
public class Ball extends JPanel {
public int maxSpeed = 10;
private BufferedImage beachBall;
private int dx = 10 - (int)Math.round(Math.random() * (maxSpeed * 2)) + 1;
private int dy = 10 - (int)Math.round(Math.random() * (maxSpeed * 2)) + 1;
private int spin = 20;
private int rotation = 0;
public Ball() {
try {
beachBall = ImageIO.read(getClass().getResource("/ball.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
setOpaque(false);
}
@Override
public Dimension getPreferredSize() {
Dimension size = beachBall == null ? new Dimension(48, 48) : new Dimension(beachBall.getWidth(), beachBall.getHeight());
size.width += 4;
size.height += 4;
return size;
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (beachBall != null) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
int x = (getWidth() - beachBall.getWidth()) / 2;
int y = (getHeight() - beachBall.getHeight()) / 2;
AffineTransform transform = g2d.getTransform();
AffineTransform at = new AffineTransform();
at.translate(getX(), getY());
at.rotate(Math.toRadians(rotation), getWidth() / 2, getHeight() / 2);
g2d.setTransform(at);
g2d.drawImage(beachBall, x, y, this);
g2d.setTransform(transform);
g2d.dispose();
}
}
public void move(Rectangle world, Rectangle collision) {
Rectangle bounds = getBounds();
bounds.x += dx;
bounds.y += dy;
if (bounds.x < 0) {
bounds.x = 0;
dx *= -1;
}
if (bounds.y < 0) {
bounds.y = 0;
dy *= -1;
}
if (bounds.x + bounds.width > world.width) {
bounds.x = world.width - bounds.width;
dx *= -1;
}
if (bounds.y + bounds.height > world.height) {
bounds.y = world.height - bounds.height;
dy *= -1;
}
if (collision.intersects(bounds)) {
insect = collision.intersection(bounds);
boolean vertical = false;
boolean horizontal = false;
boolean isLeft = false;
boolean isTop = false;
if (insect.x == collision.x) {
horizontal = true;
isLeft = true;
} else if (insect.x + insect.width == collision.x + collision.width) {
horizontal = true;
}
if (insect.y == collision.y) {
vertical = true;
isTop = true;
} else if (insect.y + insect.height == collision.y + collision.height) {
vertical = true;
}
if (horizontal && vertical) {
if (insect.width > insect.height) {
horizontal = false;
} else {
vertical = false;
}
}
System.out.println("v = " + vertical + "; h = " + horizontal);
if (horizontal) {
dx *= -1;
if (isLeft) {
bounds.x = collision.x - bounds.width;
} else {
bounds.x = collision.x + collision.width;
}
} else if (vertical) {
dy *= -1;
if (isTop) {
bounds.y = collision.y - bounds.height;
} else {
bounds.y = collision.y + collision.height;
}
}
}
rotation += spin;
setBounds(bounds);
repaint();
}
}
}
for (int i = 0; i < r.size(); i++)
? You could usefor (Rectangle rect : r)
instead, as you're not usingi
for any other reasons...Whytemp = new Rectangle(r.elementAt(i))
? Why not just get the reference inside theVetcor
? Are you changing it? Is there something wrong withRectangle#contains
orRectangle#intersects
? – Beheldintersects
orcontains
, I'd be testing these to see if they will at least produce a positive hit result. If they do hit. I'd be making sure that the ball is moved to a safe position first, then the direction is changed (ie if you hit the right side, make sure the ball is now resting on the right side and not within the rectangle) – BeheldRectangle#outcode
, it's not perfect, but it's not bad either – Beheld