Physics2D ball trespassing paddle collider

Hello,

I am facing a problem where my game ball is going through the paddle for a frame and some strange behavior happens afterward.

Firstly, to share some details about my code, let’s say that what I am doing is to calculate where the ball hits the paddle in ‘x’ direction, calculate the desired bounce angle, move it to the contact point and move it a bit again to avoid any overlapping with the paddle, so no more hits with the paddle should happen during the following frames.

Since I am controlling how the ball bounces, I am using a Kinematic Rigidbody.

Below, you can see a good example of the ball bouncing off the paddle surface (bounce angle is fix to 70 degrees).

Even hitting the sides of the paddle works fine as far as the player is not moving the paddle too quick before the impact. The problem appears when the ball hits the side of the paddle and the player is moving it towards the ball at high speed. During the next frame the ball gets into the paddle collider partially and the contact point is inside, so when I move the ball to it, a new contact is detected and this happens for a few frames until the ball bounces well again. In the following video, you can see the wrong behavior:

My question is how I can avoid the problem of the ball going through the paddle when the paddle is being moved at high speed. Is there any way to block it? I tried to code several fixes like detecting the ball inside the paddle collider and move it to the border, but it does not look good visually speaking…

Do you have any ideas for achieving a good behavior when this happens?

Thank you very much in advance!

It looks like you’re not adding the bat’s velocity to the ball’s and so the ball is never able to get away from the bat.

1 Like

That’s correct, I am not adding bat’s velocity because I am not using a Rigidbody2D on the bat. Just the collider and these three lines in the Update method:

    private void Update()
    {
        // Get the mouse position in world coordinates
        Vector2 mousePos = mainCamera.ScreenToWorldPoint(Input.mousePosition);

        // Keep the y position of the paddle fixed, and set the x to follow the mouse
        targetPosition = new Vector2(mousePos.x, transform.position.y);

        // Smoothly move the paddle towards the target position using Lerp
        transform.position = Vector2.Lerp(transform.position, targetPosition, smoothSpeed);
    }

Could you please explain to me how I can track bat’s velocity, so I can add it to the ball? Thank you!

You should absolutely never ever modify the Transform when using 2D physics. That collider is implicitly STATIC (non-moving). The only thing that moves in physics is a Rigidbody2D and there are three body-types to choose from. You always use the Rigidbody2D API to cause movement. Colliders (and their shapes) are attached to bodies. What you are doing is causing the static collider there to be recreated again and again and appearing the new place you specify.

The paddle should have a Kinematic Rigidbody2D and you can set its velocity to cause movement.

Also, unless you’ve asked for physics to run per-frame, it doesn’t. It runs during the FixedUpdate. Changing the Transform doesn’t do anything to physics until the next time the simulation runs. Use a Rigidbody2D. :slight_smile:

1 Like

OK, that makes sense! Somehow I thought that moving a collider without a Rigidbody2D modifying its Transform would work, but now I understand that what I am doing is totally wrong. I will try this out and if I do not see any more issues I will mark your answer as the solution, so the topic can be close.

Thank you very much for your quick answers guys :slight_smile:

Well you are correct in that it will “work” in the sense that we’re forced to respect the Transform change request i.e. “the transform has changed, now we’ll have to reset everything instantly to that position” but it’s nearly always not what you intended. :slight_smile:

Given your game, you might also consider changing the SimulationMode2D to be Update. It’s up to you but it means physics is run per-frame at a variable frame-rate. This massively reduces any determinism but for most games this really doesn’t matter. The only other issue is that if you get very low frame-rates, the simulation accuracy suffers but again, it might not matter. Besides, there’s sub-stepping support in the Physics2D settings to get around this too

It does mean that you no longer need to bother with FixedUpdate and you can read input and control physics at the same time during update. Also, interpolation is automatically disabled because it’s no longer required.

It might be something you want to test.

1 Like

Ok, I will try that out too. So in case I choose to go with Update SimulationMode2D, I should move all my code from FixedUpdate to Update, correct? Is this option changed here?

On the other hand, I have just modified my code right now in order to move the bat using its rigidbody. I forgot to ask, but I understand I should move it using Rigibody2D.MovePosition method right? But in this case the bat’s velocity is (0,0) forever:

    private void Update()
    {
        // Get the mouse position in world coordinates
        mousePos = mainCamera.ScreenToWorldPoint(Input.mousePosition);
    }

    private void FixedUpdate()
    {
        rb.MovePosition(mousePos);

        Debug.Log($"Bat speed {rb.velocity}");
    }

Should I calculate the bat’s movement differently?

Yes, that is correct but I only mention this as a test. Ensure you have everything working as it is first. :slight_smile: Just remember that when you do that, everything is tied to your frame-rate which is (most of the time) variable.

For a paddle in this game, it’s as I said above; just set its linear velocity. In this case you only need to set: Unity - Scripting API: Rigidbody2D.linearVelocityX

Unlike in 3D physics, in 2D you can set a velocity on a Kinematic body-type.

1 Like

