/** * Sends out a ray that starts at the given position and moves out in the given direction. * * @param position the starting position * @param direction the direction to proceed in * @param visitTile the action to perform on each visited tile */ private void castLightRay(Vector2 position, Vector2 direction, VisitTile visitTile) { RectangleF viewRect = engine.graphicsComponent.camera.viewRect; if (!((position.x > viewRect.right && direction.x > 0) || (position.x < viewRect.left && direction.x < 0) || (position.y < viewRect.bottom && direction.y > 0) || (position.y > viewRect.top && direction.y < 0))) { return; } castRay(position, direction, (tile) => (tile.opacity), visitTile); }
/** * Shoot an imaginary ray from a point in a direction, ending on some condition * * @param position The starting position * @param direction The direction of the ray * @param visitTile The operation (if any) to perform on each tile * @param endCond Condition when the ray should end * * @return Length of the ray */ private float castRay(Vector2 position, Vector2 direction, Predicate <Tile> endCondition, VisitTile visitTile = null) { //Note: taken from http://www.cse.yorku.ca/~amana/research/grid.pdf if (endCondition == null) { throw new Exception("Error: An ending condition for the ray must be specified."); } //Bounds checking Tile curTile = getTileAt(position); if (curTile == null || direction == Vector2.Zero) { return(0); } int X = curTile.xIndex; //X index of current tile int Y = curTile.yIndex; //Y index of current tile int xDir = (direction.x >= 0) ? 1 : -1; //Direction of scanning along X int yDir = (direction.y >= 0) ? 1 : -1; //Direction of scanning along Y //Handle hor. and ver. cases separately if (direction.x == 0) { while (!endCondition(curTile)) { if (visitTile != null) { visitTile(curTile); } Y += yDir; if (Y < 0 || Y >= height) { return(getDistToTile(position, xDir, yDir, X, Y)); } curTile = tileArray[X, Y]; } //Apply to the last tile if (visitTile != null) { visitTile(curTile); } return(getDistToTile(position, xDir, yDir, X, Y)); } else if (direction.y == 0) { while (!endCondition(curTile)) { if (visitTile != null) { visitTile(curTile); } X += xDir; if (X < 0 || X >= width) { return(getDistToTile(position, xDir, yDir, X, Y)); } curTile = tileArray[X, Y]; } //Apply to the last tile if (visitTile != null) { visitTile(curTile); } return(getDistToTile(position, xDir, yDir, X, Y)); } float tMaxX = (Tile.size * (X + (xDir + 1) / 2) - position.x) / direction.x; // t at vert tile boundary float tMaxY = (Tile.size * (Y + (yDir + 1) / 2) - position.y) / direction.y; // t at horiz tile boundary float tDeltaX = Math.Abs(Tile.size / direction.x); // T required to move Tile.size in X float tDeltaY = Math.Abs(Tile.size / direction.y); // T required to move Tile.size in Y while (!endCondition(curTile)) { if (visitTile != null) { visitTile(curTile); } //Find next tile in ray if (tMaxX < tMaxY) { tMaxX += tDeltaX; X += xDir; } else { tMaxY += tDeltaY; Y += yDir; } if (X < 0 || X >= width || Y < 0 || Y >= height) { return(getDistToTile(position, xDir, yDir, X, Y)); } curTile = tileArray[X, Y]; } //Apply to the last tile if (visitTile != null) { visitTile(curTile); } return(getDistToTile(position, xDir, yDir, X, Y)); }
/** * Sends out a cone of light that starts at position and moves out in direction. * * @param position Vertex of the cone * @param direction Direction of the axis of the cone * @param coneWidth Angle of the cone, in degrees * @param numRays Number of rays to shoot within the cone * @param mod The action to perform on each visited tile */ public void castLightCone(Vector2 position, Vector2 direction, float coneWidth, int numRays, VisitTile mod) { if (coneWidth < 0) { throw new Exception("World.castCone(): coneWidth < 0"); } if (numRays < 0) { throw new Exception("World.castCone(): numRays < 0"); } //Calculate cos and sine for angle of cone float c = (float)Math.Cos(coneWidth / 180 * Math.PI / 2); float s = (float)Math.Sin(coneWidth / 180 * Math.PI / 2); //Beginning vector for the cone Vector2 v = new Vector2(c * direction.x + s * direction.y, -s * direction.x + c * direction.y); //Recalculate c and s for angle between rays c = (float)Math.Cos(coneWidth / 180 * Math.PI / numRays); s = (float)Math.Sin(coneWidth / 180 * Math.PI / numRays); for (int i = 0; i < numRays; i++) { castLightRay(position, v, mod); v = new Vector2(c * v.x - s * v.y, s * v.x + c * v.y); } }