I am trying to make the basic collision dynamics work

I simplified things to cubes / single cube colliding with a rectangle with infinite mass and the following code:

The problem is that the boxes tend to rotate too much and get stuck together, and if binary search is turned on, just click and rotate a lot.

Thanks for any help.

/// <summary> /// Projects an abstract 1D line "perpendicular" to the axis, /// stretching across the width of the model, /// measured from that axis. /// </summary> /// <param name="Axis"></param> /// <param name="Min"></param> /// <param name="Max"></param> protected virtual void ProjectToAxis(Vector2 Axis, IMotionData motionData, out double Min, out double Max) { Double DotP = Axis.Dot(motionData.PositionGS + (this.Vertices[0].Position * this.Model.Scale).Rotate(motionData.RotationGS)); Min = Max = DotP; for (int t = 1; t < this.Vertices.Count(); ++t) { DotP = Axis.Dot(motionData.PositionGS + (this.Vertices[t].Position * this.Model.Scale).Rotate(motionData.RotationGS)); Min = Math.Min(DotP, Min); Max = Math.Max(DotP, Max); } } /// <summary> /// Projects two imaginary lines even with each edge, /// equal to the width of each object while looking at /// that edge, then checks to see if they intersect. /// </summary> /// <param name="B1"></param> /// <param name="B2"></param> /// <returns></returns> public static bool DetectCollision(Body B1, Body B2, Double elapsedSeconds) { CollisionData collisionInfo = new CollisionData(); double lowestDistance = double.MaxValue; double distance; Vector2 normalB1ToB2 = (B2.MotionHandler.PositionGS - B1.MotionHandler.PositionGS).Normalized; foreach (Edge edge in B1.Edges) { if (edge.Normal.RelativePosition.Dot(normalB1ToB2) >= 0.0) { double minA, minB, maxA, maxB; B1.ProjectToAxis(edge.Normal.RelativePosition, B1.MotionHandler.MotionDataGet, out minA, out maxA); B2.ProjectToAxis(edge.Normal.RelativePosition, B2.MotionHandler.MotionDataGet, out minB, out maxB); if (minA < minB) distance = minB - maxA; else distance = minA - maxB; if (distance > 0.0f) return false; else if (Math.Abs(distance) < lowestDistance) { lowestDistance = Math.Abs(distance); collisionInfo.Normal = edge.Normal.RelativePosition; collisionInfo.Edge = edge; } } } Vector2 normalB2ToB1 = -normalB1ToB2; foreach (Edge edge in B2.Edges) { if (edge.Normal.RelativePosition.Dot(normalB2ToB1) >= 0.0) { double minA, minB, maxA, maxB; B1.ProjectToAxis(edge.Normal.RelativePosition, B1.MotionHandler.MotionDataGet, out minA, out maxA); B2.ProjectToAxis(edge.Normal.RelativePosition, B2.MotionHandler.MotionDataGet, out minB, out maxB); if (minA < minB) distance = minB - maxA; else distance = minA - maxB; if (distance > 0.0f) return false; else if (Math.Abs(distance) < lowestDistance) { lowestDistance = Math.Abs(distance); collisionInfo.Normal = edge.Normal.RelativePosition; collisionInfo.Edge = edge; } } } collisionInfo.Depth = lowestDistance; /* Double lowHighSeconds = elapsedSeconds; Double highLowSeconds = 0.0; Double seconds; IMotionData md1; IMotionData md2; bool collision; do { md1 = B1.MotionHandler.MotionDataLastGet.Copy; md2 = B2.MotionHandler.MotionDataLastGet.Copy; collision = true; lowestDistance = Double.MaxValue; seconds = MathExtensions.MathExt.Lerp(highLowSeconds, lowHighSeconds, 0.5); B1.MotionHandler.Simulate(seconds, ref md1); B2.MotionHandler.Simulate(seconds, ref md2); normalB1ToB2 = (md2.PositionGS - md1.PositionGS).Normalized; foreach (Edge edge in B1.Edges) { if ((edge.Normal.Position * B1.Model.Scale).Rotate(md1.RotationGS).Dot(normalB1ToB2) >= 0.0) { double minA, minB, maxA, maxB; B1.ProjectToAxis((edge.Normal.Position * B1.Model.Scale).Rotate(md1.RotationGS), md1, out minA, out maxA); B2.ProjectToAxis((edge.Normal.Position * B1.Model.Scale).Rotate(md1.RotationGS), md2, out minB, out maxB); if (minA < minB) distance = minB - maxA; else distance = minA - maxB; if (distance > 0.0f) collision = false; else if (Math.Abs(distance) < lowestDistance) { lowestDistance = Math.Abs(distance); collisionInfo.Normal = (edge.Normal.Position * B1.Model.Scale).Rotate(md1.RotationGS); collisionInfo.Edge = edge; } } } normalB2ToB1 = -normalB1ToB2; foreach (Edge edge in B2.Edges) { if ((edge.Normal.Position * B2.Model.Scale).Rotate(md2.RotationGS).Dot(normalB2ToB1) >= 0.0) { double minA, minB, maxA, maxB; B2.ProjectToAxis((edge.Normal.Position * B2.Model.Scale).Rotate(md2.RotationGS), md2, out minA, out maxA); B1.ProjectToAxis((edge.Normal.Position * B2.Model.Scale).Rotate(md2.RotationGS), md1, out minB, out maxB); if (minA < minB) distance = minB - maxA; else distance = minA - maxB; if (distance > 0.0f) collision = false; else if (Math.Abs(distance) < lowestDistance) { lowestDistance = Math.Abs(distance); collisionInfo.Normal = (edge.Normal.Position * B2.Model.Scale).Rotate(md2.RotationGS); collisionInfo.Edge = edge; } } } collisionInfo.Depth = lowestDistance; if (!collision) { lowHighSeconds = seconds; } else { highLowSeconds = seconds; } } while (Math.Abs(highLowSeconds - lowHighSeconds) > 0.0001); B1.MotionHandler.MotionDataSet = md1; B2.MotionHandler.MotionDataSet = md2; */ // bool flip = false; if (collisionInfo.Edge.Parent != B2.Model) { Body temp = B1; B1 = B2; B2 = temp; } //This is needed to make sure that the collision normal is pointing at B1 int Sign = Math.Sign( collisionInfo.Normal.Dot( B1.MotionHandler.MotionDataGet.PositionGS + (B1.Center * B1.Model.Scale).Rotate(B1.MotionHandler.MotionDataGet.RotationGS) - B2.MotionHandler.MotionDataGet.PositionGS + (B2.Center * B2.Model.Scale).Rotate(B2.MotionHandler.MotionDataGet.RotationGS) ) ); //Remember that the line equation is N*( R - R0 ). We choose B2->Center //as R0; the normal N is given by the collision normal if (Sign != 1) collisionInfo.Normal = -collisionInfo.Normal; //Revert the collision normal if it points away from B1 double SmallestD = double.MaxValue; //Initialize the smallest distance to a high value //Measure the distance of the vertex from the line using the line equation for (int t = 0; t < B1.Vertices.Count(); ++t) { double Distance = collisionInfo.Normal.Dot(B1.Vertices[t].WorldPosition - B2.Center); // If the measured distance is smaller than the smallest distance reported // so far, set the smallest distance and the collision vertex if (Distance < SmallestD) { SmallestD = Distance; collisionInfo.Vertex = B1.Vertices[t]; } } if ((Body.CollisionType & CollisionType.Velocity) > 0) { Vector2 vab1 = B1.MotionHandler.VelocityGS - B2.MotionHandler.VelocityGS; Vector2 rap = (B1.MotionHandler.PositionGS - collisionInfo.Normal); Vector2 rbp = (B2.MotionHandler.PositionGS - collisionInfo.Normal); Double rap2 = (rap.Cross(collisionInfo.Normal)); Double rbp2 = (rbp.Cross(collisionInfo.Normal)); Vector2 one = (collisionInfo.Vertex.WorldPosition - B1.MotionHandler.PositionGS).GetPerpendicular; Vector2 two = (collisionInfo.Vertex.WorldPosition - B2.MotionHandler.PositionGS).GetPerpendicular; Double j = (-(1 + 0.0) * vab1.Dot(collisionInfo.Normal)) / ((collisionInfo.Normal.Dot(collisionInfo.Normal) * (B1.MotionHandler.InverseMassGS + B2.MotionHandler.InverseMassGS)) + (one.Dot(one) * B1.MotionHandler.InverseInertiaGS) + (two.Dot(two) * B2.MotionHandler.InverseInertiaGS)); B1.MotionHandler.AddImpulse = new Force( collisionInfo.Normal, j /* , one */ ); B2.MotionHandler.AddImpulse = new Force( collisionInfo.Normal, -(j) /* , two */ ); NewtonianMotionData data1 = (NewtonianMotionData)B1.MotionHandler.MotionDataGet; NewtonianMotionData data2 = (NewtonianMotionData)B2.MotionHandler.MotionDataGet; data1.AngularVelocity += (one.Dot(j * collisionInfo.Normal)) * data1.inverseInertia; data2.AngularVelocity += (two.Dot(-j * collisionInfo.Normal)) * data2.inverseInertia; B1.MotionHandler.MotionDataSet = data1; B2.MotionHandler.MotionDataSet = data2; } return true; } 
+4
source share
2 answers

