public bool RayCastToCollider(Ray ray, out Hit hit, Collider collider) { hit = new Hit(0, null); Collider rect = collider; Vector2 relative = rect.GetCentrePoint() - ray.position; Matrix3 t = collider.GetConnectedPhysicsObject().GetGlobalTransform(); float minTotalDistance = 0; float maxTotalDistance = float.PositiveInfinity; float scale = t.GetScaleX(); Vector2 max = rect.GetLocalHalfWidthHeightVector() * scale; Vector2 min = max * -1; Vector2 x = new Vector2(t.m11, t.m21) / scale; float e = Vector2.Dot(x, relative); float f = Vector2.Dot(x, ray.direction); float minimumDistance; float maximumDistance; if (Math.Abs(f) > 0.0001f) { f = 1 / f; minimumDistance = (e + min.x) * f; maximumDistance = (e + max.x) * f; if (minimumDistance > maximumDistance) { float cache = minimumDistance; minimumDistance = maximumDistance; maximumDistance = cache; } maxTotalDistance = maximumDistance; if (minimumDistance > 0) { minTotalDistance = minimumDistance; } if (maxTotalDistance < minTotalDistance) { return(false); } } else if (min.x - e > 0 || max.x - e < 0) { return(false); } Vector2 y = new Vector2(t.m12, t.m22) / scale; e = Vector2.Dot(y, relative); f = Vector2.Dot(y, ray.direction); if (Math.Abs(f) > 0.0001f) { f = 1 / f; minimumDistance = (e + min.y) * f; maximumDistance = (e + max.y) * f; if (minimumDistance > maximumDistance) { float cache = minimumDistance; minimumDistance = maximumDistance; maximumDistance = cache; } if (maximumDistance < maxTotalDistance) { maxTotalDistance = maximumDistance; } if (minimumDistance > minTotalDistance) { minTotalDistance = minimumDistance; } if (maxTotalDistance < minTotalDistance) { return(false); } } else if (min.y - e > 0 || max.y - e < 0) { return(false); } hit = new Hit(minTotalDistance, collider.GetConnectedPhysicsObject()); return(true); }
public bool RayCast(Ray ray, out Hit hit, float magnitude = float.PositiveInfinity, params CollisionLayer[] ignoredLayers) { //this raycast was done using this algorithm //http://www.opengl-tutorial.org/miscellaneous/clicking-on-objects/picking-with-custom-ray-obb-function/ //this page isn't the only one to use these concepts but it is where I found out how to do this without (many) transformation shenanigans bool collided = false; float distance = float.PositiveInfinity; PhysicsObject colliderHit = null; for (int i = 0; i < objList.Count; i++) { Collider rect = objList[i].GetCollider(); if (ignoredLayers.Contains(rect.GetLayer()) || (ray.position - rect.GetCentrePoint()).MagnitudeSquared() > magnitude * magnitude) { continue; } Vector2 relative = rect.GetCentrePoint() - ray.position; Matrix3 t = objList[i].GetGlobalTransform(); float minTotalDistance = 0; float maxTotalDistance = float.PositiveInfinity; //everything in the game has uniform scaling on x and y (because otherwise there are rotation issues) so only one getscale is required float scale = t.GetScaleX(); Vector2 max = rect.GetLocalHalfWidthHeightVector() * scale; Vector2 min = max * -1; //X axis //We don't use t.GetRightVector() to cut down on the square rooting Vector2 x = new Vector2(t.m11, t.m21) / scale; //the distance between the ray and the centre of the rectangle along the rectangle's x axis float e = Vector2.Dot(x, relative); //The projection of the ray's direction onto the rectangle's x axis float f = Vector2.Dot(x, ray.direction); float minimumDistance; float maximumDistance; //if f is around zero that means f is almost the same as x: aka it is parallel and is not colliding (unless you have a really massive object) if (Math.Abs(f) > 0.0001f) { f = 1 / f; //these contain the distance between the ray position and the hit point //there are two because we are crossing two lines for each axis //e is translation, f is rotation minimumDistance = (e + min.x) * f; maximumDistance = (e + max.x) * f; if (minimumDistance > maximumDistance) { float cache = minimumDistance; minimumDistance = maximumDistance; maximumDistance = cache; } maxTotalDistance = maximumDistance; //min distance should be more than zero. If it isn't, it should be ignored if (minimumDistance > 0) { minTotalDistance = minimumDistance; } } else if (min.x - e > 0 || max.x - e < 0) { continue; } //Y axis (same as x basically) Vector2 y = new Vector2(t.m12, t.m22) / scale; e = Vector2.Dot(y, relative); f = Vector2.Dot(y, ray.direction); if (Math.Abs(f) > 0.0001f) { f = 1 / f; minimumDistance = (e + min.y) * f; maximumDistance = (e + max.y) * f; if (minimumDistance > maximumDistance) { float cache = minimumDistance; minimumDistance = maximumDistance; maximumDistance = cache; } if (maximumDistance < maxTotalDistance) { maxTotalDistance = maximumDistance; } if (minimumDistance > minTotalDistance) { minTotalDistance = minimumDistance; } if (maxTotalDistance < minTotalDistance) { continue; } } else if (min.y - e > 0 || max.y - e < 0) { continue; } collided = true; if (minTotalDistance < distance) { distance = minTotalDistance; colliderHit = objList[i]; } } hit = new Hit(distance, colliderHit); if (collided) { return(true); } return(false); }