/// <summary> /// Verifica si este poligono esta colisionando o va a colisionar /// con una caja (rectangulo) dada /// 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="box">Caja con la cual se hará la verificacion</param> /// <param name="distance">Distancia por la cual se movera este poligono</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; // 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 poligono y del // rectangulo (Para el rectangulo solo es necesario estudiar dos lados) int sideCountPolygon = this.sides.Count; int sideCountRectangle = 2; float minIntervalDistance = float.PositiveInfinity; Vector2 translationAxis = new Vector2(); // Lado actual a analizar 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 sideIndex = 0; sideIndex < sideCountPolygon + sideCountRectangle; sideIndex++) { if (sideIndex < sideCountPolygon) { currentSide = this.sides[sideIndex]; } else { if (sideIndex - sideCountPolygon == 0) { currentSide = box.Sides[0]; } else { currentSide = box.Sides[1]; } } // 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 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); box.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; // 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 las figuras tambien intersectaran al moverse intervalDistance = IntervalDistance(minA, maxA, minB, maxB); if (intervalDistance > 0) result.willIntersect = false; // Si ya sabemos que las figuras 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 = this.Center - box.Center; if (Vector2.Dot(d, translationAxis) < 0) { translationAxis = -translationAxis; } } } // El vector minimo de transición // servira para separar ambas figuras en caso de que colisionen. if (result.willIntersect) { result.minimumTranslationVector = translationAxis * minIntervalDistance; } result.translationAxis = translationAxis; return result; }
/// <summary> /// Verifica si este rectangulo esta colisionando o va a colisionar /// con otro rectangulo 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="box">Segundo rectangulo con el cual se hará la verificacion</param> /// <param name="distance">Distancia por la cual se movera el rectangulo 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 IntersectWithBox(Box box, ref Vector2 distance) { CollisionResult result = new CollisionResult(); result.triggeringBody = this; result.affectedBody = box; // Se asume que ambos rectangulos intersectan hasta que // se pueda demostrar lo contrario result.intersect = true; result.willIntersect = true; float minIntervalDistance = float.PositiveInfinity; Vector2 translationAxis = new Vector2(); Line currentSide; Vector2 axis = Vector2.Zero; // Se realiza una verificacion por dos lados perpendiculares de cada rectangulo // hasta que pueda encontrarse alguna separación entre ambos o hasta que se hayan // verificado todos los lados for (int edgeIndex = 0; edgeIndex < 4; edgeIndex++) { if (edgeIndex < 2) { if (edgeIndex == 0) { currentSide = this.sides[0]; } else { currentSide = this.sides[1]; } } else { if (edgeIndex - 2 == 0) { currentSide = box.sides[0]; } else { currentSide = box.sides[1]; } } // 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 ambos rectangulos sobre el mismo eje float minA = 0; float minB = 0; float maxA = 0; float maxB = 0; this.Project(ref axis, out minA, out maxA); box.Project(ref axis, out minB, out maxB); // Se obtiene el intervalo entre ambos rectangulos sobre el eje, // si el intervalo (separación) es mayor a 0 entonces los rectangulos // 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 rectangulo en (posible) movimiento float velocityProjection; Vector2.Dot(ref axis, ref distance, out velocityProjection); //if (velocityProjection < 0) //{ minA += velocityProjection; //} //else //{ maxA += velocityProjection; //} // Si el intervalo de distancia es menor a 0 con el rectangulo en movimiento, // entonces las rectangulos tambien intersectaran al moverse intervalDistance = IntervalDistance(minA, maxA, minB, maxB); if (intervalDistance > 0) result.willIntersect = false; // Si ya sabemos que los rectangulos 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 rectangulos en esa dirección intervalDistance = Math.Abs(intervalDistance); if (intervalDistance < minIntervalDistance) { minIntervalDistance = intervalDistance; translationAxis = axis; Vector2 d; Vector2 secondBodyCenter = box.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 ambos rectangulos en caso de que colisionen. if (result.willIntersect) { Vector2.Multiply(ref translationAxis, minIntervalDistance, out result.minimumTranslationVector); } result.translationAxis = translationAxis; return result; }