You have two problems.

1) Something is wrong with the code. You need to fix it.

2) You do not know how to understand what “something” is.

The solution to the first problem is related to the solution to the second problem. You need to learn how to debug the program you just wrote.

You already tested it and got a result that you called meaningless. This is a good first step. Now break it even further. Choose a simple problem in this area that you can solve with a pencil and paper; do this, and then see how your algorithm solves the same problem in the debugger by checking every step along the way. Listen to quiet doubts. . When there is something that looks slightly unexpected or unexpected, stop what you are doing and research the problem until you understand whether things are working correctly or not. In the end, you will find a step where everything is not as it should be, and that where the error is.

Yes, it is tiring. When you find a mistake and fix it, stop and think about what made you write the error in the first place, and find out how not to write that error again.

UPDATE:

Re: your last comments.

Apology accepted. Now calm down. You will never find this mistake if you handle it. Your brain will not allow you. People in a panicky, processed state lose their ability to reason. That is why fire opens doors outward; people fleeing a burning building literally will not stop thinking: "I push this door and I don’t open it, maybe I should try to pull it." They just get tense. I suspect you click more.

Debugging requires rationality and careful attention to small details. If you all figured out this problem, then it will exit the window, and it will only get worse. Take it from the one who was there. We were all there. This is a very unpleasant thing that caused an error in your own program, which you then cannot find.

