Skip to content

Instantly share code, notes, and snippets.

@mnstrspeed
Last active December 23, 2015 02:28
Show Gist options
  • Save mnstrspeed/6566662 to your computer and use it in GitHub Desktop.
Save mnstrspeed/6566662 to your computer and use it in GitHub Desktop.
Collisions based on lines
package nl.tomsanders.game.egine.util;
public class Line
{
private final double slope;
private final double intercept;
private final Vector pointA;
private final Vector pointB;
public Line(Vector a, Vector b)
{
Vector d = b.subtract(a);
this.slope = d.getY() / d.getX();
this.intercept = a.getY() - this.slope * a.getX();
this.pointA = a;
this.pointB = b;
}
public double getSlope()
{
return this.slope;
}
public double getIntercept()
{
return this.intercept;
}
public Vector getPointA()
{
return this.pointA;
}
public Vector getPointB()
{
return this.pointB;
}
public boolean isOnLine(Vector b)
{
if (this.isVertical())
{
return (b.getY() >= this.pointA.getY() && b.getY() <= this.pointB.getY() ||
b.getY() >= this.pointB.getY() && b.getY() <= this.pointA.getY());
}
else
{
return (b.getX() >= this.pointA.getX() && b.getX() <= this.pointB.getX()) ||
(b.getX() >= this.pointB.getX() && b.getX() <= this.pointA.getX());
}
}
public boolean isVertical()
{
return this.slope == Double.POSITIVE_INFINITY ||
this.slope == Double.NEGATIVE_INFINITY;
}
public double intersect(Line b)
{
if (this.isVertical())
{
return this.pointA.getX();
}
else
{
return (b.getIntercept() - this.getIntercept()) /
(this.getSlope() - b.getSlope());
}
}
public Vector project(double x)
{
return new Vector(x, this.slope * x + this.intercept);
}
}
import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.List;
import nl.tomsanders.game.egine.Game;
import nl.tomsanders.game.egine.GameTime;
import nl.tomsanders.game.egine.util.Line;
import nl.tomsanders.game.egine.util.Rectangle;
import nl.tomsanders.game.egine.util.Size;
import nl.tomsanders.game.egine.util.Vector;
public class PhysicsGame extends Game
{
private Rectangle obstacle;
private Rectangle x;
private Rectangle dx;
private Vector speed;
public PhysicsGame()
{
/*
// Horizontal test from top
this.obstacle = new Rectangle(
new Vector(400, 400),
new Size(400, 50));
this.x = new Rectangle(
new Vector(500, 250),
new Size(50, 50));
this.speed = new Vector(200, 250);
*/
// Vertical test from right
this.obstacle = new Rectangle(
new Vector(600, 200),
new Size(50, 300));
this.x = new Rectangle(
new Vector(800, 300),
new Size(50, 50));
this.speed = new Vector(-400, 100);
// Calculate next frame position
this.dx = new Rectangle(
this.x.getPosition().translate(this.speed),
this.x.getSize());
}
private boolean showIntersectionPoints = false;
private boolean drawTrajectoryLines = true;
private boolean drawObstacleLines = false;
@Override
public void render(Graphics g)
{
super.render(g);
g.setColor(Color.BLACK);
this.drawBox(g, this.obstacle);
this.drawBox(g, this.x);
this.drawBox(g, this.dx);
if (drawTrajectoryLines)
{
g.setColor(Color.LIGHT_GRAY);
for (Line line : this.x.getConnectingLines(this.dx))
{
this.drawLine(g, line);
}
}
if (drawObstacleLines)
{
g.setColor(Color.BLUE);
for (Line line : this.obstacle.getLines())
{
this.drawLine(g, line);
}
}
this.drawBoxAfterCollision(g);
}
private void drawBoxAfterCollision(Graphics g)
{
// Basically, the "first" (or "closest") intersection
// will determine how far the object can travel
Vector closestDelta = this.speed;
for (Line line : this.obstacle.getLines()) // Lines bounding the rectangle of the obstacle
{
for (Line trajectory : this.x.getConnectingLines(this.dx)) // Lines connecting the corners of rectangles in current and next frame
{
for (Vector intersection : this.determineIntersections(line, trajectory)) // 0 or 1 intersections
{
Vector delta = intersection.subtract(trajectory.getPointA());
if (delta.getLengthSquared() < closestDelta.getLengthSquared())
{
closestDelta = delta;
}
// For debugging purposes
if (showIntersectionPoints)
{
g.setColor(Color.GREEN);
g.fillOval((int)intersection.getX() - 5, (int)intersection.getY() - 5, 10, 10);
}
}
}
}
// Draw box
g.setColor(Color.RED);
Rectangle box = new Rectangle(
this.x.getPosition().translate(closestDelta),
this.x.getSize());
this.drawBox(g, box);
}
private List<Vector> determineIntersections(Line a, Line b)
{
ArrayList<Vector> result = new ArrayList<Vector>();
if (a.isVertical())
{
if (b.isVertical())
{
// Perpendicular lines, so no intersections
return result;
}
// Swap a and b
return this.determineIntersections(b, a);
}
Vector intersection = a.project(b.intersect(a));
if (a.isOnLine(intersection) && b.isOnLine(intersection))
{
result.add(intersection);
}
return result;
}
private void drawBox(Graphics g, Rectangle box)
{
g.drawRect((int)box.getTopLeftCorner().getX(), (int)box.getTopLeftCorner().getY(),
(int)box.getSize().getWidth(), (int)box.getSize().getHeight());
}
public static void main(String[] args)
{
new PhysicsGame().start();
}
}
package nl.tomsanders.game.egine.util;
import java.util.ArrayList;
import java.util.List;
public class Rectangle {
private final Vector position;
private final Size size;
public Rectangle(Vector position, Size size) {
this.position = position;
this.size = size;
}
public Size getSize() {
return this.size;
}
public Vector getPosition() {
return this.position;
}
public Vector getTopLeftCorner() {
return this.position;
}
public Vector getTopRightCorner() {
return new Vector(
this.position.x + this.size.getWidth(),
this.position.y);
}
public Vector getBottomLeftCorner() {
return new Vector(
this.position.x,
this.position.y + this.size.getHeight());
}
public Vector getBottomRightCorner() {
return new Vector(
this.position.x + this.size.getWidth(),
this.position.y + this.size.getHeight());
}
public double getTop() {
return this.position.getY();
}
public double getBottom() {
return this.position.getY() + this.size.getHeight();
}
public double getLeft() {
return this.position.getX();
}
public double getRight() {
return this.position.getX() + this.size.getWidth();
}
public List<Line> getLines() {
ArrayList<Line> lines = new ArrayList<Line>();
lines.add(new Line(this.getTopLeftCorner(), this.getTopRightCorner()));
lines.add(new Line(this.getTopRightCorner(), this.getBottomRightCorner()));
lines.add(new Line(this.getBottomLeftCorner(), this.getBottomRightCorner()));
lines.add(new Line(this.getTopLeftCorner(), this.getBottomLeftCorner()));
return lines;
}
public List<Line> getConnectingLines(Rectangle b)
{
ArrayList<Line> lines = new ArrayList<Line>();
lines.add(new Line(this.getTopLeftCorner(), b.getTopLeftCorner()));
lines.add(new Line(this.getTopRightCorner(), b.getTopRightCorner()));
lines.add(new Line(this.getBottomLeftCorner(), b.getBottomLeftCorner()));
lines.add(new Line(this.getBottomRightCorner(), b.getBottomRightCorner()));
return lines;
}
}
package nl.tomsanders.game.egine.util;
public class Vector
{
public final double x;
public final double y;
public Vector(double x, double y)
{
this.x = x;
this.y = y;
}
public Vector(Vector v)
{
this.x = v.getX();
this.y = v.getY();
}
public double getX()
{
return this.x;
}
public double getY()
{
return this.y;
}
public double getLength()
{
return Math.sqrt(this.getLengthSquared());
}
public double getLengthSquared()
{
return this.x * this.x + this.y * this.y;
}
public Vector subtract(Vector b)
{
return new Vector(
this.getX() - b.getX(),
this.getY() - b.getY());
}
// ...
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment