- Home /
Collision contact point check is late on physics
I'm making a breakout/arkanoid clone. Here's the logic of the ball movement:
I have a normalized vector3 called "direction" (its Z axis is always 0 since the ball moves on the X/Y plane)
Movement is set every fixed update with a Rigidbody.velocity assignment = direction * speed (int)
If a collision is detected the script takes the position of the collision relative to the ball (contactVector = Collision.contact.point - transform.position).
A check on this vector is made (INSIDE the collision function) and if contactVector.x or contactVector.y are != 0 then that axis is inverted in the original direction vector, bouncing the ball on bricks and walls.
I think that the logic is solid and it should work fine, but it doesn't. What I get is a velocity change of BOTH axis, always.
If i print the contactVector value of a, let's say, contact on the right wall I can see that the x value is fine (= to the sphere radius) but the value on the other axis is NOT zero. This non-zero value is proportional to the ball speed and its sign is opposite of the velocity. This means that the concactVector is calculated AFTER the collision. Why is that? I don't want to find a workaround about this issue, I'm here to learn how things work.
This is the code, I'm sorry if it's a bit messy (please ignore the player part), I hope someone can help me understand:
public class Ball : MonoBehaviour {
Rigidbody rb;
public float speed;
Vector3 direction;
[Range (0, Mathf.PI/4)]
public float maxAngle;
float angle;
float playerSize;
public GameObject player;
// Use this for initialization
void Start ()
{
rb = GetComponent<Rigidbody> ();
direction = new Vector3 (1, 1, 0f).normalized;
}
// Update is called once per frame
void Update ()
{
rb.velocity = speed * direction;
}
void OnCollisionEnter(Collision hit)
{
ContactPoint contactPoint = hit.contacts[0];
Vector3 contactDir = contactPoint.point - transform.position;
if (hit.gameObject.tag == "Player")
{
Vector3 contactPlayer = contactPoint.point - player.transform.position;
playerSize = player.GetComponent<Collider> ().bounds.size.x / 2;
angle = Mathf.PI / 2 -Mathf.Sign(contactPlayer.x) * Mathf.Abs(maxAngle * contactPlayer.x / playerSize);
direction = new Vector3 (Mathf.Cos(angle), Mathf.Sin(angle), 0f);
}
else
{
if (hit.gameObject.tag == "Bricks")
Destroy (hit.gameObject);
if (Mathf.Abs (contactDir.x) != 0)
direction.x *= -1;
if (Mathf.Abs (contactDir.y) != 0)
direction.y *= -1;
Debug.Log (contactDir);
}
}
}
Hi, a physics engine is never exactly accurate. You might get better results with settings the "continuous" collision flag on your objects' rigidbodies. Changing the physics settings may help get a more accurate result too (Solver iterations, solver velocity iterations) but will cost more CPU, it's often a trade off. For a more high level explanation, check wikipedia here: https://en.wikipedia.org/wiki/Collision_detection#A_posteriori_.28discrete.29_versus_a_priori_.28continuous.29
So, I could I get a precise result here? Are there alternative common practices to achieve what I'm trying? Thanks for the interesting info. (I've already tried to change the collision method in the rigidbody, with no luck).
Usually, you just let the physics engine detect collisions and compute collision responses. In your case, in Start() you setup the initial velocity using rb.velocity or AddForce. $$anonymous$$ake sure you also setup a physics material with correct bounce value for all object. If you want to make sure the speed is stable, in each FixedUpdate() (the physics engine is updated just after all FixedUpdate() are called, not Update()) you can normalize the rb.velocity & multiply it by speed. Events like OnCollisionEnter() are usually used to manage gameplay events, like damaging a brick or playing a sound.
The collisions may not be perfect but should be good enough for a game. I don't know the 2D physics engine a lot but maybe it's more accurate because things are simpler in 2D (and it's based on another engine). You can still make some 3D objects child of the 2D objects containing the colliders & rigidbody2D, gameplay is 2D but representation is still 3D.
If you want a perfect solution with exact contact points, you'll have to manage raycasts yourself but that's more advanced coding and there is usually no reasons to do that :-) but maybe you have one ?
btw, there is a tutorial for a breakout game here: https://unity3d.com/learn/tutorials/modules/beginner/live-training-archive/creating-a-breakout-game
This doesn't make sense to me, the physics for a ball already work as they should. Have you tried making a physics material for your ball that contains the appropriate bounce you want? Unitys physics engine already takes into account velocities, direction, force. and mass.
Yes, but since I had to modify the bounce once the ball hits the player's bar I wanted to manage everything from script. Now I get how that's impossibile.
Well, if you want to have a custom reaction, you'll need a custom solution. This is a classic with game engines, it never does exactly what we want :-) So, some trick i used some time ago to get precise collisions with a sphere/circle: imagine a rectangle, now, ins$$anonymous$$d of colliding a circle with this rectangle, imagine colliding a point with the rectangle: what shape would the rectangle have to have so that the point would react exactly like the center of a circle colliding with it ? The idea is to replace the circle by a point so it's easy to raycast it's movement, as the trajectory of a point is a line (or a curve if gravity is involved but it can always be approximated by a line). Now, imagine you scale the rectangle by exactly the circle radius = each of the 4 lines will be pushed by radius unit. We then have the exact same situation: point vs extended rectangle = circle with regular rectangle. There is a problem at the corners though: those must become circle ins$$anonymous$$d of points so a point/line colliding with them would react line a circle colliding a point/corner. Now, ins$$anonymous$$d of having a rectangle, you can have 2 rectangles, each one scaled in X or Y (not both) to extend them by the radius of the circle. Place 4 circle collider at each corner of the original rectangle. Now, you can use raycast from the center of the ball to the direction of it's velocity * fixeddeltatime. The position of the raycast hit should be very accurate. You now have to compute the new velocity. The difficulty is to know which collider was hit but you can test it's type (circle or rectangle, returned by the raycast functions) and compute the reaction from that. This is applicable to 3D as well, it can leads to lots of colliders though (but if they are static, unity will optimize computations). This concept can also be applied to other shapes, maybe using capsule colliders can be better in 3D. Well, I hope you get the idea, try drawing it on paper, it should be clearer :)
Your answer
Follow this Question
Related Questions
2D 360 degress platformer example needed 0 Answers
Ball physics and camera rotation determined by ball's collision contact normal 0 Answers
Softbodies in Unity 3D 1 Answer
Double Jump Physics 1 Answer
Help with 2D physics script 1 Answer