Esempio n. 1
0
        /// <summary>
        /// Verifica si esta esfera esta colisionando o va a colisionar
        /// con una caja (rectangulo) dada
        /// </summary>
        /// <param name="box">Caja con la cual se hará la verificacion</param>
        /// <param name="distance">Distancia por la cual se movera esta esfera</param>
        /// <param name="result">Referencia del objeto que contendrá la información necesaria sobre 
        /// si se esta intersectando, si se intersectará y la distancia necesaria 
        /// para evitar una intersección</param>
        protected override CollisionResult IntersectWithBox(Box box, ref Vector2 distance)
        {
            CollisionResult result = new CollisionResult();
            result.triggeringBody = this;
            result.affectedBody = box;
            // Empezamos asumiendo que las dos figuras no
            // se encuentran intersectando
            result.intersect = false;
            result.willIntersect = false;
            int sideCountRectangle = 4;
            int pointCountRectangle = 4;
            // Dos valores distintos para guardar
            float minIntervalDistanceAfterMove = float.PositiveInfinity;
            float minIntervalDistance = float.PositiveInfinity;
            Vector2 translationAxis = new Vector2();

            // Ahora se estudia cada lado del rectangulo para verificar si el centro
            // de la esfera se encuentra perpendicular a algun punto de ese lado
            for (int sideIndex = 0; sideIndex < sideCountRectangle; sideIndex++)
            {
                Line currentSide = box.Sides[sideIndex];

                // Se crea un vector paralelo al lado donde puedan proyectase
                // ambos puntos del lado actual mas el centro de la esfera
                Vector2 axis = new Vector2(currentSide.Edge.X, currentSide.Edge.Y);
                axis.Normalize();

                float centerA = Vector2.Dot(axis, this.Center);
                float minB = Vector2.Dot(axis, currentSide.StartPoint);
                float maxB = Vector2.Dot(axis, currentSide.EndPoint);

                float velocityProjection = Vector2.Dot(axis, distance);

                // Se realiza un chequeo preliminar antes de sumar el vector
                // de distancia
                #region Verificaciones de intersección actual

                // Si el punto centro se encuentra perpendicular a algun
                // punto del lado actual, entonces la esfera puede encontrarse en esa region
                if (minB <= centerA && maxB >= centerA)
                {
                    // Creamos un eje perpendicular a la linea para obtener la distancia
                    // entre un punto de la linea y el centro de la esfera
                    axis = new Vector2(-currentSide.Edge.Y, currentSide.Edge.X);
                    axis.Normalize();

                    // Ya que el eje es perpendicular, tanto el punto inicial de la linea
                    // como el final terminan en la misma posicion al ser proyectados
                    float pointA = Vector2.Dot(axis, this.Center);
                    float pointB = Vector2.Dot(axis, currentSide.EndPoint);

                    // Se obtiene el intervalo y se guarda en caso de que sea menor
                    // al intervalo anterior (La esfera se encontrara en la region de voronoi
                    // que tenga el punto mas cercano desde la esfera hacia el rectangulo)
                    float intervalDistance = Math.Abs(pointA - pointB);
                    if (intervalDistance < minIntervalDistance)
                    {
                        minIntervalDistance = intervalDistance;
                    }
                }
                #endregion

                // Aplicamos la proyeccion de velocidad a el lado actual
                centerA += velocityProjection;

                // Si el punto centro se encuentra perpendicular a algun
                // punto del lado actual, entonces la esfera puede encontrarse en esa region
                if (minB <= centerA && maxB >= centerA)
                {
                    // Creamos un eje perpendicular a la linea para obtener la distancia
                    // entre un punto de la linea y el centro de la esfera
                    axis = new Vector2(-currentSide.Edge.Y, currentSide.Edge.X);
                    axis.Normalize();

                    // Volvemos a aplicar la proyeccion de velocidad puesto que
                    // esta vez estamos proyectando en un diferente eje
                    velocityProjection = Vector2.Dot(axis, distance);
                    // Ya que el eje es perpendicular, tanto el punto inicial de la linea
                    // como el final terminan en la misma posicion al ser proyectados
                    float pointA = Vector2.Dot(axis, this.Center) + velocityProjection;
                    float pointB = Vector2.Dot(axis, currentSide.EndPoint);

                    // Se obtiene el intervalo y se guarda en caso de que sea menor
                    // al intervalo anterior (La esfera se encontrara en la region de voronoi
                    // que tenga el punto mas cercano desde la esfera hacia el rectangulo)
                    float intervalDistance = Math.Abs(pointA - pointB);
                    if (intervalDistance < minIntervalDistanceAfterMove)
                    {
                        minIntervalDistanceAfterMove = intervalDistance;
                        translationAxis = axis;

                        Vector2 d = this.Center - box.Center;
                        if (Vector2.Dot(d, translationAxis) < 0)
                        {
                            translationAxis = -translationAxis;
                        }
                    }
                }
            }

            // Luego se estudia la distancia entre cada vertice del rectangulo
            // contra el centro de la esfera, si se encuentra alguna distancia
            // menor que las ya guardadas entonces se guarda
            for (int pointIndex = 0; pointIndex < pointCountRectangle; pointIndex++)
            {
                Vector2 currentPoint = box.Points[pointIndex];
                // Creamos una linea que vaya desde el vertice hasta el centro
                // de la esfera, sumandole el vector de distancia al vertice
                Line line = new Line(this.Center + distance, currentPoint);

                Vector2 axis = new Vector2(line.Edge.X, line.Edge.Y);
                axis.Normalize();

                if (line.Lenght < minIntervalDistanceAfterMove)
                {
                    minIntervalDistanceAfterMove = line.Lenght;
                    translationAxis = axis;

                    Vector2 d = this.Center - box.Center;
                    if (Vector2.Dot(d, translationAxis) < 0)
                    {
                        translationAxis = -translationAxis;
                    }
                }

                // Misma verificacion sin sumar el vector de distancia
                #region Verificaciones de intersección actual
                line = new Line(this.Center, currentPoint);

                axis = new Vector2(line.Edge.X, line.Edge.Y);
                axis.Normalize();

                if (line.Lenght < minIntervalDistance)
                {
                    minIntervalDistance = line.Lenght;
                }
                #endregion
            }

            // Se verifica si el poligono intersecta
            bool isInside = box.PointInBody(this.Center);
            if (isInside || minIntervalDistance < this.Radius)
            {
                result.intersect = true;
            }

            // Se verifica si intersectaran y aplica un vector de transicion
            // diferente dependiendo de si el centro de la esfera se encuentra
            // dentro o fuera del rectangulo
            isInside = box.PointInBody(this.Center - distance);
            if (isInside)
            {
                result.minimumTranslationVector =
                       translationAxis * (this.Radius + minIntervalDistanceAfterMove);
                result.willIntersect = true;
            }
            else if (minIntervalDistanceAfterMove < this.Radius)
            {
                result.minimumTranslationVector =
                       translationAxis * Math.Abs(this.Radius - minIntervalDistanceAfterMove);
                result.willIntersect = true;
            }

            result.translationAxis = translationAxis;
            return result;
        }