/// <summary> /// Add a new CollisionUnit to the CollisionChief. /// </summary> /// <param name="unit">The CollisionUnit to add to the CollisionChief.</param> public void AddCollisionUnit(CollisionUnit unit) { Vector2 circleCenter; int circleRadius; Vector2 lineStart; Vector2 lineEnd; Point topLeftCell; Point bottomRightCell; if (unit.GetCollisionType() == CollisionUnit.CollisionType.COLLISION_CIRCLE) { circleCenter = unit.GetCircleCenter(); circleRadius = unit.GetCircleRadius(); // put in appropriate cells topLeftCell = CalculateCircleTopLeftCell(circleCenter, circleRadius); bottomRightCell = CalculateCircleBottomRightCell(circleCenter, circleRadius); for (int i = topLeftCell.X; i <= bottomRightCell.X; i++) { for (int j = topLeftCell.Y; j <= bottomRightCell.Y; j++) { mCollisionGrid.Cells[i,j].AddCollisionUnit(unit); } } } else if (unit.GetCollisionType() == CollisionUnit.CollisionType.COLLISION_LINE) { lineStart = unit.GetLineStart(); lineEnd = unit.GetLineEnd(); // TODO: put in appropriate cells } else if (unit.GetCollisionType() == CollisionUnit.CollisionType.COLLISION_BOX) { topLeftCell = CalculateCellPosition(unit.GetUpperLeft()); bottomRightCell = CalculateCellPosition(unit.GetLowerRight()); for (int i = topLeftCell.X; i <= bottomRightCell.X; i++) { for (int j = topLeftCell.Y; j <= bottomRightCell.Y; j++) { mCollisionGrid.Cells[i, j].AddCollisionUnit(unit); } } } }
/// <summary> /// Get the point of collision with the <paramref name="other"/> CollisionUnit. This does not do any real collision checks so the returned value might not be the actual collision point. /// </summary> /// <param name="other">The CollisionUnit to get the collision point from.</param> /// <returns>A Vector2 representing the collision point.</returns> public Vector2 GetCollisionPoint(CollisionUnit other) { Vector2 otherCircleCenter; int otherCircleRadius; Vector2 boxUpperLeft; Vector2 boxLowerRight; float dX; float dY; float ratio; Vector2 collisionPoint = new Vector2(-1, -1); if (mCollisionType == CollisionType.COLLISION_CIRCLE && other.GetCollisionType() == CollisionType.COLLISION_CIRCLE) { otherCircleCenter = other.GetCircleCenter(); otherCircleRadius = other.GetCircleRadius(); dX = otherCircleCenter.X - mCircleCenter.X; dY = otherCircleCenter.Y - mCircleCenter.Y; ratio = ((float)mCircleRadius) / (mCircleRadius + otherCircleRadius); dX *= ratio; dY *= ratio; collisionPoint = new Vector2(mCircleCenter.X + dX, mCircleCenter.Y + dY); } else if ((mCollisionType == CollisionType.COLLISION_CIRCLE && other.GetCollisionType() == CollisionType.COLLISION_BOX) || (mCollisionType == CollisionType.COLLISION_BOX && other.GetCollisionType() == CollisionType.COLLISION_CIRCLE)) { if (mCollisionType == CollisionType.COLLISION_CIRCLE) { otherCircleCenter = mCircleCenter; otherCircleRadius = mCircleRadius; boxUpperLeft = other.GetUpperLeft(); boxLowerRight = other.GetLowerRight(); } else { otherCircleCenter = other.GetCircleCenter(); otherCircleRadius = other.GetCircleRadius(); boxUpperLeft = mPoint1; boxLowerRight = mPoint2; } if (otherCircleCenter.X > boxLowerRight.X) { if (otherCircleCenter.Y > boxLowerRight.Y) { collisionPoint = boxLowerRight; } else if (otherCircleCenter.Y < boxUpperLeft.Y) { collisionPoint = new Vector2(boxLowerRight.X, boxUpperLeft.Y); } else { collisionPoint = new Vector2(otherCircleCenter.X - otherCircleRadius, otherCircleCenter.Y); } } else if (otherCircleCenter.X < boxUpperLeft.X) { if (otherCircleCenter.Y > boxLowerRight.Y) { collisionPoint = new Vector2(boxUpperLeft.X, boxLowerRight.Y); } else if (otherCircleCenter.Y < boxUpperLeft.Y) { collisionPoint = boxUpperLeft; } else { collisionPoint = new Vector2(otherCircleCenter.X + otherCircleRadius, otherCircleCenter.Y); } } else if (otherCircleCenter.Y > boxLowerRight.Y) { collisionPoint = new Vector2(otherCircleCenter.X, otherCircleCenter.Y - otherCircleRadius); } else if (otherCircleCenter.Y < boxUpperLeft.Y) { collisionPoint = new Vector2(otherCircleCenter.X, otherCircleCenter.Y + otherCircleRadius); } else { // inside collisionPoint = otherCircleCenter; } } return collisionPoint; }
/// <summary> /// Determine if this CollisionUnit collides with another. /// </summary> /// <param name="other">The CollisionUnit to check for collision.</param> /// <returns><code>true</code> if this collision unit collides with the <paramref name="other"/> CollisionUnit, <code>false</code> if otherwise.</returns> public bool CollidesWith(CollisionUnit other) { Vector2 otherCircleCenter; int otherCircleRadius; float dX; float dY; float dist; Vector2 otherUpperLeft; Vector2 otherLowerRight; mCheckedUnits.Add(other); if (mCollisionType == CollisionType.COLLISION_CIRCLE && other.GetCollisionType() == CollisionType.COLLISION_CIRCLE) { otherCircleCenter = other.GetCircleCenter(); otherCircleRadius = other.GetCircleRadius(); dX = mCircleCenter.X - otherCircleCenter.X; dY = mCircleCenter.Y - otherCircleCenter.Y; dist = (dX * dX) + (dY * dY); // (radius + radius)^2 instead of more expensive squareRoot(dist) return (dist <= (mCircleRadius + otherCircleRadius) * (mCircleRadius + otherCircleRadius)); } else if (mCollisionType == CollisionType.COLLISION_CIRCLE && other.GetCollisionType() == CollisionType.COLLISION_BOX) { return CircleBoxCollision(mCircleCenter, mCircleRadius, other.GetUpperLeft(), other.GetLowerRight()); } else if (mCollisionType == CollisionType.COLLISION_BOX && other.GetCollisionType() == CollisionType.COLLISION_CIRCLE) { return CircleBoxCollision(other.GetCircleCenter(), other.GetCircleRadius(), mPoint1, mPoint2); } else if (mCollisionType == CollisionType.COLLISION_BOX && other.GetCollisionType() == CollisionType.COLLISION_BOX) { otherUpperLeft = other.GetUpperLeft(); otherLowerRight = other.GetLowerRight(); return (mPoint1.X <= otherLowerRight.X && mPoint1.Y <= otherLowerRight.Y && otherUpperLeft.X <= mPoint2.X && otherUpperLeft.Y <= mPoint2.Y); } // TODO: other collision types - line, pixel return false; }
/// <summary> /// Add a bounce between two CollisionUnits based on the velocity. /// </summary> /// <param name="unit">The main collision unit involved in the "bounce".</param> /// <param name="other">The secondary collision unit involved in the "bounce".</param> public void AddBounce(CollisionUnit unit, CollisionUnit other) { Vector2 unitCircleCenter; Vector2 otherCircleCenter; int unitRadius; int otherRadius; Vector2 oldVelocity; Vector2 norm; Vector2 unitNorm; Vector2 unitTan = new Vector2(); Vector2 oldVelocityOther = new Vector2(0, 0); float velocityNorm; float velocityTan; float velocityNormOther; float velocityTanOther; float newVelocityScalar; float newVelocityScalarOther; Vector2 newVelocityNorm; Vector2 newVelocityNormOther; Vector2 newVelocityTan; Vector2 newVelocityTanOther; Vector2 newVelocity; Vector2 newVelocityOther; float mass = 1; float massOther = 10000; Vector2 velocityDiff; if (unit.GetCollisionType() == CollisionUnit.CollisionType.COLLISION_CIRCLE && other.GetCollisionType() == CollisionUnit.CollisionType.COLLISION_CIRCLE) { unitCircleCenter = unit.GetCircleCenter(); unitRadius = unit.GetCircleRadius(); otherCircleCenter = other.GetCircleCenter(); otherRadius = other.GetCircleRadius(); oldVelocity = mVelocity; //////////////// norm = otherCircleCenter - unitCircleCenter; unitNorm = norm / ((float)Math.Sqrt(norm.X * norm.X + norm.Y * norm.Y)); unitTan.X = unitNorm.Y * -1; unitTan.Y = unitNorm.X; velocityNorm = Vector2.Dot(unitNorm, oldVelocity); velocityTan = Vector2.Dot(unitTan, oldVelocity); velocityNormOther = Vector2.Dot(unitNorm, oldVelocityOther); velocityTanOther = Vector2.Dot(unitTan, oldVelocityOther); newVelocityScalar = (velocityNorm * (mass - massOther) + 2 * massOther * velocityNormOther) / (mass + massOther); newVelocityScalarOther = (velocityNormOther * (massOther - mass) + 2 * mass * velocityNorm) / (mass + massOther); newVelocityNorm = newVelocityScalar * unitNorm; newVelocityNormOther = newVelocityScalarOther * unitNorm; newVelocityTan = velocityTan * unitTan; newVelocityTanOther = velocityTanOther * unitTan; newVelocity = newVelocityNorm + newVelocityTan; newVelocityOther = newVelocityNormOther + newVelocityTanOther; newVelocity *= 0.99f; velocityDiff = newVelocity - mVelocity; mVelocity = newVelocity; // some fudge //if (velocityDiff.X > 1 || velocityDiff.X < -1 || velocityDiff.Y > 1 || velocityDiff.Y < -1) //{ mOwner.Translate(0.04f * (unitCircleCenter.X - otherCircleCenter.X), 0.04f * (unitCircleCenter.Y - otherCircleCenter.Y)); //} } else { newVelocity = mVelocity * -0.8f; velocityDiff = newVelocity - mVelocity; mVelocity = newVelocity; unitCircleCenter = unit.GetCircleCenter(); otherCircleCenter = unit.GetCollisionPoint(other); //if (velocityDiff.X > 1 || velocityDiff.X < -1 || velocityDiff.Y > 1 || velocityDiff.Y < -1) //{ mOwner.Translate(0.04f * (unitCircleCenter.X - otherCircleCenter.X), 0.04f * (unitCircleCenter.Y - otherCircleCenter.Y)); //} } }
/// <summary> /// Translate a CollisionUnit by the given amount. /// </summary> /// <param name="unit">The CollisionUnit to translate.</param> /// <param name="translation">The delta translation to perform on the CollisionUnit.</param> public void TranslateCollisionUnit(CollisionUnit unit, Vector2 translation) { int circleRadius; Vector2 oldCircleCenter; Vector2 oldLineStart; Vector2 oldLineEnd; Point oldTopLeftCell; Point oldBottomRightCell; Vector2 newCircleCenter; Point newTopLeftCell; Point newBottomRightCell; if (unit.GetCollisionType() == CollisionUnit.CollisionType.COLLISION_CIRCLE || unit.GetCollisionType() == CollisionUnit.CollisionType.COLLISION_BOX) { if (unit.GetCollisionType() == CollisionUnit.CollisionType.COLLISION_CIRCLE) { circleRadius = unit.GetCircleRadius(); oldCircleCenter = unit.GetCircleCenter(); newCircleCenter = oldCircleCenter + translation; // calculate containing cells oldTopLeftCell = CalculateCircleTopLeftCell(oldCircleCenter, circleRadius); oldBottomRightCell = CalculateCircleBottomRightCell(oldCircleCenter, circleRadius); newTopLeftCell = CalculateCircleTopLeftCell(newCircleCenter, circleRadius); newBottomRightCell = CalculateCircleBottomRightCell(newCircleCenter, circleRadius); } else { oldTopLeftCell = CalculateCellPosition(unit.GetUpperLeft()); oldBottomRightCell = CalculateCellPosition(unit.GetLowerRight()); newTopLeftCell = CalculateCellPosition(unit.GetUpperLeft() + translation); newBottomRightCell = CalculateCellPosition(unit.GetLowerRight() + translation); } // remove from cells no longer within and add to new cells that unit is within if (translation.X > 0) { // remove for (int i = oldTopLeftCell.X; i < newTopLeftCell.X && i <= oldBottomRightCell.X; i++) { for (int j = oldTopLeftCell.Y; j <= oldBottomRightCell.Y; j++) { mCollisionGrid.Cells[i, j].RemoveCollisionUnit(unit); } } // add int minStart = oldBottomRightCell.X + 1; if (minStart < newTopLeftCell.X) { minStart = newTopLeftCell.X; } for (int i = minStart; i <= newBottomRightCell.X; i++) { for (int j = newTopLeftCell.Y; j <= newBottomRightCell.Y; j++) { mCollisionGrid.Cells[i, j].AddCollisionUnit(unit); } } } else if (translation.X < 0) { // remove int minStart = newBottomRightCell.X + 1; if (minStart < oldTopLeftCell.X) { minStart = oldTopLeftCell.X; } for (int i = minStart; i <= oldBottomRightCell.X; i++) { for (int j = oldTopLeftCell.Y; j <= oldBottomRightCell.Y; j++) { mCollisionGrid.Cells[i, j].RemoveCollisionUnit(unit); } } // add for (int i = newTopLeftCell.X; i < oldTopLeftCell.X && i <= newBottomRightCell.X; i++) { for (int j = newTopLeftCell.Y; j <= newBottomRightCell.Y; j++) { mCollisionGrid.Cells[i, j].AddCollisionUnit(unit); } } } if (translation.Y > 0) { // remove for (int i = oldTopLeftCell.Y; i < newTopLeftCell.Y && i <= oldBottomRightCell.Y; i++) { for (int j = oldTopLeftCell.X; j <= oldBottomRightCell.X; j++) { mCollisionGrid.Cells[j, i].RemoveCollisionUnit(unit); } } // add int minStart = oldBottomRightCell.Y + 1; if (minStart < newTopLeftCell.Y) { minStart = newTopLeftCell.Y; } for (int i = minStart; i <= newBottomRightCell.Y; i++) { // avoid double adds if (translation.X > 0) { for (int j = newTopLeftCell.X; j <= oldBottomRightCell.X; j++) { mCollisionGrid.Cells[j, i].AddCollisionUnit(unit); } } else { for (int j = oldTopLeftCell.X; j <= newBottomRightCell.X; j++) { mCollisionGrid.Cells[j, i].AddCollisionUnit(unit); } } } } else if (translation.Y < 0) { // remove int minStart = newBottomRightCell.Y + 1; if (minStart < oldTopLeftCell.Y) { minStart = oldTopLeftCell.Y; } for (int i = minStart; i <= oldBottomRightCell.Y; i++) { for (int j = oldTopLeftCell.X; j <= oldBottomRightCell.X; j++) { mCollisionGrid.Cells[j, i].RemoveCollisionUnit(unit); } } // add for (int i = newTopLeftCell.Y; i < oldTopLeftCell.Y && i <= newBottomRightCell.Y; i++) { // avoid double adds if (translation.X > 0) { for (int j = newTopLeftCell.X; j <= oldBottomRightCell.X; j++) { mCollisionGrid.Cells[j, i].AddCollisionUnit(unit); } } else { for (int j = oldTopLeftCell.X; j <= newBottomRightCell.X; j++) { mCollisionGrid.Cells[j, i].AddCollisionUnit(unit); } } } } } else if (unit.GetCollisionType() == CollisionUnit.CollisionType.COLLISION_LINE) { oldLineStart = unit.GetLineStart(); oldLineEnd = unit.GetLineEnd(); // TODO: put in appropriate cells } }
/// <summary> /// Scale a CollisionUnit by the given amount. /// </summary> /// <param name="unit">The CollisionUnit to scale.</param> /// <param name="scale">The delta scale to perform on the CollisionUnit.</param> public void ScaleCollisionUnit(CollisionUnit unit, Vector2 scale) { int oldCircleRadius; int newCircleRadius; Vector2 circleCenter; Vector2 oldLineStart; Vector2 oldLineEnd; Point oldTopLeftCell; Point oldBottomRightCell; Point newTopLeftCell; Point newBottomRightCell; bool scaleFromCenter = false; if (unit.GetCollisionType() == CollisionUnit.CollisionType.COLLISION_CIRCLE || unit.GetCollisionType() == CollisionUnit.CollisionType.COLLISION_BOX) { if (unit.GetCollisionType() == CollisionUnit.CollisionType.COLLISION_CIRCLE) { oldCircleRadius = unit.GetCircleRadius(); //newCircleRadius = (int)(unit.GetCircleRadius() * ((scale.X + scale.Y) / 2)); //newCircleRadius = (int)(unit.GetCircleRadius() + unit.GetCircleRadius() * ((scale.X + scale.Y) / 2)); newCircleRadius = (int)(unit.GetCircleRadius() + ((scale.X + scale.Y) / 2)); circleCenter = unit.GetCircleCenter(); // calculate containing cells oldTopLeftCell = CalculateCircleTopLeftCell(circleCenter, oldCircleRadius); oldBottomRightCell = CalculateCircleBottomRightCell(circleCenter, oldCircleRadius); newTopLeftCell = CalculateCircleTopLeftCell(circleCenter, newCircleRadius); newBottomRightCell = CalculateCircleBottomRightCell(circleCenter, newCircleRadius); } else { Vector2 point1 = unit.GetUpperLeft(); Vector2 point2 = unit.GetLowerRight(); int oldWidth = unit.GetWidth(); int oldHeight = unit.GetHeight(); //int xChange = (int)(((oldWidth * scale.X) - oldWidth) / 2); //int yChange = (int)(((oldHeight * scale.Y) - oldHeight) / 2); //int xChange = (int)(oldWidth * scale.X / 2); //int yChange = (int)(oldHeight * scale.Y / 2); int xChange = (int)scale.X; int yChange = (int)scale.Y; // scale from center if (scaleFromCenter) { point1.X -= xChange; point1.Y -= yChange; point2.X += xChange; point2.Y += yChange; } else { // scale with top left stationary point2.X += xChange * 2; point2.Y += yChange * 2; } oldTopLeftCell = CalculateCellPosition(unit.GetUpperLeft()); oldBottomRightCell = CalculateCellPosition(unit.GetLowerRight()); newTopLeftCell = CalculateCellPosition(point1); newBottomRightCell = CalculateCellPosition(point2); } //if (scale.X < 1) if (scale.X < 0) { for (int j = oldTopLeftCell.Y; j <= oldBottomRightCell.Y; j++) { // remove left for (int i = oldTopLeftCell.X; i < newTopLeftCell.X; i++) { mCollisionGrid.Cells[i, j].RemoveCollisionUnit(unit); } // remove right for (int i = newBottomRightCell.X + 1; i <= oldBottomRightCell.X; i++) { mCollisionGrid.Cells[i, j].RemoveCollisionUnit(unit); } } //if (scale.Y < 1) if (scale.Y < 0) { for (int i = newTopLeftCell.X; i <= newBottomRightCell.X; i++) { // remove top for (int j = oldTopLeftCell.Y; j < newTopLeftCell.Y; j++) { mCollisionGrid.Cells[i, j].RemoveCollisionUnit(unit); } // remove bottom for (int j = newBottomRightCell.Y + 1; j <= oldBottomRightCell.Y; j++) { mCollisionGrid.Cells[i, j].RemoveCollisionUnit(unit); } } } //else if (scale.Y > 1) else if (scale.Y > 0) { for (int i = newTopLeftCell.X; i <= newBottomRightCell.X; i++) { // add top for (int j = newTopLeftCell.Y; j < oldTopLeftCell.Y; j++) { mCollisionGrid.Cells[i, j].AddCollisionUnit(unit); } // add bottom for (int j = oldBottomRightCell.Y + 1; j <= newBottomRightCell.Y; j++) { mCollisionGrid.Cells[i, j].AddCollisionUnit(unit); } } } } //else if (scale.X > 1) else if (scale.X > 0) { for (int j = newTopLeftCell.Y; j <= newBottomRightCell.Y; j++) { // add left for (int i = newTopLeftCell.X; i < oldTopLeftCell.X; i++) { mCollisionGrid.Cells[i, j].AddCollisionUnit(unit); } // add right for (int i = oldBottomRightCell.X + 1; i <= newBottomRightCell.X; i++) { mCollisionGrid.Cells[i, j].AddCollisionUnit(unit); } } //if (scale.Y < 1) if (scale.Y < 0) { for (int i = oldTopLeftCell.X; i <= oldBottomRightCell.X; i++) { // remove top for (int j = oldTopLeftCell.Y; j < newTopLeftCell.Y; j++) { mCollisionGrid.Cells[i, j].RemoveCollisionUnit(unit); } // remove bottom for (int j = newBottomRightCell.Y + 1; j <= oldBottomRightCell.Y; j++) { mCollisionGrid.Cells[i, j].RemoveCollisionUnit(unit); } } } //else if (scale.Y > 1) else if (scale.Y > 0) { for (int i = oldTopLeftCell.X; i <= oldBottomRightCell.X; i++) { // add top for (int j = newTopLeftCell.Y; j < oldTopLeftCell.Y; j++) { mCollisionGrid.Cells[i, j].AddCollisionUnit(unit); } // add bottom for (int j = oldBottomRightCell.Y + 1; j <= newBottomRightCell.Y; j++) { mCollisionGrid.Cells[i, j].AddCollisionUnit(unit); } } } } } else if (unit.GetCollisionType() == CollisionUnit.CollisionType.COLLISION_LINE) { oldLineStart = unit.GetLineStart(); oldLineEnd = unit.GetLineEnd(); // TODO: put in appropriate cells } }