public CollisionContact(ICutlassCollidable a, ICutlassCollidable b, Vector2 normal, float distance) { A = a; B = b; Normal = normal; Distance = distance; }
private void AddCollidableObject(ICutlassCollidable collidableObject) { //Want to extend collisions slightly outside visible screen. BoundingRectangle collisionSpace = BoundingRectangle.Scale(_ViewScreen.VisibleArea, 1.1f, _ViewScreen.VisibleArea.Center); BoundingRectangle intersection = BoundingRectangle.Intersection(collidableObject.CurrentFrameBoundingRect, collisionSpace); int minHorizontalGroup = -1, maxHorizontalGroup = -1, minVerticalGroup = -1, maxVerticalGroup = -1; if (!intersection.IsZero) { float groupWidth = collisionSpace.Width / _HorizontalGroups; float groupHeight = collisionSpace.Height / _VerticalGroups; minHorizontalGroup = Math.Max(0, (int)((intersection.Left - collisionSpace.Left) / groupWidth)); maxHorizontalGroup = Math.Min(_HorizontalGroups - 1, (int)((intersection.Right - collisionSpace.Left) / groupWidth)); minVerticalGroup = Math.Max(0, (int)((intersection.Top - collisionSpace.Top) / groupHeight)); maxVerticalGroup = Math.Min(_VerticalGroups - 1, (int)((intersection.Bottom - collisionSpace.Top) / groupHeight)); for (int i = minHorizontalGroup; i <= maxHorizontalGroup; i++) { for (int j = minVerticalGroup; j <= maxVerticalGroup; j++) { if (_CurrentCollidableObjects[i, j] == null) { _CurrentCollidableObjects[i, j] = new List <ICutlassCollidable>(); } _CurrentCollidableObjects[i, j].Add(collidableObject); } } } }
public void CollisionDetectedWithCorrection(ICutlassCollidable collisionTarget, Vector2 normal, float distance) { //Check for jumping through top-only collisions, or level-transition zones. if ((collisionTarget.Side == CollisionSide.Top && normal.Y == -1 && _IsJumpingDown) || collisionTarget is LevelTransition) { return; } //get the separation and penetration separately, this is to stop penetration //from causing the obejcts to ping apart float separation = Math.Max(distance, 0.0f); float penetration = Math.Min(distance, 0.0f); //get relative normal velocity so object will stop exactly on surface. float relativeNormalVelocity = 0.0f; relativeNormalVelocity = VectorUtilities.DotProduct(Velocity, normal); if (relativeNormalVelocity < 0) { //remove normal velocity Velocity -= normal * relativeNormalVelocity; } //is this ground? if (normal.Y < 0.0f) { _WasOnGround = true; //StandingOn.Add(contact.B); } }
private void CheckCollisionsInGroup(GameTime gameTime, List <ICutlassCollidable> collidableObjects) { if (collidableObjects == null || collidableObjects.Count <= 1) { return; } for (int i = 0; i < collidableObjects.Count; i++) { ICutlassCollidable first = collidableObjects[i]; for (int j = i + 1; j < collidableObjects.Count; j++) { ICutlassCollidable second = collidableObjects[j]; bool collisionCategoriesOverlap = CheckCollisionCategories(first, second); //If both are stationary, or neither is stationary, detect collision but don't do anything about it. if (first.Stationary == second.Stationary) { if (collisionCategoriesOverlap && first.NextFrameBoundingRect.Intersects(second.NextFrameBoundingRect)) { first.CollisionDetected(second); second.CollisionDetected(first); } } //If only one is stationary, detect collision and correct non-stationary object else { //contact.A is non-stationary object. CollisionContact collisionContact; if (first.Stationary) { collisionContact = new CollisionContact() { A = second, B = first } } ; else { collisionContact = new CollisionContact() { A = first, B = second } }; if (collisionCategoriesOverlap && CalculateCollisionCorrection(gameTime, ref collisionContact) && !IsInternalEdge(collisionContact)) { collisionContact.A.CollisionDetectedWithCorrection(collisionContact.B, collisionContact.Normal, collisionContact.Distance); collisionContact.B.CollisionDetected(collisionContact.A); } } } } }
public void AddObject(ICutlassSceneObject o) { SceneObjectId sceneObjectId; lock (_SceneObjectIdLock) { sceneObjectId = _NextSceneObjectId++; } Objects.Add(sceneObjectId, o); //Add object to necessary lists. ICutlassLoadable loadable = o as ICutlassLoadable; if (loadable != null) { LoadableObjects.Add(sceneObjectId, loadable); } ICutlassMovable movable = o as ICutlassMovable; if (movable != null) { MovableObjects.Add(sceneObjectId, movable); } ICutlassCollidable collidable = o as ICutlassCollidable; if (collidable != null) { CollidableObjects.Add(sceneObjectId, collidable); } ICutlassUpdateable updateable = o as ICutlassUpdateable; if (updateable != null) { UpdateableObjects.Add(sceneObjectId, updateable); } ICutlassDrawable drawable = o as ICutlassDrawable; if (drawable != null) { DrawableObjects.Add(sceneObjectId, drawable); } //If this scene has already been initialized, initialize this object now. if (_Initialized && loadable != null) { loadable.LoadContent(); } }
public virtual void CollisionDetectedWithCorrection(ICutlassCollidable collisionTarget, Vector2 normal, float distance) { }
public virtual void CollisionDetected(ICutlassCollidable collisionTarget) { }
private void AddCollidableObject(ICutlassCollidable collidableObject) { //Want to extend collisions slightly outside visible screen. BoundingRectangle collisionSpace = BoundingRectangle.Scale(_ViewScreen.VisibleArea, 1.1f, _ViewScreen.VisibleArea.Center); BoundingRectangle intersection = BoundingRectangle.Intersection(collidableObject.CurrentFrameBoundingRect, collisionSpace); int minHorizontalGroup = -1, maxHorizontalGroup = -1, minVerticalGroup = -1, maxVerticalGroup = -1; if (!intersection.IsZero) { float groupWidth = collisionSpace.Width / _HorizontalGroups; float groupHeight = collisionSpace.Height / _VerticalGroups; minHorizontalGroup = Math.Max(0, (int)((intersection.Left - collisionSpace.Left) / groupWidth)); maxHorizontalGroup = Math.Min(_HorizontalGroups - 1, (int)((intersection.Right - collisionSpace.Left) / groupWidth)); minVerticalGroup = Math.Max(0, (int)((intersection.Top - collisionSpace.Top) / groupHeight)); maxVerticalGroup = Math.Min(_VerticalGroups - 1, (int)((intersection.Bottom - collisionSpace.Top) / groupHeight)); for (int i = minHorizontalGroup; i <= maxHorizontalGroup; i++) for (int j = minVerticalGroup; j <= maxVerticalGroup; j++) { if (_CurrentCollidableObjects[i, j] == null) _CurrentCollidableObjects[i, j] = new List<ICutlassCollidable>(); _CurrentCollidableObjects[i, j].Add(collidableObject); } } }
private bool CheckCollisionCategories(ICutlassCollidable first, ICutlassCollidable second) { //No collision if their catgories don't overlap. return ((first.Category & second.CategoryMask) != 0 && (second.Category & first.CategoryMask) != 0); }
public void CollisionDetectedWithCorrection(ICutlassCollidable collisionTarget, Vector2 normal, float distance) { //Check for jumping through top-only collisions, or level-transition zones. if ((collisionTarget.Side == CollisionSide.Top && normal.Y == -1 && _IsJumpingDown) || collisionTarget is LevelTransition) return; //get the separation and penetration separately, this is to stop penetration //from causing the obejcts to ping apart float separation = Math.Max(distance, 0.0f); float penetration = Math.Min(distance, 0.0f); //get relative normal velocity so object will stop exactly on surface. float relativeNormalVelocity = 0.0f; relativeNormalVelocity = VectorUtilities.DotProduct(Velocity, normal); if (relativeNormalVelocity < 0) { //remove normal velocity Velocity -= normal * relativeNormalVelocity; } //is this ground? if (normal.Y < 0.0f) { _WasOnGround = true; //StandingOn.Add(contact.B); } }
private bool CalculateCollisionCorrection(GameTime gameTime, ref CollisionContact contact) { //Non-stationary object ICutlassCollidable first = contact.A; //Stationary object ICutlassCollidable second = contact.B; Vector2 halfExtents = new Vector2(second.CurrentFrameBoundingRect.Width / 2, second.CurrentFrameBoundingRect.Height / 2); BoundingRectangle firstPlusHalfExtents = new BoundingRectangle(first.CurrentFrameBoundingRect.Left - halfExtents.X, first.CurrentFrameBoundingRect.Top - halfExtents.Y, first.CurrentFrameBoundingRect.Width + 2 * halfExtents.X, first.CurrentFrameBoundingRect.Height + 2 * halfExtents.Y); //Get closest point Vector2 closestPoint = second.CurrentFrameBoundingRect.Center; //X Axis float x = second.CurrentFrameBoundingRect.Center.X; if (x < firstPlusHalfExtents.Left) { x = firstPlusHalfExtents.Left; } if (x > firstPlusHalfExtents.Right) { x = firstPlusHalfExtents.Right; } closestPoint.X = x; //Y Axis float y = second.CurrentFrameBoundingRect.Center.Y; if (y < firstPlusHalfExtents.Top) { y = firstPlusHalfExtents.Top; } if (y > firstPlusHalfExtents.Bottom) { y = firstPlusHalfExtents.Bottom; } closestPoint.Y = y; //Check if second's center is inside fisrtPlusHalfExtents. If so, find closest edge for negative distance. if (closestPoint == second.CurrentFrameBoundingRect.Center) { Vector2 distanceToClosestEdge; //X Axis if (Math.Abs(closestPoint.X - firstPlusHalfExtents.Right) > Math.Abs(closestPoint.X - firstPlusHalfExtents.Left)) { distanceToClosestEdge.X = closestPoint.X - firstPlusHalfExtents.Left; } else { distanceToClosestEdge.X = closestPoint.X - firstPlusHalfExtents.Right; } //Y Axis if (Math.Abs(closestPoint.Y - firstPlusHalfExtents.Bottom) > Math.Abs(closestPoint.Y - firstPlusHalfExtents.Top)) { distanceToClosestEdge.Y = closestPoint.Y - firstPlusHalfExtents.Top; } else { distanceToClosestEdge.Y = closestPoint.Y - firstPlusHalfExtents.Bottom; } //Minimum Vector2 axisMinor = VectorUtilities.MinorAxis(distanceToClosestEdge); closestPoint = closestPoint + axisMinor; } //Calculate distance and Normal Vector2 distance = closestPoint - second.CurrentFrameBoundingRect.Center; contact.Distance = VectorUtilities.MaximumComponent(distance); if (contact.Distance == 0.0f)//right up against each other, will get invalid normal. { //On Top if (first.CurrentFrameBoundingRect.Bottom == second.CurrentFrameBoundingRect.Top) { contact.Normal = new Vector2(0.0f, -1.0f); if ((first.Side & CollisionSide.Bottom) == 0 || (second.Side & CollisionSide.Top) == 0) { return(false); } } //To the Right else if (first.CurrentFrameBoundingRect.Left == second.CurrentFrameBoundingRect.Right) { contact.Normal = new Vector2(1.0f, 0.0f); if ((first.Side & CollisionSide.Left) == 0 || (second.Side & CollisionSide.Right) == 0) { return(false); } } //On Bottom else if (first.CurrentFrameBoundingRect.Top == second.CurrentFrameBoundingRect.Bottom) { contact.Normal = new Vector2(0.0f, 1.0f); if ((first.Side & CollisionSide.Top) == 0 || (second.Side & CollisionSide.Bottom) == 0) { return(false); } } //To the Left else if (first.CurrentFrameBoundingRect.Right == second.CurrentFrameBoundingRect.Left) { contact.Normal = new Vector2(-1.0f, 0.0f); if ((first.Side & CollisionSide.Right) == 0 || (second.Side & CollisionSide.Left) == 0) { return(false); } } } else { contact.Normal = VectorUtilities.NormalizedMajorAxis(distance); } //On top if (contact.Normal.Y == -1.0f && ((first.Side & CollisionSide.Bottom) == 0 || (second.Side & CollisionSide.Top) == 0)) { return(false); } //To the Right else if (contact.Normal.X == 1.0f && ((first.Side & CollisionSide.Left) == 0 || (second.Side & CollisionSide.Right) == 0)) { return(false); } //On Bottom else if (contact.Normal.Y == 1.0f && ((first.Side & CollisionSide.Top) == 0 || (second.Side & CollisionSide.Bottom) == 0)) { return(false); } //To the Left else if (contact.Normal.X == -1.0f && ((first.Side & CollisionSide.Right) == 0 || (second.Side & CollisionSide.Left) == 0)) { return(false); } Boolean returnValue = false; //Collision Direction is X if (Math.Abs(contact.Normal.X) >= Math.Abs(contact.Normal.Y) && (((first.Velocity.X * gameTime.ElapsedGameTime.TotalMilliseconds) + contact.Distance) * contact.Normal.X < 0)) { returnValue = true; } //Collision Direction is Y if (Math.Abs(contact.Normal.X) <= Math.Abs(contact.Normal.Y) && (((first.Velocity.Y * gameTime.ElapsedGameTime.TotalMilliseconds) + contact.Distance) * contact.Normal.Y < 0)) { returnValue = true; } return(returnValue); }
private bool CheckCollisionCategories(ICutlassCollidable first, ICutlassCollidable second) { //No collision if their catgories don't overlap. return((first.Category & second.CategoryMask) != 0 && (second.Category & first.CategoryMask) != 0); }