Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@TheBrenny
Last active December 15, 2018 18:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TheBrenny/98f8f27ad1bb53f4ee80179f43dbc64b to your computer and use it in GitHub Desktop.
Save TheBrenny/98f8f27ad1bb53f4ee80179f43dbc64b to your computer and use it in GitHub Desktop.
Raycasting my homebrew'd way using linear functions and their x and y intercepts! The map is (theoretically) infinite in length along both axis' leading into the negative and positive. (See also: https://www.desmos.com/calculator/axjo4hkzxg)
public class AngleSpeed extends Vector {
protected float xSpeed;
protected float ySpeed;
public AngleSpeed(AngleSpeed old) {
this(old.distance, old.xSpeed, old.ySpeed, old.angle.getAngle());
}
public AngleSpeed(float speed, float xSpeed, float ySpeed, float angle) {
super(angle, speed);
this.xSpeed = xSpeed;
this.ySpeed = ySpeed;
}
public AngleSpeed addAngleSpeed(AngleSpeed add) {
this.xSpeed += add.xSpeed;
this.ySpeed += add.ySpeed;
this.distance = (float) Math.sqrt(xSpeed * xSpeed + ySpeed * ySpeed);
this.angle.setAngle(Angle.getAngle(0, 0, this.xSpeed, this.ySpeed));
return this;
}
public float getSpeed() {
return this.distance;
}
public float getXSpeed() {
return xSpeed;
}
public float getYSpeed() {
return ySpeed;
}
public String toString() {
return StringUtil.insert("{0}[s={1}, xs={2}, ys={3}, a={4}]", getClass().getName(), this.distance, this.xSpeed, this.ySpeed, this.angle);
}
public static AngleSpeed getAngleSpeed(float angle, float speed) {
float xSpeed = 0.0F;
float ySpeed = 0.0F;
double sin = Math.sin(Math.toRadians(angle));
double cos = Math.cos(Math.toRadians(angle));
xSpeed = (float) sin * speed;
ySpeed = (float) -cos * speed;
return new AngleSpeed(speed, xSpeed, ySpeed, angle);
}
public static AngleSpeed getAngleSpeed(Vector v) {
return getAngleSpeed(v.angle.getAngle(), v.distance);
}
}
public class Vector {
public Angle angle;
public float distance;
public Vector(float angle, float distance) {
this(new Angle(angle), distance);
}
public Vector(Angle angle, float distance) {
this.angle = angle;
this.distance = distance;
}
public Angle getAngle() {
return this.angle;
}
public float getDistance() {
return this.distance;
}
public String toString() {
return getClass().getName() + StringUtil.insert("[angle={0},dist={1}]", angle, distance);
}
}
public class Ray extends Vector {
public Point2D location;
public Ray(Point2D location, Vector v) {
this(location, v.angle.getAngle(), v.distance);
}
public Ray(Point2D location, float angle) {
this(location, angle, Float.MAX_VALUE / 2);
}
public Ray(Point2D location, float angle, float distance) {
super(angle, distance);
this.location = location;
}
public Ray(Ray ray) {
this(new Point2D.Float((float) ray.location.getX(), (float) ray.location.getY()), ray.angle.getAngle(), ray.distance);
}
public Point2D.Float getLocation() {
if(!(this.location instanceof Point2D.Float)) this.location = new Point2D.Float((float) this.location.getX(), (float) this.location.getY());
return (Point2D.Float) this.location;
}
public Point2D.Float getRelativeEndLoaction() {
AngleSpeed as = getAngleSpeed();
return new Point2D.Float(as.getXSpeed(), as.getYSpeed());
}
public Point2D.Float getEndLocation() {
Point2D.Float rel = getRelativeEndLoaction();
return new Point2D.Float(this.getLocation().x + rel.x, this.getLocation().y + rel.y);
}
public AngleSpeed getAngleSpeed() {
return AngleSpeed.getAngleSpeed(this);
}
public String toString() {
return getClass().getName() + StringUtil.insert("[loc={0},angle={1},dist={2}]", location, angle, distance);
}
}
public Ray castRay(Ray ray, int skip) {
if(ray.distance == 0) return ray;
ray = new Ray(ray); // duplicate so we don't duck the original
// Start prepping vars
// Origin stuff
final Point2D.Float origin = new Point2D.Float(ray.getLocation().x, ray.getLocation().y);
//final Point originTile = roundTileCoords(origin.x, origin.y);
final Point tlTile = topLeftTile();
final Point brTile = bottomRightTile();
final float angle = new Angle(ray.getAngle().getAngle()).getAngle();
// Moving stuff
Point tile = roundTileCoords(origin.x, origin.y);
Point grid = new Point(tile);
Point2D.Float mvp = new Point2D.Float(origin.x, origin.y);
int dirX, dirY;
int tileShiftX = 0, tileShiftY = 0;
float dist;
// Function stuff
float gradient;
MathUtil.LinearFunction f;
MathUtil.LinearFunction g;
// Loop stuff
int giveUpLoop = 100; // this is the max chickens cycles for the while loop
int giveUpCounter = 0;
boolean hit = false;
boolean exhausted = false;
// Get gradient and produce functions
AngleSpeed asTmp = AngleSpeed.getAngleSpeed(ray.angle.getAngle(), 1);
gradient = (asTmp.getYSpeed() / asTmp.getXSpeed());
f = (x, m) -> m * (x - origin.x) + origin.y;
g = (y, m) -> (1 / m) * (y - origin.y) + origin.x;
if(asTmp.getXSpeed() == 0) {
g = (y, m) -> origin.x; // ((1/0)=0) * (y - origin.y) + origin.x;
f = (x, m) -> Float.POSITIVE_INFINITY;
}
// determine direction to traverse
dirX = angle > 0 && angle < 180 ? 1 : angle > 180 ? -1 : 0;
dirY = angle > 90 && angle < 270 ? 1 : angle < 90 || angle > 270 ? -1 : 0;
tileShiftX = dirX > 0 ? dirX : 0;
tileShiftY = dirY > 0 ? dirY : 0;
for(int i = 0; i <= skip; i++) {
hit = false;
exhausted = false;
giveUpCounter = 0;
do {
// move tile
grid.x += tileShiftX;
grid.y += tileShiftY;
// OLD: grid = roundTileCoords(mvp.x + tileShiftX, mvp.y + tileShiftY);
// We want to maintain the grid.x and grid.y because it should keep moving, instead of resetting it.
tileShiftX = tileShiftY = 0;
mvp.x = g.eq(grid.y, gradient);
mvp.y = f.eq(grid.x, gradient);
// find the closest of the intercepts
if(MathUtil.distanceSqrd(mvp.x, grid.y, origin.x, origin.y) < MathUtil.distanceSqrd(grid.x, mvp.y, origin.x, origin.y)) mvp.y = grid.y;
else mvp.x = grid.x;
// get tile coord
tile = roundTileCoords(mvp.x, mvp.y);
// check to see if we are exhausted
if((dist = MathUtil.distance(mvp.x, mvp.y, origin.x, origin.y)) >= ray.distance) exhausted = true;
else if(giveUpCounter >= giveUpLoop) exhausted = true;
else if(!(tile.x >= tlTile.x && tile.x <= brTile.x && tile.y >= tlTile.y && tile.y <= brTile.y)) exhausted = true;
if(exhausted) break;
// determine if hit is solid
if(mvp.x % 1 == 0) tileShiftX = dirX;
if(mvp.y % 1 == 0) tileShiftY = dirY;
//if(getTileRelative((int) Math.floor(mvp.x), (int) Math.floor(mvp.y)).isSolid() && !exhausted) {
if(getTileRelative(tile.x + (dirX < 0 ? tileShiftX : 0), tile.y + (dirY < 0 ? tileShiftY : 0)).isSolid() && !exhausted) {
hit = true;
ray.distance = dist;
}
giveUpCounter++;
} while(!hit && !exhausted);
}
return ray;
}
/**
* Rounds a tile coordinate to return the tile that contains this tile
* coordinate, so as to round towards negative infinity. This is done
* by {@code floor}ing the coordinates.
*
* @param x
* The x coordinate
* @param y
* The y coordinate
* @return The tile coordinate which "owns" the given coordinate.
*/
public static Point roundTileCoords(float x, float y) {
x = (float) Math.floor(x);
y = (float) Math.floor(y);
return new Point((int) x, (int) y);
}
/**
* An interface to make it easy to create a linear function Lambda
* expression.
*/
public static interface MathUtil.LinearFunction {
float eq(float n, float m);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment