private void initialize() { Program.GAME.ComponentManager.addUpdateableOnly(this); // crea la entidad this.collisionEntity = new Entity(GlobalIDs.CAMERA_ENTITY_ID); // crea body de colision // para que quede justo en el centro de focus float x = (viewportWidth / 2) - (initCollisionWidth / 2); float y = (viewportHeight / 2) - (initCollisionHeight / 2); this.cameraCollisionBody = ShapeFactory.CreateRectangle(GlobalIDs.CAMERA_COLLISION_BODY_ID, collisionEntity, true, false/*, viewportWidth - 80, viewportHeight - 80, new Vector2(40, 40)*/, viewportWidth, viewportHeight, Vector2.Zero, false, GameLayers.FRONT_HUD_AREA); // componente de colision basico this.collisionEntity.addComponent(new AnimatedCollisionComponent(collisionEntity, this.cameraCollisionBody)); // asigna x y y this.collisionEntity.addVectorProperty(EntityProperty.Position, new Vector2(x, y)); // registrar listener EventManager.Instance.addListener(EventType.POSITION_CHANGED_EVENT, collisionEntity, this); }
/// <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> /// Construye un rectangulo 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="width">Ancho del rectangulo</param> /// <param name="height">Alto del rectangulo</param> /// <param name="startPosition">Coordenadas de inicio del body</param> /// <param name="startCenter">Determina si las coordenadas de inicio se refieren /// al primer punto del body o al centro de este</param> /// <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>Rectangulo generado</returns> public static Box CreateRectangle(string id, IEntity owner, bool isSolid, bool relativeToFacing, float width, float height, Vector2 startPosition, bool startCenter, float layer, Color colorTag) { if (startCenter) { startPosition.X -= width / 2; startPosition.Y -= height / 2; } Box box = new Box(id, startPosition.X, startPosition.Y, width, height, owner, isSolid); box.Layer = layer; box.ColorTag = colorTag; if (relativeToFacing) { bool facingRight = owner.getState(EntityState.FacingRight); if (facingRight != true) { box.MirrorHorizontal(owner.getVectorProperty(EntityProperty.Position)); } } return box; }
/// <summary> /// Construye un rectangulo 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="width">Ancho del rectangulo</param> /// <param name="height">Alto del rectangulo</param> /// <returns>Rectangulo generado</returns> public static Box CreateRectangle(string id, IEntity owner, bool isSolid, bool relativeToFacing, float width, float height) { Box box = new Box(id, 0, 0, width, height, owner, isSolid); if (relativeToFacing) { bool facingRight = owner.getState(EntityState.FacingRight); if (facingRight != true) { box.MirrorHorizontal(owner.getVectorProperty(EntityProperty.Position)); } } return box; }
/// <summary> /// Verifica si este cuerpo 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 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 IntersectWithBox(Box box, ref Vector2 distance);
/// <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; }
/// <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; }