Beispiel #1
0
        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);
        }
Beispiel #2
0
        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);
        }