[HELP] Ball Velocity changes after impact

I have set up the ball launch exactly as described in the course (well almost…used FixedUpdate() instead of Update() because I thought that having the ball velocity updated in the same way as the physics engine updates would help…I read somewhere that that the physics engine updates in fixedUpdate()). See the script below. The problem I’m seeing is that occasionally, the ball will hit the edge of the paddle when it is near the wall (so the ball is between the paddle and the wall) and the velocity will increase dramatically. sometimes so fast that it goes right through the wall colliders. Other times, the ball will impact a brick or the paddle in just the right way (hard to observe what exactly) and the velocity will decrease to almost nothing.

Is there a way to ensure that once the ball is launched it will maintain the exact same velocity all the time?

using UnityEngine;
using System.Collections;

public class Ball_Main : MonoBehaviour {

private Paddle paddle;
private bool hasStarted = false;
private Vector3 paddleToBallVector;

public float velocityXvector;
public float velocityYvector;

void Start () {
	paddle = GameObject.FindObjectOfType<Paddle>();
}

void FixedUpdate () {
	if(!hasStarted){
		this.transform.position = paddle.transform.position;
		if (Input.GetMouseButtonDown (0)) {
			hasStarted = true;
			this.rigidbody2D.velocity = new Vector2 (velocityXvector, velocityYvector);
		}
	}
}

void OnCollisionEnter2D (Collision2D collision){
	//play sound on collisions
	if (hasStarted == true){
		audio.Play();
	}
}

}

Hi again :slight_smile:

There are some other posts on the forum which cover this (I searched for “velocity fixedupdate”).

This one in particular explains the issue about the transfer of y to x…

HELP - My Ball slows down

Unfortunately this is part of the problem with using a physics engine for something fairly straight forward as a reflection (for the bounce).

You could check the values and limit them to specific ranges I suppose, you’d need to run that check in the OnCollisionEnter2D() method of the ball.

Also, if you are using Update() now instead of FixedUpdate() (I noticed you moved all of the code on the other post, rather than just the Input detection), you will want to maybe multiply the x and y velocity values by Time.deltaTime (example here)

I multiplied the x and y values by time.deltaTime with very poor results. Here is the code:

void Update () {
	if(!hasStarted){
		this.transform.position = paddle.transform.position;
		if (Input.GetMouseButtonDown (0)) {
			hasStarted = true;
			this.rigidbody2D.velocity = new Vector2 (velocityXvector * Time.deltaTime, velocityYvector * Time.deltaTime);
		}
	}
}

It builds just fine. but when I test it in the game the ball barely moves at all. It launches but it is super slow. I thought that perhaps I should do the multiplication differently so I also tried:

this.rigidbody2D.velocity = new Vector2 (velocityXvector, velocityYvector)* Time.deltaTime;

with the exact same results.

As for the post you linked to describing using rounded corners in colliders, I will try that. My paddle is already an oval shape. I just will need to go in a change the collider on a bunch of different prefabs. I will set up a test level and change one prefab to test.

This is going to come down to how you calculate your velocity… Time.deltaTime is going to be a very small number, so multiplying it by your velocity vectors will also generate a small number…

Are you currently passing in the velocityXvector and velocityYvector from the inspector, like a kind of speed setting as it were?

