private void OnPlayerBodyCollision(BodyCollisionInfo info) { if (info.Penetration.Y < 0) { Fall(); } }
private void OnBodyCollision(BodyCollisionInfo info) { var player = info.CollidingBody.UserData as Player; if (player != null) { player.Damage(new DamageInfo(this, 1, Direction2DHelper.GetDirection(-info.Penetration))); } }
private void bodyCollide(Body bA, Body bB, List<BodyCollisionInfo> infoList) { int bApmCount = bA.PointMassCount; int bBpmCount = bB.PointMassCount; AABB boxB = bB.getAABB(); // check all PointMasses on bodyA for collision against bodyB. if there is a collision, return detailed info. BodyCollisionInfo infoAway = new BodyCollisionInfo(); BodyCollisionInfo infoSame = new BodyCollisionInfo(); for (int i = 0; i < bApmCount; i++) { Vector2 pt = bA.getPointMass(i).Position; // early out - if this point is outside the bounding box for bodyB, skip it! if (!boxB.contains(ref pt)) continue; // early out - if this point is not inside bodyB, skip it! if (!bB.contains(ref pt)) continue; int prevPt = (i>0) ? i-1 : bApmCount-1; int nextPt = (i < bApmCount - 1) ? i + 1 : 0; Vector2 prev = bA.getPointMass(prevPt).Position; Vector2 next = bA.getPointMass(nextPt).Position; // now get the normal for this point. (NOT A UNIT VECTOR) Vector2 fromPrev = new Vector2(); fromPrev.X = pt.X - prev.X; fromPrev.Y = pt.Y - prev.Y; Vector2 toNext = new Vector2(); toNext.X = next.X - pt.X; toNext.Y = next.Y - pt.Y; Vector2 ptNorm = new Vector2(); ptNorm.X = fromPrev.X + toNext.X; ptNorm.Y = fromPrev.Y + toNext.Y; VectorTools.makePerpendicular(ref ptNorm); // this point is inside the other body. now check if the edges on either side intersect with and edges on bodyB. float closestAway = 100000.0f; float closestSame = 100000.0f; infoAway.Clear(); infoAway.bodyA = bA; infoAway.bodyApm = i; infoAway.bodyB = bB; infoSame.Clear(); infoSame.bodyA = bA; infoSame.bodyApm = i; infoSame.bodyB = bB; bool found = false; int b1 = 0; int b2 = 1; for (int j = 0; j < bBpmCount; j++) { Vector2 hitPt; Vector2 norm; float edgeD; b1 = j; if (j < bBpmCount - 1) b2 = j + 1; else b2 = 0; Vector2 pt1 = bB.getPointMass(b1).Position; Vector2 pt2 = bB.getPointMass(b2).Position; // quick test of distance to each point on the edge, if both are greater than current mins, we can skip! float distToA = ((pt1.X - pt.X) * (pt1.X - pt.X)) + ((pt1.Y - pt.Y) * (pt1.Y - pt.Y)); float distToB = ((pt2.X - pt.X) * (pt2.X - pt.X)) + ((pt2.Y - pt.Y) * (pt2.Y - pt.Y)); if ((distToA > closestAway) && (distToA > closestSame) && (distToB > closestAway) && (distToB > closestSame)) continue; // test against this edge. float dist = bB.getClosestPointOnEdgeSquared(pt, j, out hitPt, out norm, out edgeD); // only perform the check if the normal for this edge is facing AWAY from the point normal. float dot; //Vector2.Dot(ref ptNorm, ref edgeNorm, out dot); Vector2.Dot(ref ptNorm, ref norm, out dot); if (dot <= 0f) { if (dist < closestAway) { closestAway = dist; infoAway.bodyBpmA = b1; infoAway.bodyBpmB = b2; infoAway.edgeD = edgeD; infoAway.hitPt = hitPt; infoAway.normal = norm; infoAway.penetration = dist; found = true; } } else { if (dist < closestSame) { closestSame = dist; infoSame.bodyBpmA = b1; infoSame.bodyBpmB = b2; infoSame.edgeD = edgeD; infoSame.hitPt = hitPt; infoSame.normal = norm; infoSame.penetration = dist; } } } // we've checked all edges on BodyB. add the collision info to the stack. if ((found) && (closestAway > mPenetrationThreshold) && (closestSame < closestAway)) { infoSame.penetration = (float)Math.Sqrt(infoSame.penetration); infoList.Add(infoSame); } else { infoAway.penetration = (float)Math.Sqrt(infoAway.penetration); infoList.Add(infoAway); } } }
private void _handleCollisions() { // handle all collisions! for (int i = 0; i < mCollisionList.Count; i++) { BodyCollisionInfo info = mCollisionList[i]; PointMass A = info.bodyA.getPointMass(info.bodyApm); PointMass B1 = info.bodyB.getPointMass(info.bodyBpmA); PointMass B2 = info.bodyB.getPointMass(info.bodyBpmB); // velocity changes as a result of collision. Vector2 bVel = new Vector2(); bVel.x = (B1.Velocity.x + B2.Velocity.x) * 0.5f; bVel.y = (B1.Velocity.y + B2.Velocity.y) * 0.5f; Vector2 relVel = new Vector2(); relVel.x = A.Velocity.x - bVel.x; relVel.y = A.Velocity.y - bVel.y; float relDot; // Vector2.Dot(ref relVel, ref info.normal, out relDot); relDot = Vector2.Dot(relVel, info.normal); // collision filter! if (!mMaterialPairs[info.bodyA.Material, info.bodyB.Material].CollisionFilter(info.bodyA, info.bodyApm, info.bodyB, info.bodyBpmA, info.bodyBpmB, info.hitPt, relDot)) { continue; } if (info.penetration > mPenetrationThreshold) { //Console.WriteLine("penetration above Penetration Threshold!! penetration={0} threshold={1} difference={2}", // info.penetration, mPenetrationThreshold, info.penetration-mPenetrationThreshold); mPenetrationCount++; continue; } float b1inf = 1.0f - info.edgeD; float b2inf = info.edgeD; float b2MassSum = ((float.IsPositiveInfinity(B1.Mass)) || (float.IsPositiveInfinity(B2.Mass))) ? float.PositiveInfinity : (B1.Mass + B2.Mass); float massSum = A.Mass + b2MassSum; float Amove; float Bmove; if (float.IsPositiveInfinity(A.Mass)) { Amove = 0f; Bmove = (info.penetration) + 0.001f; } else if (float.IsPositiveInfinity(b2MassSum)) { Amove = (info.penetration) + 0.001f; Bmove = 0f; } else { Amove = (info.penetration * (b2MassSum / massSum)); Bmove = (info.penetration * (A.Mass / massSum)); } float B1move = Bmove * b1inf; float B2move = Bmove * b2inf; float AinvMass = (float.IsPositiveInfinity(A.Mass)) ? 0f : 1f / A.Mass; float BinvMass = (float.IsPositiveInfinity(b2MassSum)) ? 0f : 1f / b2MassSum; float jDenom = AinvMass + BinvMass; Vector2 numV = new Vector2(); float elas = 1f + mMaterialPairs[info.bodyA.Material, info.bodyB.Material].Elasticity; numV.x = relVel.x * elas; numV.y = relVel.y * elas; float jNumerator; // Vector2.Dot(ref numV, ref info.normal, out jNumerator); jNumerator = Vector2.Dot(numV, info.normal); jNumerator = -jNumerator; float j = jNumerator / jDenom; if (!float.IsPositiveInfinity(A.Mass)) { A.Position.x += info.normal.x * Amove; A.Position.y += info.normal.y * Amove; } if (!float.IsPositiveInfinity(B1.Mass)) { B1.Position.x -= info.normal.x * B1move; B1.Position.y -= info.normal.y * B1move; } if (!float.IsPositiveInfinity(B2.Mass)) { B2.Position.x -= info.normal.x * B2move; B2.Position.y -= info.normal.y * B2move; } Vector2 tangent = new Vector2(); VectorTools.getPerpendicular(ref info.normal, ref tangent); float friction = mMaterialPairs[info.bodyA.Material, info.bodyB.Material].Friction; float fNumerator; // Vector2.Dot(ref relVel, ref tangent, out fNumerator); fNumerator = Vector2.Dot(relVel, tangent); fNumerator *= friction; float f = fNumerator / jDenom; // adjust velocity if relative velocity is moving toward each other. if (relDot <= 0.0001f) { if (!float.IsPositiveInfinity(A.Mass)) { A.Velocity.x += (info.normal.x * (j / A.Mass)) - (tangent.x * (f / A.Mass)); A.Velocity.y += (info.normal.y * (j / A.Mass)) - (tangent.y * (f / A.Mass)); } if (!float.IsPositiveInfinity(b2MassSum)) { B1.Velocity.x -= (info.normal.x * (j / b2MassSum) * b1inf) - (tangent.x * (f / b2MassSum) * b1inf); B1.Velocity.y -= (info.normal.y * (j / b2MassSum) * b1inf) - (tangent.y * (f / b2MassSum) * b1inf); } if (!float.IsPositiveInfinity(b2MassSum)) { B2.Velocity.x -= (info.normal.x * (j / b2MassSum) * b2inf) - (tangent.x * (f / b2MassSum) * b2inf); B2.Velocity.y -= (info.normal.y * (j / b2MassSum) * b2inf) - (tangent.y * (f / b2MassSum) * b2inf); } } } mCollisionList.Clear(); }
private void bodyCollide(Body bA, Body bB, List <BodyCollisionInfo> infoList) { int bApmCount = bA.PointMassCount; int bBpmCount = bB.PointMassCount; AABB boxB = bB.getAABB(); // check all PointMasses on bodyA for collision against bodyB. if there is a collision, return detailed info. BodyCollisionInfo infoAway = new BodyCollisionInfo(); BodyCollisionInfo infoSame = new BodyCollisionInfo(); for (int i = 0; i < bApmCount; i++) { Vector2 pt = bA.getPointMass(i).Position; // early out - if this point is outside the bounding box for bodyB, skip it! if (!boxB.contains(ref pt)) { continue; } // early out - if this point is not inside bodyB, skip it! if (!bB.contains(ref pt)) { continue; } int prevPt = (i > 0) ? i - 1 : bApmCount - 1; int nextPt = (i < bApmCount - 1) ? i + 1 : 0; Vector2 prev = bA.getPointMass(prevPt).Position; Vector2 next = bA.getPointMass(nextPt).Position; // now get the normal for this point. (NOT A UNIT VECTOR) Vector2 fromPrev = new Vector2(); fromPrev.x = pt.x - prev.x; fromPrev.y = pt.y - prev.y; Vector2 toNext = new Vector2(); toNext.x = next.x - pt.x; toNext.y = next.y - pt.y; Vector2 ptNorm = new Vector2(); ptNorm.x = fromPrev.x + toNext.x; ptNorm.y = fromPrev.y + toNext.y; VectorTools.makePerpendicular(ref ptNorm); // this point is inside the other body. now check if the edges on either side intersect with and edges on bodyB. float closestAway = 100000.0f; float closestSame = 100000.0f; infoAway.Clear(); infoAway.bodyA = bA; infoAway.bodyApm = i; infoAway.bodyB = bB; infoSame.Clear(); infoSame.bodyA = bA; infoSame.bodyApm = i; infoSame.bodyB = bB; bool found = false; int b1 = 0; int b2 = 1; for (int j = 0; j < bBpmCount; j++) { Vector2 hitPt; Vector2 norm; float edgeD; b1 = j; if (j < bBpmCount - 1) { b2 = j + 1; } else { b2 = 0; } Vector2 pt1 = bB.getPointMass(b1).Position; Vector2 pt2 = bB.getPointMass(b2).Position; // quick test of distance to each point on the edge, if both are greater than current mins, we can skip! float distToA = ((pt1.x - pt.x) * (pt1.x - pt.x)) + ((pt1.y - pt.y) * (pt1.y - pt.y)); float distToB = ((pt2.x - pt.x) * (pt2.x - pt.x)) + ((pt2.y - pt.y) * (pt2.y - pt.y)); if ((distToA > closestAway) && (distToA > closestSame) && (distToB > closestAway) && (distToB > closestSame)) { continue; } // test against this edge. float dist = bB.getClosestPointOnEdgeSquared(pt, j, out hitPt, out norm, out edgeD); // only perform the check if the normal for this edge is facing AWAY from the point normal. float dot; //Vector2.Dot(ref ptNorm, ref edgeNorm, out dot); // Vector2.Dot(ref ptNorm, ref norm, out dot); dot = Vector2.Dot(ptNorm, norm); if (dot <= 0f) { if (dist < closestAway) { closestAway = dist; infoAway.bodyBpmA = b1; infoAway.bodyBpmB = b2; infoAway.edgeD = edgeD; infoAway.hitPt = hitPt; infoAway.normal = norm; infoAway.penetration = dist; found = true; } } else { if (dist < closestSame) { closestSame = dist; infoSame.bodyBpmA = b1; infoSame.bodyBpmB = b2; infoSame.edgeD = edgeD; infoSame.hitPt = hitPt; infoSame.normal = norm; infoSame.penetration = dist; } } } // we've checked all edges on BodyB. add the collision info to the stack. if ((found) && (closestAway > mPenetrationThreshold) && (closestSame < closestAway)) { infoSame.penetration = (float)Math.Sqrt(infoSame.penetration); infoList.Add(infoSame); } else { infoAway.penetration = (float)Math.Sqrt(infoAway.penetration); infoList.Add(infoAway); } } }