For some reason, Unity - Scripting API: Rigidbody2D.linearVelocityX seems to be not available for the Unity version I am using (2022.3.13f1) but I understand it should be the same as setting the velocity as usual, so I have done this:

    private void FixedUpdate()
    {
        // Get direction from the object to the mouse position
        Vector2 direction = (mousePos - rb.position).normalized;

        // Apply constant velocity towards the mouse
        rb.velocity = direction * speed;
    }

Somehow, something might be wrong with this implementation because my paddle goes crazy when the mouse cursor gets close to it, as you can see in the following video:

Apart from this, if I keep the mouse cursor away, movement seems to work fine. I tried to add the velocity to the ball, but then I realized that I was using the following line of code for moving it:

rb.MovePosition(rb.position + speed * Time.fixedDeltaTime * direction);

I read somewhere else that I should do it this way when working with Kinematic rigidbodies. It is possible that it was a 3D-related thread, so… should I always move the 2D rigidbodies using velocity instead of using MovePosition method?

Yes, that’s Unity 6+. Your code is fine.

What you likely read in context was devs telling other devs not to just set the position and always use MovePosition which is correct. You are not interested in setting a specific position, you just want to move at a constant speed so just set velocity. This isn’t an issue because nothing else is touching velocity; no gravity or anything else.

The best thing is to not try to find hard rules but to understand what each does and use it appropriately for your situation. There is no “best”, there’s only appropriate and not appropriate for a specific situation. :slight_smile:

2 Likes

Ok! I was asking that because using MovePosition works fine but setting the velocity causes the glitch shown in my last video. Of course, in order to add the paddle velocity to the ball I would prefer to use the velocity but I am not able to fix this problem…
Could anyone explain me what I am doing wrong? :frowning:

You can move the paddle using the mouse with this:

	rb.velocity=new Vector2(Input.GetAxisRaw("Mouse X")*10,0);

Or a smoother method:

	rb.velocity+=new Vector2(Input.GetAxisRaw("Mouse X"),0);
	rb.velocity-=rb.velocity*0.1f; // friction
1 Like

That makes no sense because MovePosition is simply a helper that sets the velocity (as you do) to ensure it moves to the specified position in a single timestep (usually Time.deltaFixedTime). There is literally no difference apart from it temporarily disabling drag.

Are you sure it’s not an artifact of another change?

Looks like the problem is related to the code I shared above:

    private void FixedUpdate()
    {
        Vector2 direction = (mousePos - rb.position).normalized;

        // Correction
        if (Mathf.Abs((mousePos - rb.position).x) < 0.1f)
        {
            direction.x = 0f;
        }

        rb.velocity = direction * speed;

        Debug.Log($"Paddle direction {mousePos - rb.position}, normalized {direction} and velocity {Velocity}");
    }

Not sure if it is the expected behavior but when the cursor is too close to the paddle center, the direction oscillates between values that are close to 0 and normalizing it looks like this:

I do not know if there is any other cleaner solution but at least with this tolerance check it works fine :slight_smile:

Thanks!

After investigating this topic further, I have achieved the desired behavior. There is only one thing pending that I am not sure how to solve… let’s see if I can explain my doubt properly.

  • On one side, I am moving the ball on a BallController script. During its FixedUpdate call I move the ball and I cast its circle collider at a (fixed time * ballSpeed) distance, so I can detect the collisions against bricks, walls and the paddle’s surface.
  • On the other hand, I am moving the paddle with a PaddleController script. During its FixedUpdate call I move the paddle and I cast its capsule collider on the desired ‘x’ direction at a (fixed time * paddleSpeed) distance, so I can detect the collisions with the ball at paddle’s sides and add the paddle’s velocity to the ball so it can get away properly.

Why doing it like this? Because I do not have an accurate way to detect the paddle’s side hits from the ball script or the paddle’s surface hits on the paddle.

The main problem here is that I am detecting collisions on FixedUpdate method of two different scripts and depending on the calling order this may not work as expected which seems wrong to me.

I was thinking of creating a common script where I can move the paddle, the ball and process the collisions, but this also seems wrong to me due to the fact that I am putting everything into one common script… and if I want to spawn more balls for example, I will need to store them in a list and iterate it to do the same on each instantiated ball.

So… hopefully my last question to finally close this topic: is there any other alternative way of doing this? Should I stick to a common script for controlling everything during the same FixedUpdate call?

Thank you very much once again for your help guys :slight_smile: !

Neither should be moving though, those are just script callbacks. The physics isn’t running until after they’ve all been called. Only when the physics simulation runs will things be moved so the order makes no difference.

You could check both the paddle and the ball if you like.

1 Like

Yes, but if the ball’s Rigidbody2D MovePosition method is called twice during the same FixedUpdate frame (one from paddle’s script and another one from ball’s script), just the last one persists, right? At least, this is what it is happening to me :frowning:

MovePosition doesn’t do anything until the simulation runs. It doesn’t do anything to the body. If you perform it 10 times before the simulation runs, only the last one will do do anything.

1 Like