Assuming so… if instead you just pass in one value for speed as a float (maybe pass that through at about 10f and then something like this (example only);

    float xVelocity = paddleToBallVector.x * speed;
    float yVelocity = speed - Mathf.Abs(xVelocity);

Then apply your Time.deltaTime to each in your new Vector2. Try that… see what happens…

The purpose of the code is to give a specific starting velocity to the ball rigidbody, while keeping the ball position attached to the paddle position before launch, after that the code will be skipped due to the !hadStarted condition.

So, since the paddle position is updated inside Update (look at the paddle script), in order to keep timing coherence, you have to put the code inside Update and not FixedUpdate.

After the launch, however, the only thing that drives the ball rigidbody velocity is the physics engine itself, so any kind of odd behaviour depends on that, and only that.

That’s why I suggested, in the post linked by Rob, to use “rounded” colliders for the paddle and the bricks, to avoid some rare collisions which will change the ball velocity in some odd ways (for example, when the ball hits the paddle at a collider corner).

1 Like

Well, obviously I did something horribly wrong! No compile errors. The script is as follows:

using UnityEngine;
using System.Collections;

public class Ball_Main_02 : MonoBehaviour {

private Paddle paddle;
private bool hasStarted = false;
private Vector3 paddleToBallVector;
private float xVelocity;
private float yVelocity;

public float speed;


void Start () {
	paddle = GameObject.FindObjectOfType<Paddle>();
	paddleToBallVector = this.transform.position - paddle.transform.position;
	xVelocity = paddleToBallVector.x * speed;
	yVelocity = speed - Mathf.Abs (xVelocity);
}

void Update () {
	if(!hasStarted){
		this.transform.position = paddle.transform.position;
		if (Input.GetMouseButtonDown (0)) {
			hasStarted = true;
			//this.rigidbody2D.velocity = new Vector2 (velocityXvector, velocityYvector);
			this.rigidbody2D.velocity = new Vector2 (xVelocity * Time.deltaTime, yVelocity * Time.deltaTime);
		}
	}
}

void OnCollisionEnter2D (Collision2D collision){
	//play sound on collisions
	if (hasStarted == true){
		audio.Play();
	}
}

}

The result is that the ball launches on click and then moves superslow.

Hello @Jesse_Sloan,

I see some stuff, which might go wrong.

  1. In your very 1st post of your thread:
    [HELP] Ball Velocity changes after impact
  • The ball starts in the center of the paddle. It is missing a “paddleToBallVector”-offset. This will be a real problem with collisions. You need to add this value to the “this.transform.position” in the Update-function. (You calclated paddleToBallVector in the Start() function of the 3rd post of this thread but never used it )
void Update () {
    if (!hasStarted) {
        this.transform.position = paddle.transform.position + paddleToBallVector;
    }
    if (Input.GetMouseButtonDown (0)) {
        hasStarted = true;
        this.GetComponent<Rigidbody2D> ().velocity = initialVelocity;
    }
}
  1. and (the question is good)

Is there a way to ensure that once the ball is launched it will maintain the exact same velocity all the time?

  • The ball changes its velocity every moment in this physical-simulation. It has a kinetic (velocity) and a potential (height in gravity-field) dependant energy. We are assuming no (sum-of-) energy gets lost (for example: no friction). This means the sum of both energies should be the same value all the time. tl;dr The ball should have/reach a maximum height and a maximum velocity.

    using UnityEngine;
    using System.Collections;
    public class Ball : MonoBehaviour {
    public Paddle paddle;
    private bool hasStarted=false;
    private Vector3 paddleToBallVector;
    // Use this for initialization
    // Cheap hack to conserve Energy

          private Vector2 initialVelocity;
          private float initialHeight;
          private float maxEnergy;
    

    void Start () {
    initialVelocity.x = 0.0f;
    initialVelocity.y = 10.0f;
    initialHeight = this.GetComponent ().position.y;
    float velocitySquared = (initialVelocity.x * initialVelocity.x + initialVelocity.y * initialVelocity.y);
    maxEnergy = velocitySquared;
    paddleToBallVector = this.transform.position - paddle.transform.position;
    }
    // Update is called once per frame
    void Update () {
    if (!hasStarted) {
    this.transform.position = paddle.transform.position + paddleToBallVector;
    if (Input.GetMouseButtonDown (0)) {
    //Debug.Log (“Left Mouse Button clicked”);
    hasStarted = true;
    this.GetComponent ().velocity = initialVelocity;

      	}
      }
    
      //[OPTIONAL] : Check for conservative energy
    
      if(this.GetComponent<Rigidbody2D> ().position.y>=initialHeight){
      	float vX = this.GetComponent<Rigidbody2D> ().velocity.x;
      	float vY = this.GetComponent<Rigidbody2D> ().velocity.y;
      	float currentEnergy = vX * vX + vY * vY;
      	float energyQuotient = Mathf.Sqrt(currentEnergy / maxEnergy);
      	if (energyQuotient > 1.0f) {
      		Vector2 correctVelocity;
      		correctVelocity.x = vX / energyQuotient;
      		correctVelocity.y = vY / energyQuotient;
      		this.GetComponent<Rigidbody2D> ().velocity = correctVelocity;
      		Debug.Log (correctVelocity);
      	}
      }
    

    }
    tbc… (currently editing)

Thanks for the answer.

I will definitely be looking into the solution you offered later today. I will let you know how it works out and what my final script looks like. I have, up until now been toying around with RigidBody2D.Addforce - Mode Impulse - instead of manipulating the x and y directly as the course instructor did. In my further reading, I see that this is really a no-no. It disappointments me that the instructor wrote code that works for demonstration in the class but is clearly incorrect. why teach the wrong way?! It makes no sense to me.

What I have so far seems to work. However, I will use some of what you put in your post as a means to check the speed and force it to stay at my chosen value instead of constantly accelerating.

As for the paddleToBallVector, I really actually want the ball to start in the middle of the paddle. My paddle is an oval shape with a hole in the center that fits the ball nicely. After hundreds of test launches it has never created a problem even though the ball is launched from within the paddle. Maybe it will lead to some problem but i have not seen it yet.

I’ve found that using this comparison to energyQuotient works:

if (energyQuotient > 0.5f)