예제 #1
0
        /// <summary>
        /// Verifica si este poligono esta colisionando o va a colisionar
        /// con otro poligono dado.
        /// Esta verificación se realiza utilizando SAT (Separating Axis Theorem) la cual explica
        /// que, si existe una linea en el plano que pueda separar dos distintas figuras en diferentes
        /// lados por completo, entonces estas figuras no están colisionando.
        /// Nota: Este teorema solo debería aplicarse ante poligonos concavos, de otra forma el algoritmo
        /// no daria una respuesta correcta ante poligonos convexos.
        /// </summary>
        /// <param name="polygon2">Segundo poligono con el cual se hará la verificacion</param>
        /// <param name="distance">Distancia por la cual se movera el poligono principal (this)</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 IntersectWithPolygon(Polygon polygon2, ref Vector2 distance)
        {
            CollisionResult result = new CollisionResult();
            result.triggeringBody = this;
            result.affectedBody = polygon2;
            // Se asume que ambos poligonos intersectan hasta que
            // se pueda demostrar lo contrario
            result.intersect = true;
            result.willIntersect = true;
            // Se obtiene la cantidad de lados del poligono principal
            // y del segundo poligono
            int sideCountA = this.sides.Count;
            int sideCountB = polygon2.Sides.Count;
            float minIntervalDistance = float.PositiveInfinity;
            Vector2 translationAxis = new Vector2();
            // Lado actual a analizar
            Line currentSide;

            // Se realiza una verificacion por cada uno de los lados de ambos poligonos
            // hasta que pueda encontrarse alguna separación entre ambos o hasta que se hayan
            // verificado todos los lados
            for (int sideIndex = 0; sideIndex < sideCountA + sideCountB; sideIndex++)
            {
                if (sideIndex < sideCountA)
                {
                    currentSide = this.sides[sideIndex];
                }
                else
                {
                    currentSide = polygon2.Sides[sideIndex - sideCountA];
                }
                // Se obtiene un Vector perpendicular al lado actual con valores unitarios,
                // esto servira para obtener el eje sobre el cual deben hacerse las proyecciones
                Vector2 axis = new Vector2(-currentSide.Edge.Y, currentSide.Edge.X);
                axis.Normalize();

                // Se proyectan ambos poligonos sobre el mismo eje
                float minA = 0; float minB = 0; float maxA = 0; float maxB = 0;
                this.Project(ref axis, out minA, out maxA);
                polygon2.Project(ref axis, out minB, out maxB);

                // Se obtiene el intervalo entre ambos poligonos sobre el eje,
                // si el intervalo (separación) es mayor a 0 entonces los poligonos
                // no están intersectando
                float intervalDistance = IntervalDistance(minA, maxA, minB, maxB);
                if (intervalDistance > 0) result.intersect = false;

                // Luego se realizan los mismos calculos pero sumando el vector
                // de velocidad al poligono en (posible) movimiento
                float velocityProjection = Vector2.Dot(axis, distance);

                //if (velocityProjection < 0)
                //{
                    minA += velocityProjection;
                //}
                //else
                //{
                    maxA += velocityProjection;
                //}

                // Si el intervalo de distancia es menor a 0 con el poligono en movimiento,
                // entonces los poligonos tambien intersectaran al moverse
                intervalDistance = IntervalDistance(minA, maxA, minB, maxB);
                if (intervalDistance > 0) result.willIntersect = false;

                // Si ya sabemos que los poligonos estan intersectando y van a intersectar,
                // no hay mucho mas que hacer aquí asi que terminamos las verificaciones
                if (result.intersect == false && result.willIntersect == false)
                {
                    break;
                }

                // Si el intervalo de distancia es el minimo, se guarda
                // junto con el eje donde fue encontrado para asi poder separar
                // a los poligonos en esa dirección
                intervalDistance = Math.Abs(intervalDistance);
                if (intervalDistance < minIntervalDistance)
                {
                    minIntervalDistance = intervalDistance;
                    translationAxis = axis;

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

            // El vector minimo de transición
            // servira para separar ambos poligonos en caso de que colisionen.
            if (result.willIntersect)
            {
                result.minimumTranslationVector =
                       translationAxis * minIntervalDistance;
            }

            result.translationAxis = translationAxis;
            return result;
        }
예제 #2
0
        /// <summary>
        /// Construye un poligono según las opciones dadas
        /// </summary>
        /// <param name="id">Id del body</param>
        /// <param name="owner">Owner del body</param>
        /// <param name="isSolid">Si el body es sólido</param>
        /// <param name="relativeToFacing">Determina si se debe tomar en cuenta la dirección 
        /// a la que está mirando la entidad para posicionar el body 
        /// relativo a esa dirección</param>
        /// <param name="polygonPoints">Coordenadas de cada punto del poligono</param>
        /// <param name="startPosition">Coordenadas de inicio del body</param>
        /// <param name="startCenter">Determina si las coordenadas de inicio se refieren
        /// al punto de inicio desde donde se crea el body o al centro del mismo</param>
        /// <returns>Poligono generado</returns>
        /// <param name="layer">Capa de colisión donde se encontrará el body</param>
        /// <param name="colorTag">Tag de color especifico para responses que solo se activen ante dicho Tag</param>
        /// <returns>Poligono generado</returns>
        public static Polygon CreatePolygon(string id, IEntity owner, bool isSolid, bool relativeToFacing, List<Vector2> polygonPoints, Vector2 startPosition, bool startCenter, float layer, Color colorTag)
        {
            Polygon polygon = new Polygon(id, polygonPoints, owner, isSolid, startPosition);
            polygon.Layer = layer;
            polygon.ColorTag = colorTag;

            if (startCenter)
            {
                polygon.Offset(startPosition.X - polygon.Center.X, startPosition.Y - polygon.Center.Y);
            }

            if (relativeToFacing)
            {
                bool facingRight = owner.getState(EntityState.FacingRight);
                if (facingRight != true)
                {
                    polygon.MirrorHorizontal(owner.getVectorProperty(EntityProperty.Position));
                }
            }

            return polygon;
        }
예제 #3
0
        /// <summary>
        /// Construye un poligono según las opciones dadas
        /// </summary>
        /// <param name="id">Id del body</param>
        /// <param name="owner">Owner del body</param>
        /// <param name="isSolid">Si el body es sólido</param>
        /// <param name="relativeToFacing">Determina si se debe tomar en cuenta la dirección 
        /// a la que está mirando la entidad para posicionar el body 
        /// relativo a esa dirección</param>
        /// <param name="polygonPoints">Coordenadas de cada punto del poligono</param>
        /// <returns>Poligono generado</returns>
        public static Polygon CreatePolygon(string id, IEntity owner, bool isSolid, bool relativeToFacing, List<Vector2> polygonPoints)
        {
            Polygon polygon = new Polygon(id, polygonPoints, owner, isSolid);

            if (relativeToFacing)
            {
                bool facingRight = owner.getState(EntityState.FacingRight);
                if (facingRight != true)
                {
                    polygon.MirrorHorizontal(owner.getVectorProperty(EntityProperty.Position));
                }
            }

            return polygon;
        }
예제 #4
0
 /// <summary>
 /// Verifica si este cuerpo esta colisionando o va a colisionar
 /// con un poligono dado
 /// </summary>
 /// <param name="polygon">Poligono con el cual se hará la verificacion</param>
 /// <param name="distance">Distancia por la cual se movera este cuerpo</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 abstract CollisionResult IntersectWithPolygon(Polygon polygon, ref Vector2 distance);
예제 #5
0
파일: Sphere.cs 프로젝트: Towanime/FeInwork
        /// <summary>
        /// Verifica si esta esfera esta colisionando o va a colisionar
        /// con un poligono dado
        /// </summary>
        /// <param name="polygon">Poligono con el 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 IntersectWithPolygon(Polygon polygon, ref Vector2 distance)
        {
            CollisionResult result = new CollisionResult();
            result.triggeringBody = this;
            result.affectedBody = polygon;
            // Empezamos asumiendo que las dos figuras no
            // se encuentran intersectando
            result.intersect = false;
            result.willIntersect = false;
            // Se toman tanto la cantidad de puntos
            // como la cantidad de lados del poligono
            int sideCountPolygon = polygon.Sides.Count;
            int pointCountPolygon = polygon.Vertices.Count;
            // Dos valores distintos para guardar
            float minIntervalDistanceAfterMove = float.PositiveInfinity;
            float minIntervalDistance = float.PositiveInfinity;
            Vector2 translationAxis = new Vector2();

            // Ahora se estudia cada lado del poligono para verificar si el centro
            // de la esfera se encuentra perpendicular a algun punto de ese lado
            for (int sideIndex = 0; sideIndex < sideCountPolygon; sideIndex++)
            {
                Line currentSide = polygon.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 poligono)
                    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 poligono)
                    float intervalDistance = Math.Abs(pointA - pointB);
                    if (intervalDistance < minIntervalDistanceAfterMove)
                    {
                        minIntervalDistanceAfterMove = intervalDistance;
                        translationAxis = axis;

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

            // Luego se estudia la distancia entre cada vertice del poligono
            // contra el centro de la esfera, si se encuentra alguna distancia
            // menor que las ya guardadas entonces se guarda
            for (int pointIndex = 0; pointIndex < pointCountPolygon; pointIndex++)
            {
                Vector2 currentPoint = polygon.Vertices[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 = polygon.Center - this.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(currentPoint, this.Center);

                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 = polygon.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 poligono
            isInside = polygon.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;
        }
예제 #6
0
파일: Box.cs 프로젝트: Towanime/FeInwork
        /// <summary>
        /// Verifica si este rectangulo esta colisionando o va a colisionar
        /// con un poligono dado.
        /// Esta verificación se realiza utilizando SAT (Separating Axis Theorem) la cual explica
        /// que, si existe una linea en el plano que pueda separar dos distintas figuras en diferentes
        /// lados por completo, entonces estas figuras no están colisionando.
        /// Nota: Este teorema solo debería aplicarse ante poligonos concavos, de otra forma el algoritmo
        /// no daria una respuesta correcta ante poligonos convexos.
        /// </summary>
        /// <param name="polygon2">Poligono con el cual se hará la verificacion</param>
        /// <param name="distance">Distancia por la cual se movera este rectangulo</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 IntersectWithPolygon(Polygon polygon, ref Vector2 distance)
        {
            CollisionResult result = new CollisionResult();
            result.triggeringBody = this;
            result.affectedBody = polygon;
            // Se asume que ambas figuras intersectan hasta que
            // se pueda demostrar lo contrario
            result.intersect = true;
            result.willIntersect = true;
            // Se obtiene la cantidad de lados del rectangulo
            // y del poligono
            int edgeCountRectangle = 2;
            int edgeCountPolygon = polygon.Sides.Count;
            float minIntervalDistance = float.PositiveInfinity;
            Vector2 translationAxis = new Vector2();
            Vector2 axis = new Vector2();
            Line currentSide;

            // Se realiza una verificacion por cada uno de los lados del poligono y por dos lados
            // perpendiculares del rectangulo hasta que pueda encontrarse alguna separación entre
            // ambas o hasta que se hayan verificado todos los lados
            for (int edgeIndex = 0; edgeIndex < edgeCountPolygon + edgeCountRectangle; edgeIndex++)
            {
                if (edgeIndex < edgeCountRectangle)
                {
                    if (edgeIndex == 0)
                    {
                        currentSide = this.sides[0];
                    }
                    else
                    {
                        currentSide = this.sides[1];
                    }
                }
                else
                {
                    currentSide = polygon.Sides[edgeIndex - edgeCountRectangle];
                }

                // Se obtiene un Vector perpendicular al lado actual con valores unitarios,
                // esto servira para obtener el eje sobre el cual deben hacerse las proyecciones
                axis.X = -currentSide.Edge.Y;
                axis.Y = currentSide.Edge.X;
                axis.Normalize();

                // Se proyectan ambas figuras sobre el mismo eje
                float minA = 0; float minB = 0; float maxA = 0; float maxB = 0;
                this.Project(ref axis, out minA, out maxA);
                polygon.Project(ref axis, out minB, out maxB);

                // Se obtiene el intervalo entre ambas figuras sobre el eje,
                // si el intervalo (separación) es mayor a 0 entonces las figuras
                // no están intersectando
                float intervalDistance = IntervalDistance(minA, maxA, minB, maxB);
                if (intervalDistance > 0) result.intersect = false;

                float velocityProjection;
                Vector2.Dot(ref axis, ref distance, out velocityProjection);

                // Luego se realizan los mismos calculos pero sumando el vector
                // de velocidad al rectangulo en (posible) movimiento
                //if (velocityProjection < 0)
                //{
                    minA += velocityProjection;
                //}
                //else
                //{
                    maxA += velocityProjection;
                //}

                // Si el intervalo de distancia es menor a 0 con el rectangulo en movimiento,
                // entonces las figuras tambien intersectaran al moverse
                intervalDistance = IntervalDistance(minA, maxA, minB, maxB);
                if (intervalDistance > 0) result.willIntersect = false;

                // Si ya sabemos que los poligonos estan intersectando y van a intersectar,
                // no hay mucho mas que hacer aquí asi que terminamos las verificaciones
                if (result.intersect == false && result.willIntersect == false)
                {
                    break;
                }

                // Si el intervalo de distancia es el minimo, se guarda
                // junto con el eje donde fue encontrado para asi poder separar
                // a las figuras en esa dirección
                intervalDistance = Math.Abs(intervalDistance);
                if (intervalDistance < minIntervalDistance)
                {
                    minIntervalDistance = intervalDistance;
                    translationAxis = axis;

                    Vector2 d;
                    Vector2 secondBodyCenter = polygon.Center;
                    Vector2.Subtract(ref this.center, ref secondBodyCenter, out d);

                    float dot;
                    Vector2.Dot(ref d, ref translationAxis, out dot);
                    if (dot < 0)
                    {
                        Vector2.Multiply(ref translationAxis, -1, out translationAxis);
                    }
                }
            }

            // El vector minimo de transición
            // servira para separar ambas figuras en caso de que colisionen.
            if (result.willIntersect)
            {
                Vector2.Multiply(ref translationAxis, minIntervalDistance, out result.minimumTranslationVector);
            }

            result.translationAxis = translationAxis;
            return result;
        }