Preventing overlap in OnCollisionEnter2D

So I have successfully built a script that can determine the physics for a ball reflecting linearly off of walls, similar to pong, while the ball is using a kinematic rigidbody. The issue is when the ball hits the wall and the OnCollisionEnter2D is called, the ball has had it's position adjusted based on it's velocity such that it is phased inside the wall a tiny amount. My understanding is that this is necessary in order for the engine to recognize the collision. The ball seemingly reflects like normal, but in the uncommon event that the reflected angle allows the ball to travel while still having it's collider phased inside the wall, and the ball impacts a different surface of the wall, this 2nd impact will not result in the OnCollisionEnter2D call and the reflecting result, since technically the collider never left the wall in this time. This causes the ball to fly past the wall and off-screen.

I've tried many solutions to fix this, mainly making it so that before the bounce occurs but in the collision event, I would move the ball back to the precise location where the edge of the ball hit the edge of the wall. This would mean that the collider of the ball is in-bounds at all times and therefore no odd behavior as a result of overlapping would occur. I've yet to find a good method to do this, and I'm wondering if there is one that could be suggested to me or if there's a different solution entirely (which doesn't involve changing the ball's rigidbody from kinematic).

Below is the appropriate parts of the script I have written currently, as well as an image of what the game view looks like for visualization.

public class sword : MonoBehaviour {

public Rigidbody2D rb;
public CircleCollider2D coll;

public float speed;
public Vector2 trajectory;
public GameObject Player;

// Use this for initialization
void Start () {
speed = 0.0f;
}

private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "player")
{
transform.position = new Vector2(-1000, -1000);
Player.GetComponent<movement>().equipped = true;
}
if (collision.gameObject.tag == "solid")
{
ContactPoint2D otherObject = collision.contacts[0];
Vector2 normal = otherObject.normal;
trajectory = Vector2.Reflect(trajectory, normal);

}
}

private void FixedUpdate()
{
//constantly calculating new velocity
rb.velocity = speed * trajectory;
speed = speed * 0.93f; //drag
if (speed < 0.0001f)
{
speed = 0.0f;
}
if (speed > 1)
{
Physics2D.IgnoreCollision(Player.GetComponent<CircleCollider2D>(), coll, true);
}
else
{
Physics2D.IgnoreCollision(Player.GetComponent<CircleCollider2D>(), coll, false);
}
}


}

More Details: The sword in this image is what acts like the ball. My speed variable is set from a separate script. Everything is on the default layer. The Rigidbody for the ball it is kinematic, and for the walls it is static. Trajectory is a Vector2 that corresponds with the angular direction of the ball.

• Have you tried scanning ahead of the ball with a CircleCast to detect the exact collision point before you overshoot it & overlap? I show a similar case for 3D in this answer. – DMGregory Nov 21 '18 at 11:38
• So have a Circlecast predict the next collision ahead of time and if the next velocity adjustment will push the ball over that point then make the position of the ball at the location where it won't go over? I'd have to be very exact with this position because if it's not right on the edge I believe the OnCollisionEnter2D would not trigger. I've thought about this solution before, but have had trouble implementing it. What kind of math should be used in this case? – Morgan Nov 21 '18 at 14:26
• Why would you need OnCollisionEnter2D to trigger in that case? You've already detected the collision yourself and can fire off any custom collision response behaviour you want. The math is effectively identical to what's used in the answer I linked. Feel free to edit your question if you have a specific aspect you're wondering about. – DMGregory Nov 21 '18 at 14:29

Are you sure, that the OnCollisionEnter2D is not triggered? Use a print() statement or a breakpoint to make sure it is not called. I think it is BTW but you using only the first element of the collision.contacts array for your calculations. Find a way to access the second or the requried element, to get the right normals to your Reflect function.

If I am wrong, then:

You can try using triggers(just tick the isTrigger on the colliders). Then you would use OnTriggerEnter2D and maybe OnTriggerStay2D in code to check for "collisions" and stuff. Find a way to get your contact points, then. See the link for a few suggestions.

Please let me know if this way it triggers the OnTriggerEnter call!

If that does not work, and you want to go with collisions, than you can find out how much is the ball in the wall (for example raycast from the ball) and push it back by that value (in OnTriggerEnter or Stay).

You can also just simply push it back by a value of your will(1/4 of the diameter of the ball for example) when in collision. You might have to adjust the graphics of the ball to hide this hack.

You shold try and play around with all of the above, and decide which one is for your setup.

• For your first suggestion, I had originally used triggers but found it to be mroe complicated than it's worth. The Vector2.Reflect() function is easier to use with a collision because I can obtain the normal of the surface I collided with. For the 2nd suggestion, I am wondering if the raycast from the position of the ball can really tell me how far it's gone in, since the center of the ball is not the point of collision, and rather the edge of the ball is. I was experiencing issues trying to program this pushback system when I tried it. Could you suggest a good way to program it? – Morgan Nov 21 '18 at 14:13
• Please see my edited answer. – formatc2013 Nov 21 '18 at 15:39