The reason no one helps you is because ... well, let me list a set of prerequisites that must be met for me to help you with more than vague platitudes and suggestions on how to focus my efforts debugging:

1) I need to learn something about simulation of 3d physics. I had a pretty decent understanding of the differential equations of simple Newtonian mechanics in 1992, but I have not used it since. And the spring damped driving equation is quite different from the solid collision equations. If I spend a couple of weeks looking through my notes, I can return the math, but this is unrealistic. You need someone who is deeply familiar with 3D collision modeling.

2) I must be able to read and understand your code, hundreds of lines of code written by someone other than me to solve a problem that I am not familiar with. Even worse, a hundred lines of this code are commented out. What for? Is this relevant? Is there a mistake there? Moreover, I need to be able to read and understand the code without running it in the debugger. Hell, I can't even compile this code. It depends on libraries that I don't have.

And even worse, one of these libraries may contain an error. As far as I know, the error is a typo in some code that calculates a normal place somewhere that you did not show us. The code shown may be perfect.

3) I need to have free time to work on someone’s serious problem; the problem is that the person who wrote the code and understands physics is not moving forward.

All these are requirements; if any of them is missing, the reader cannot help you effectively. You ask people you don’t know to help you find a black cat in a dark warehouse at midnight without a flashlight - a cat that might not even be there. It is not surprising that you get few participants. Of the 74 users who read your question, how many of them meet all three requirements? I have not met any of them.

If you need help on this site, submit an easier problem. Clarify the problem before the problem, which requires less specialized knowledge in physics and modeling algorithms and has only the appropriate code, preferably code that can be compiled and run.

+7
source

This may not be good news, but I have a few things to add to Eric Lippert's analysis and suggestion.

Your comments are misleading. I know that if you are not familiar with mathematics and physics, it is difficult to be precise, but look at "ProjectToAxis":

  /// Projects an abstract 1D line "perpendicular" to the axis,  
 /// stretching across the width of the model, 
 /// measured from that axis. 

Excuse me if that sounds harsh but

  • "abstract 1st line" is kind of meaningless, it should just say "line".
  • This is not a line projection.
  • The degree is parallel to the axis, not perpendicular to it.
  • It’s not “wide”, for sure, it’s just the most.
  • “measured from this axis” is either meaningless or incorrect, I can’t say what exactly.

Believe me, I'm not trying to choose nits, it's just that I'm trying to figure out what this code should do, and a bad comment is worse than no one. I see what this function does (assuming functions like "Dot" work as advertised), but I still don't know if it does what you want.

Now I look at DetectCollision (which is more than just collision detection):

  /// Projects two imaginary lines even with each edge, 
 /// equal to the width of each object while looking at 
 /// that edge, then checks to see if they intersect. 

What? All I can do is ignore this and look at the code ... There are parts that don't make much sense (for example, why the hell are you projecting a body onto each of its edges?), So reverse engineering will be very difficult .

If I knew the algorithm you tried, I could try to find the error. If the code worked, I could try to output the algorithm. But if the code does not work and (as I suspect), you really do not know the algorithm yourself, we are kind of stuck.

An approach may work here . This function is too long, it does a lot, and you do not know which parts it does correctly. Ergo, you must break it down into several functions and test them individually. (I cannot do this myself for the reasons that Eric Lippert presented.) You can start by splitting into two functions that compute CollisionInfo (leaving the bodies constant), and another that regulates the movement of the bodies (leaving the CollisionInfo constant).

+2
source

Source: https://habr.com/ru/post/1300584/


All Articles