/// <summary> /// zero param constructor requires that a RenderableComponent be on the entity so that the collider can size itself when the /// entity is added to the scene. /// </summary> public BoxCollider() { // we stick a 1x1 box in here as a placeholder until the next frame when the Collider is added to the Entity and can get more // accurate auto-sizing data shape = new Box( 1, 1 ); _colliderRequiresAutoSizing = true; }
public static bool intersectMovingCircleBox( Circle s, Box b, Vector2 movement, out float time ) { // compute the AABB resulting from expanding b by sphere radius r var e = b.bounds; e.inflate( s.radius, s.radius ); // Intersect ray against expanded expanded Rectangle e. Exit with no intersection if ray // misses e, else get intersection point p and time t as result var ray = new Ray2D( s.position - movement, s.position ); if( !e.rayIntersects( ref ray, out time ) && time > 1.0f ) return false; // get the intersection point var point = ray.start + ray.direction * time; // compute which min and max faces of b the intersection point p lies outside of. Note, u and v cannot have the // same bits set and they must have at least one bit set among them. int u = 0, v = 0; if( point.X < b.bounds.left ) u |= 1; if( point.X > b.bounds.right ) v |= 1; if( point.Y < b.bounds.top ) u |= 2; if( point.Y > b.bounds.bottom ) v |= 2; // 'or' all set bits together into a bitmask (note u + v == u | v) var m = u + v; // if all 3 bits are set then point is in a vertex region if( m == 3 ) { // must now intersect segment against the capsules of the two edges meeting at the vert and return the best time, // if one or more hit // https://play.google.com/books/reader?printsec=frontcover&output=reader&id=VSoIBwAAAEAJ&pg=GBS.PA267 // https://github.com/noonat/hello/blob/580b986f3bb27b93645087441d2744eeb99d6d35/hello/collisions/Collision.hx#L675 //throw new NotImplementedException(); Debug.log( "m == 3. corner {0}", Time.frameCount ); } // if only one bit is set in m then point is in a face region if( ( m & ( m - 1 ) ) == 0 ) { Debug.drawHollowBox( point, 4, Color.Black, 0.4f ); // do nothing. time from the expanded rect intersection is the correct time return true; } // point is on an edge region. intersect against the capsule at the edge. return true; }
public static bool pointToBox( Vector2 point, Box box, out CollisionResult result ) { result = new CollisionResult(); if( box.containsPoint( point ) ) { // get the point in the space of the Box result.point = box.bounds.getClosestPointOnRectangleBorderToPoint( point, out result.normal ); result.minimumTranslationVector = point - result.point; return true; } return false; }
public static bool boxToBox( Box first, Box second, out CollisionResult result ) { result = new CollisionResult(); var minkowskiDiff = minkowskiDifference( first, second ); if( minkowskiDiff.contains( 0f, 0f ) ) { // calculate the MTV. if it is zero then we can just call this a non-collision result.minimumTranslationVector = minkowskiDiff.getClosestPointOnBoundsToOrigin(); if( result.minimumTranslationVector == Vector2.Zero ) return false; result.normal = -result.minimumTranslationVector; result.normal.Normalize(); return true; } return false; }
/// <summary> /// checks the result of a box being moved by deltaMovement with second /// </summary> /// <returns><c>true</c>, if to box cast was boxed, <c>false</c> otherwise.</returns> /// <param name="first">First.</param> /// <param name="second">Second.</param> /// <param name="deltaMovement">Delta movement.</param> /// <param name="hit">Hit.</param> public static bool boxToBoxCast( Box first, Box second, Vector2 movement, out RaycastHit hit ) { // http://hamaluik.com/posts/swept-aabb-collision-using-minkowski-difference/ hit = new RaycastHit(); // first we check for an overlap. if we have an overlap we dont do the sweep test var minkowskiDiff = minkowskiDifference( first, second ); if( minkowskiDiff.contains( 0f, 0f ) ) { // calculate the MTV. if it is zero then we can just call this a non-collision var mtv = minkowskiDiff.getClosestPointOnBoundsToOrigin(); if( mtv == Vector2.Zero ) return false; hit.normal = -mtv; hit.normal.Normalize(); hit.distance = 0f; hit.fraction = 0f; return true; } else { // ray-cast the movement vector against the Minkowski AABB var ray = new Ray2D( Vector2.Zero, -movement ); float fraction; if( minkowskiDiff.rayIntersects( ref ray, out fraction ) && fraction <= 1.0f ) { hit.fraction = fraction; hit.distance = movement.Length() * fraction; hit.normal = -movement; hit.normal.Normalize(); hit.centroid = first.bounds.center + movement * fraction; return true; } } return false; }
/// <summary> /// works for circles whos center is in the box as well as just overlapping with the center out of the box. /// </summary> /// <returns><c>true</c>, if to box was circled, <c>false</c> otherwise.</returns> /// <param name="circle">First.</param> /// <param name="box">Second.</param> /// <param name="result">Result.</param> public static bool circleToBox( Circle circle, Box box, out CollisionResult result ) { result = new CollisionResult(); var closestPointOnBounds = box.bounds.getClosestPointOnRectangleBorderToPoint( circle.position, out result.normal ); // deal with circles whos center is in the box first since its cheaper to see if we are contained if( box.containsPoint( circle.position ) ) { result.point = closestPointOnBounds; // calculate mtv. Find the safe, non-collided position and get the mtv from that. var safePlace = closestPointOnBounds + result.normal * circle.radius; result.minimumTranslationVector = circle.position - safePlace; return true; } float sqrDistance; Vector2.DistanceSquared( ref closestPointOnBounds, ref circle.position, out sqrDistance ); // see if the point on the box is less than radius from the circle if( sqrDistance == 0 ) { result.minimumTranslationVector = result.normal * circle.radius; } else if( sqrDistance <= circle.radius * circle.radius ) { result.normal = circle.position - closestPointOnBounds; var depth = result.normal.Length() - circle.radius; result.point = closestPointOnBounds; Vector2Ext.normalize( ref result.normal ); result.minimumTranslationVector = depth * result.normal; return true; } return false; }
public BoxCollider( float x, float y, float width, float height ) { _localOffset = new Vector2( x, y ); shape = new Box( width, height ); }
/// <summary> /// note: if circle center lies in the box the collision result will be incorrect! /// </summary> /// <returns><c>true</c>, if to box was circled, <c>false</c> otherwise.</returns> /// <param name="first">First.</param> /// <param name="second">Second.</param> /// <param name="result">Result.</param> public static bool circleToBox( Circle first, Box second, out CollisionResult result ) { result = new CollisionResult(); var closestPointOnBounds = second.bounds.getClosestPointOnRectangleBorderToPoint( first.position ); float sqrDistance; Vector2.DistanceSquared( ref closestPointOnBounds, ref first.position, out sqrDistance ); // see if the point on the box is less than radius from the circle if( sqrDistance <= first.radius * first.radius ) { var distanceToCircle = first.position - closestPointOnBounds; var depth = distanceToCircle.Length() - first.radius; result.point = closestPointOnBounds; result.normal = Vector2.Normalize( distanceToCircle ); result.minimumTranslationVector = depth * result.normal; return true; } return false; }
static RectangleF minkowskiDifference( Box first, Box second ) { var topLeft = first.position - second.bounds.max; var fullSize = first.bounds.size + second.bounds.size; return new RectangleF( topLeft.X, topLeft.Y, fullSize.X, fullSize.Y ); }
public RectangleF minkowskiDifference( Box other ) { var topLeft = position - other.bounds.max; var fullSize = bounds.size + other.bounds.size; return new RectangleF( topLeft.X, topLeft.Y, fullSize.X, fullSize.Y ); }
static RectangleF minkowskiDifference( Box first, Box second ) { // we need the top-left of our first box but it must include our motion. Collider only modifies position with the motion so we // need to figure out what the motion was using just the position. var positionOffset = first.position - ( first.bounds.location + first.bounds.size / 2f ); var topLeft = first.bounds.location + positionOffset - second.bounds.max; var fullSize = first.bounds.size + second.bounds.size; return new RectangleF( topLeft.X, topLeft.Y, fullSize.X, fullSize.Y ); }
/// <summary> /// checks to see if circle overlaps box and returns (via out param) the point of intersection /// </summary> /// <returns><c>true</c>, if circle box was tested, <c>false</c> otherwise.</returns> /// <param name="circle">Circle.</param> /// <param name="box">Box.</param> /// <param name="point">Point.</param> public static bool testCircleBox( Circle circle, Box box, out Vector2 point ) { // find the point closest to the sphere center point = box.bounds.getClosestPointOnRectangleFToPoint( circle.position ); // circle and box intersect if sqr distance from circle center to point is less than the circle sqr radius var v = point - circle.position; float dist; Vector2.Dot( ref v, ref v, out dist ); return dist <= circle.radius * circle.radius; }