public IEnumerable<Tile<TileInfo>> CastRay(Ray ray) { // check http://playtechs.blogspot.nl/2007/03/raytracing-on-grid.html // for possible optimisations var start = ray.Start.NumericValue; var diff = ray.Direction.NumericValue; float startX, startY; this.positionToTileSpace(start.X, start.Y, out startX, out startY); int x1, y1; this.positionToTile(start.X + diff.X, start.Y + diff.Y, out x1, out y1); var x0 = (int)startX; var y0 = (int)startY; var tileDistanceX = x1 - x0; var tileDistanceY = y1 - y0; var count = 1 + Math.Abs(tileDistanceX) + Math.Abs(tileDistanceY); yield return new Tile<TileInfo>(this.tilemap, x0, y0); if (count <= 2) { if (count == 2) { yield return new Tile<TileInfo>(this.tilemap, x1, y1); } yield break; } var tileX = x0; var tileY = y0; var tileStepX = Math.Sign(tileDistanceX); var tileStepY = Math.Sign(tileDistanceY); float intersectXStep, intersectYStep; float nextIntersectX, nextIntersectY; getRayIntersectParameters(startX, diff.X, out nextIntersectX, out intersectXStep); getRayIntersectParameters(startY, diff.Y, out nextIntersectY, out intersectYStep); for (int i = 2; i < count; i++) { if (nextIntersectX < nextIntersectY) { tileX += tileStepX; nextIntersectX += intersectXStep; } else { tileY += tileStepY; nextIntersectY += intersectYStep; } yield return new Tile<TileInfo>(this.tilemap, tileX, tileY); } yield return new Tile<TileInfo>(this.tilemap, x1, y1); }
public static HitResult? TryHit(this IProjectileCollider collider, Ray ray) { var start = ray.Start.NumericValue; var dir = ray.Direction.NumericValue; var center = collider.Position.NumericValue; var radius = collider.Radius.NumericValue; var a = start.X - center.X; var b = start.Y - center.Y; var r2 = radius * radius; var c2 = dir.X * dir.X; var d2 = dir.Y * dir.Y; var cd = dir.X * dir.Y; var s = (r2 - a * a) * d2 + (r2 - b * b) * c2 + 2 * a * b * cd; // if s is less than 0, the solutions for f are imaginary // and the ray's line does not intersect the circle if (s >= 0) { var f = (Mathf.Sqrt(s) + a * dir.X + b * dir.Y) / -(c2 + d2); if (f <= 1) { var isInside = a * a + b * b < r2; if (f >= 0 || (isInside && !float.IsNegativeInfinity(f))) { // TODO: currently returns negative f if ray starts inside // consider treating this case differently? // do we ever care about 'exit hits'? // probably want to split entry/exit hits // how to handle moving targets? return new HitResult( new Position2(start + dir * f), Direction2.Of(new Vector2(a, b)), f, isInside ); } } } return null; }
public HitResult? Update(GameState game, TimeSpan time) { var vDelta = this.velocity * time; var hitResult = new Ray(this.position, vDelta) .Shoot(game, true, this.collideWithProjectileColliders); var f = hitResult.HasValue ? hitResult.Value.RayFactor : 1; var zDelta = this.vz * time; var z = this.z + zDelta; if (z < 0.U()) { f = this.z / zDelta; z = 0.U(); hitResult = new HitResult(this.position + vDelta * f, Direction2.Zero, f, false); } this.position += vDelta * f; this.z = z; this.vz += game.Gravity * time; return hitResult; }
public HitResult? TryHit(Ray ray) { var start = ray.Start.NumericValue; var direction = ray.Direction.NumericValue; var topLeft = this.TopLeft.NumericValue; var bottomRight = (this.TopLeft + this.Size).NumericValue; HitResult? result = null; var bestF = 1f; if (direction.X != 0) { { // left var f = (topLeft.X - start.X) / direction.X; if (f >= 0 && f < bestF) { var y = start.Y + direction.Y * f; if (y >= topLeft.Y && y <= bottomRight.Y) { bestF = f; result = new HitResult(new Position2(topLeft.X, y), Direction2.FromDegrees(180), f, start.X > topLeft.X); } } } { // right var f = (bottomRight.X - start.X) / direction.X; if (f >= 0 && f < bestF) { var y = start.Y + direction.Y * f; if (y >= topLeft.Y && y <= bottomRight.Y) { bestF = f; result = new HitResult(new Position2(bottomRight.X, y), Direction2.FromDegrees(0), f, start.X < bottomRight.X); } } } } if (direction.Y != 0) { { // top var f = (topLeft.Y - start.Y) / direction.Y; if (f >= 0 && f < bestF) { var x = start.X + direction.X * f; if (x >= topLeft.X && x <= bottomRight.X) { bestF = f; result = new HitResult(new Position2(x, topLeft.Y), Direction2.FromDegrees(270), f, start.Y > topLeft.Y); } } } { // bottom var f = (bottomRight.Y - start.Y) / direction.Y; if (f >= 0 && f < bestF) { var x = start.X + direction.X * f; if (x >= topLeft.X && x <= bottomRight.X) { bestF = f; result = new HitResult(new Position2(x, bottomRight.Y), Direction2.FromDegrees(90), f, start.Y < bottomRight.Y); } } } } return result; }