- Home /
Wrong ContactPoint position when modifying velocity
I'm working on a game where we have spaceships, and they can behave in two different ways when it comes to applying forces.
Space: We apply a force in the local forward direction. It pushes the ship. But if we turn, it gives this gliding effect.
rigidbody.AddRelativeForce(0, 0, force, ForceMode.Acceleration);
Plane: Simplified. When we turn, our speed is kept in the forward direction.
Vector3 dir = rigidbody.rotation.eulerAngles;
rigidbody.velocity = dir * rigidbody.velocity.magnitude;
rigidbody.AddRelativeForce(0, 0, force, ForceMode.Acceleration);
Both of these snippets are taken from FixedUpdate()
.
Now, my problem is that while in Space mode everything works as expected, in Plane mode my collisions seem to be located at the wrong location.
void OnCollisionEnter(Collision theCollision) {
Debug.Log(theCollision.contacts[0].point);
}
In Space Mode, I get results such as (46.1, 0.0, -71.5)
. All my ships are centered at y=0, so this is expected.
Now, Plane Mode. I get for a similar collision results like (-22.6, -158.3, 152.1)
The more the collision happens at an x close to 0, and the more the angle is facing up (towards +z), the more these coordinates get to the expected value.
I have spawned spheres at every collision point, and here is what they look like:
The few spheres that are stuck in the walls are actually at the right place and were obtained through Space physics. The rest are results of different hits in Plane mode. Keep in mind that they all have the same size...
Now, here are my thoughts about this. I think that cheating with the velocity the way I am doing it and the way I found in most of the missile/plane/car tutorials I've seen is not the way to go. Manually modifying the velocity vector resets the inertia (I think) and that may explain why my collisions are not logical. I know my physics, just not enough to make my theory work. Having a forward force like I have is good. But for rotations, we also need a centripetal force. Therefore, in addition to the Transform.Rotate(...)
that I am using (and which I will change to rigidbody.AddTorque(...)
), I think I need to add a perpendicular force. F = m * v² / r.
m is Mass, v is Speed and r is Radius.
Speed is simply rigidbody.velocity.magnitude
.
To make it perpendicular I used something like cForce * Vector3.Cross([forward vector], Vector3.up).normalized
.
Simple, no? Well not really. The values I tried for Mass and Radius don't give the expected results at all. I get a perpendicular force when I turn, but it does not really help.
I have been looking for Centripetal Force everywhere in the Forum and Answers of Unity, and I haven't found anything. Am I looking too far? Possibly.
There. I'm done talking, I've been wrapping my head around this issue for a long while, I don't know if someone recognizes this issue (contact points all over the place) or if you have made a working prototype of something moving with this kind of physics (with collisions).
Thank you very much, sorry for the wall of text. Ask if you need more info about my issue.
Answer by LalymMaed · Feb 10, 2014 at 01:23 AM
I have finally made it. Had the right formulas, just didn't use them right.
So, in FixedUpdate()
, I first handle my input and trigger turning accordingly.
if (leftPressed)
rigidbody.AddTorque(0.0f, -turnSpeed, 0.0f, ForceMode.Acceleration);
if (rightPressed)
rigidbody.AddTorque(0.0f, turnSpeed, 0.0f, ForceMode.Acceleration);
Then, I add the forward movement.
rigidbody.AddRelativeForce(0, 0, acceleration), ForceMode.Acceleration);
Here's the tricky part.
if (planeMode) {
float radius = Mathf.Abs(rigidbody.velocity.magnitude / rigidbody.angularVelocity.y);
float angVelY = rigidbody.angularVelocity.y;
float force = -angVelY * Mathf.Abs(angVelY) * radius;
Vector3 forceUnitVec = Vector3.Cross(rigidbody.velocity, Vector3.up).normalized;
rigidbody.AddForce(force * forceUnitVec, ForceMode.Acceleration);
}
So yeah here it is. Basically, I have used the following concepts:
1. I used a torque to trigger the rotation. In order to have it controlled, I increased the angular drag to 6 in my rigidbody.
2. Unity is dealing with the whole collision system, acceleration, whatnot. So I figured I should reuse what it gave me. Hence, as v = w r (`speed = angularSpeed radius`), I could come up with the radius pretty easily. To modify this radius, I can simply modify the turnSpeed variable.
3. I figured what the centripetal force was by using F = w^2 r (`centripetalForce = Mathf.Pow(angularSpeed, 2) radius`).
4. If you're wondering why I did not use Mathf.Pow()
in my code, it's because I used the sign of the angularVelocity.y
to make the force at the right side.
5. Now I use the Cross Product to figure out a unit vector that is perpendicular to both the up vector and the velocity of the plane.
6. Multiply the centripetal force by this unit vector and apply it to the rigidbody.
7. Profit!
The little collision spheres are now all appearing along the walls of the room, and I'm happy!
I have been trying to find this algorithm everywhere, I'm glad I managed to come up with it myself. By all means, try it and share it if it fits your needs or can help someone.
Things to keep in mind:
1. I am using this in a map where everything has a y = 0
. You will need a bit more algebra calculations if you try to do this in proper 3D.
2. I am using accelerations only (basically assuming mass = 1), so the mass of every object does not matter in my movement system. Only collisions are affected. If you want to accelerate differently according to mass, use ForceMode.Force
instead.
Your answer
Follow this Question
Related Questions
Drawing a line along all Contact Points? 0 Answers
How to use Best of contactPoints2d 0 Answers
Find point perpendicular to line 2 Answers
raycast to object, load wrong script!? 2 Answers
How do I make all these things occur? 0 Answers