# Ball inside Ball collision - stop bouncing

Show 40 post(s) from this thread on one page
Page 1 of 2 12 Last
• 03-01-2009, 10:49 PM
nolen
Ball inside Ball collision - stop bouncing
I'm currently wrapping my head around vectors and have tried my hand at ball vs ball collisions.

My problem comes when the ball has bounced enough and should finally be coming to rest.

Code below:
PHP Code:

``` import com.nolentabner.Vector2D; // initialization of the balls var smallBall:Sprite = new Sprite(); var bigBall:Sprite = new Sprite(); // bigger ball bigBall.graphics.lineStyle(1, 0x000000); bigBall.graphics.drawCircle(0, 0, 120); bigBall.x = stage.stageWidth/2; bigBall.y = stage.stageHeight/2; addChild(bigBall); // smaller ball smallBall.graphics.beginFill(0xFF0000); smallBall.graphics.drawCircle(0,0, 40); smallBall.graphics.endFill(); smallBall.x = stage.stageWidth/2 - 50; smallBall.y = stage.stageHeight/2; addChild(smallBall); // initialization of the vectors // main movement vector for smallBall var movement:Vector2D = new Vector2D(0, -2); // vector between ball centers var ballDist:Vector2D = new Vector2D(0,0); // used for collision reaction var projection:Vector2D = new Vector2D(0,0); var proj2:Vector2D = new Vector2D(0,0); // step per frame. will eventually be replaced with a timer stage.addEventListener(Event.ENTER_FRAME, onFrame); // enterframe function function onFrame(e:Event):void {     movement.y += 0.8;          ballDist.x = bigBall.x - smallBall.x;     ballDist.y = bigBall.y - smallBall.y;          // if the smaller ball is outside the bigger ball     // perform reaction     if(ballDist.len+smallBall.width/2 > bigBall.width / 2)     {             var wall:Vector2D = ballDist.leftNormal;         var dp:Number = dot(movement, wall.normal);         var dp2:Number = dot(movement, wall.leftNormal.normal);         projection.x = dp*wall.normal.x;         projection.y = dp*wall.normal.y;         proj2.x = dp2*(wall.leftNormal.x/wall.len);         proj2.y = dp2*(wall.leftNormal.y/wall.len);         proj2.x *= -1;         proj2.y *= -1;         movement.x = projection.x + proj2.x;         movement.y = projection.y + proj2.y;         movement.x *= 0.5;         movement.y *= 0.5;                  /*if(movement.len < 0.4)         {             movement.x = 0;             movement.y = 0;         }*/     }          smallBall.x+=movement.x;     smallBall.y+=movement.y; } // finds the dot product between two vectors function dot(v1:Vector2D, v2:Vector2D):Number {     return (v1.x*v2.x + v1.y*v2.y); }  ```
PHP Code:

``` package com.nolentabner {         public class Vector2D extends Object     {         // x         private var __x:Number;         // y         private var __y:Number;         // length of our vector         private var __len:Number;         // our normal         private var __normal:Vector2D;         // our lefthand normal         private var __leftNormal:Vector2D;         // our righthand normal         private var __rightNormal:Vector2D;                  // tracking variables         private var lenUndefined:Boolean;                  public function Vector2D(x:Number=0, y:Number=0)         {             this.__x = x;             this.__y = y;                          lenUndefined = true;         }                  /*** GETTERS/SETTERS ***/         // x         public function get x():Number         {             return __x;         }                  public function set x(num:Number):void         {             __x = num;         }                  // y         public function get y():Number         {             return __y;         }                  public function set y(num:Number):void         {             __y = num;         }                  // length (read only)         public function get len():Number         {             __len = Math.sqrt(__x * __x + __y * __y);                          return __len;         }         // normal (read only)         public function get normal():Vector2D         {             if(!__normal)             {                 //normalized unit-sized vector                 __normal = new Vector2D();                                  if (this.len>0)                 {                     __normal.x = __x/__len;                     __normal.y = __y/__len;                 } else                 {                     __normal.x = 0;                     __normal.y = 0;                 }             }             return __normal;         }                  // left hand normal (read only)         public function get leftNormal():Vector2D         {             return new Vector2D(__y, __x * -1);         }                  // right hand normal (read only)         public function get rightNormal():Vector2D         {             return new Vector2D(__y * -1, __x);         }                  /*** UTILITIES ***/         public function normalize():void         {             if (this.len == 0)             {                 __x = 0;                 __y = 0;             }             else              {                 __x /= this.len;                 __y /= this.len;             }         }                  public function toString():String         {             var st:String = "Vector2D: x=" + __x.toString() + " y=" + __y.toString();             st += " length="+len.toString();                          return st;         }     } }  ```
You can paste this into any version of Flash CS3 or greater.

edit: I posted the Vector2D class as well. It's nothing amazing but works for now.

You can see where i commented out a check where if the length of the movement vector is less than a specific amount, to set the x/y properties of the vector to 0. It somewhat worked but it didn't look natural.

Can anyone take a look at this and help point me in the right direction? I just want a mostly-realistic collision reaction between two balls where one is inside the other.

Here is an example of what happens for those who don't want to create their own .swf. I know how lazy some of you are out there ;).
http://www.nolentabner.com/projects/ball/test.swf

Thanks!
• 03-01-2009, 11:45 PM
TOdorus
PHP Code:

``` function updateVelocity(){    //Applying acceleration    VelocityVector += AccelerationVector //Can be any number, so you can use 0 when no acceleration    //Applying friction    VelocityVector.length * 0.8   //Or any number between 0 and 1 }  ```
So I think in your example you keep Velocity at a certain value and after a while when you want to stop it apply friction. To actually have balls of different weight you should also add a Force vector and acceleration should be:
PHP Code:

``` AccelerationVector += ForceVector / Mass  ```
• 03-02-2009, 12:10 AM
nolen
Quote:

Originally Posted by TOdorus
PHP Code:

``` function updateVelocity(){    //Applying acceleration    VelocityVector += AccelerationVector //Can be any number, so you can use 0 when no acceleration    //Applying friction    VelocityVector.length * 0.8   //Or any number between 0 and 1 }  ```
So I think in your example you keep Velocity at a certain value and after a while when you want to stop it apply friction. To actually have balls of different weight you should also add a Force vector and acceleration should be:
PHP Code:

``` AccelerationVector += ForceVector / Mass  ```

When you refer to Velocity, are you meaning the vector I named 'movement'? That one seems to change as gravity is applied and it bounces off the other circle. I think I'm still a bit confused.
• 03-02-2009, 12:46 PM
nolen
Bump for the "morning" crew.
• 03-02-2009, 01:55 PM
Frag
I couldn't tell by glancing from your code, but this may be a similar problem to one I had. I was working on collision with a point and a line and I had this infinite bouncing dilemma as well. I finally realized that it was my gravity that kept adding energy to the system every frame and so the point (ball in your case) would never come to rest because that downward vector was being redirected upward based on the angle of the line (or circle). I think I abandoned that project after that but at least this may have helped you find your problem? Or maybe I'm the only one with that issue =p
• 03-02-2009, 02:50 PM
nolen
Quote:

Originally Posted by Frag
I couldn't tell by glancing from your code, but this may be a similar problem to one I had. I was working on collision with a point and a line and I had this infinite bouncing dilemma as well. I finally realized that it was my gravity that kept adding energy to the system every frame and so the point (ball in your case) would never come to rest because that downward vector was being redirected upward based on the angle of the line (or circle). I think I abandoned that project after that but at least this may have helped you find your problem? Or maybe I'm the only one with that issue =p

Yeah, I can see that it's the gravity that's making it "shiver". My issue comes from knowing when to make the gravity stop affecting the ball.

When I turn it off too early, it stops the ball on the side of the larger ball, thus giving an unrealistic feel.

I'll keep playing around with the code, but hopefully someone can hop in and say, "hey dude, you're doing it wrong. try this". Haha.
• 03-02-2009, 03:28 PM
realMakc
Quote:

Originally Posted by nolen
You can paste this into any version of Flash CS3 or greater.

and it will not compile because...
Quote:

Originally Posted by nolen
the Vector2D class is just a small class I came up with to simplify things. I don't think it's relevant to post it.

yeah well I don't think it's relevant for me to re-write it to get your code to compile :P
Quote:

My issue comes from knowing when to make the gravity stop affecting the ball.
you dont need to turn it off, instead you need to multiply your ball velocity by something smaller than 1 (like 0.98) - this will eventually stop it.
• 03-02-2009, 03:30 PM
realMakc
• 03-02-2009, 04:44 PM
nolen
Quote:

Originally Posted by realMakc
and it will not compile because...yeah well I don't think it's relevant for me to re-write it to get your code to compile :Pyou dont need to turn it off, instead you need to multiply your ball velocity by something smaller than 1 (like 0.98) - this will eventually stop it.

Oh poop. You're totally right. I'll upload a copy of the class when I'm home from the office tonight.

So right now I'm adding gravity to the velocity (that's the 0.8 at the beginning of the frame loop, and if there is a collision I am multiplying the velocity by 0.5 (that's at the end of the loop). You're saying I need to multiply it by yet another number?

Sorry to be so dense. It was a long weekend.
• 03-02-2009, 06:41 PM
nolen
Alright, I've updated the first post with the code for the Vector2D class in case anyone else wanted to compile the example and whatnot.

I appreciate all the help so far. Thanks!
• 03-02-2009, 08:49 PM
Pazil
I'd say just check at what speed the ball is traveling at, and if yspeed is 0 (or very VERY close to zero), then set the yspeed to 0, and if xspeed is VERY close to zero, set that to zero too. That way, when you multiply your speeds by the gravity, it doesn't have anything to multiply by...
Not sure, but at first thought, it should work...

P.
• 03-02-2009, 08:54 PM
TOdorus
Allright, please read up on mechanics. A force creates acceleration which gives something velocity. Something with velocity generates friction with the stuff surrounding it.

So in mechanical terms multiplying something by .xx is friction. Gravity is not friction, it is a constant acceleration downward. So when a ball is thrown it moves at a pretty much constant speed in the horizontal vector (slight friction and only little time in the air) and a constant acceleration towards the earth in the vertical vector, because of gravity. Example
• 03-02-2009, 09:00 PM
Pazil
I know, but sometimes it's easier to simply multiply something by a number rather than adding it, solely for the behavour of multiplication versus addition.

I can only suggest what I've found to work in most cases, though of course, it doesn't reflect real life at all...something I should work on, since I like precise math anyway. Anyways, I'm paying attention to this thread...

P.
• 03-02-2009, 09:01 PM
nolen
Quote:

Originally Posted by Pazil
I'd say just check at what speed the ball is traveling at, and if yspeed is 0 (or very VERY close to zero), then set the yspeed to 0, and if xspeed is VERY close to zero, set that to zero too. That way, when you multiply your speeds by the gravity, it doesn't have anything to multiply by...
Not sure, but at first thought, it should work...

P.

I just tried that and it stops the ball unnaturally on either side, instead of in the middle. :( There has to be something simple that I'm missing.
• 03-02-2009, 09:12 PM
Pazil
hmmm...

Well, I've never used physics like yours before...I've only used shortcuts, and that was a long time ago too...

I'll try to read up on some physics, see if I find anything...

P.
• 03-02-2009, 09:15 PM
TOdorus
So your code in your first post is still the code your using for your onEnterFrame function? Because there's still no friction in there, only when the small ball it hits the big ball.
• 03-02-2009, 09:29 PM
nolen
Quote:

Originally Posted by TOdorus
So your code in your first post is still the code your using for your onEnterFrame function? Because there's still no friction in there, only when the small ball it hits the big ball.

So the "if(ballDist.len+smallBall.width/2 > bigBall.width / 2)" statement, I should multiply the movement vector by something like 0.5? That seems to slow the whole thing down and still cause a wiggle.

I think vectors and I aren't meant to be friends.
• 03-02-2009, 10:11 PM
TOdorus
Not vector but physics really. You seem to understand vectors just fine, it's just that if an object doesn't generate any friction it will never stop. Well here we go:

PHP Code:

``` // enterframe function  function onFrame(e:Event):void  {      movement.y += 0.8;            ballDist.x = bigBall.x - smallBall.x;      ballDist.y = bigBall.y - smallBall.y;            // if the smaller ball is outside the bigger ball      // perform reaction      if(ballDist.len+smallBall.width/2 > bigBall.width / 2)      {              (....)         //Friction is applied         movement.x *= 0.5;         movement.y *= 0.5;                    /*if(movement.len < 0.4) //This is still quite a big number. I'd go with a value like < 0.000001         {              movement.x = 0;              movement.y = 0;          }*/      } else {          //Friction is applied          movement.x *= 0.95;          movement.y *= 0.95;      }     smallBall.x+=movement.x;      smallBall.y+=movement.y;  }  ```
Unless the ball is in a vacuum, it generates friction by rubbing against the air or whatever gas it is in. This is considerably less friction then rubbing against a solid wall, but friction none the less.
• 03-02-2009, 10:17 PM
nolen
Quote:

Originally Posted by TOdorus
Not vector but physics really. You seem to understand vectors just fine, it's just that if an object doesn't generate any friction it will never stop. Well here we go:

PHP Code:

``` // enterframe function function onFrame(e:Event):void {     movement.y += 0.8;          ballDist.x = bigBall.x - smallBall.x;     ballDist.y = bigBall.y - smallBall.y;          // if the smaller ball is outside the bigger ball     // perform reaction     if(ballDist.len+smallBall.width/2 > bigBall.width / 2)     {             (....)        //Friction is applied        movement.x *= 0.5;        movement.y *= 0.5;                  /*if(movement.len < 0.4) //This is still quite a big number. I'd go with a value like < 0.000001        {             movement.x = 0;             movement.y = 0;         }*/     } else {         //Friction is applied         movement.x *= 0.95;         movement.y *= 0.95;     }    smallBall.x+=movement.x;     smallBall.y+=movement.y; }  ```
Unless the ball is in a vacuum, it generates friction by rubbing against the air or whatever gas it is in. This is considerably less friction then rubbing against a solid wall, but friction none the less.

OH. I see what you mean.

What I've done is tried something similar to what you've just posted and then also putting the smaller ball to sleep if the velocity is less than a certain amount. It seems to do the trick well enough for what I'm trying to accomplish.

Unless the ball is much smaller, then it sticks to the sides :/.

Thanks TOdorus. You're helping a lot.
• 03-02-2009, 10:45 PM
nolen
PHP Code:

``` import com.nolentabner.Vector2D; var gravity:Vector2D = new Vector2D(0, 0.8); var mass:Number = 5; // initialization of the balls var smallBall:Sprite = new Sprite(); var bigBall:Sprite = new Sprite(); // bigger ball bigBall.graphics.lineStyle(1, 0x000000); bigBall.graphics.drawCircle(0, 0, 120); bigBall.x = stage.stageWidth/2; bigBall.y = stage.stageHeight/2; addChild(bigBall); // smaller ball smallBall.graphics.beginFill(0xFF0000); smallBall.graphics.drawCircle(0,0, 60); smallBall.graphics.endFill(); smallBall.x = stage.stageWidth/2 - 50; smallBall.y = stage.stageHeight/2; addChild(smallBall); // initialization of the vectors // main movement vector for smallBall var movement:Vector2D = new Vector2D(0, -2); // vector between ball centers var ballDist:Vector2D = new Vector2D(0,0); // used for collision reaction var projection:Vector2D = new Vector2D(0,0); var proj2:Vector2D = new Vector2D(0,0); // step per frame. will eventually be replaced with a timer stage.addEventListener(Event.ENTER_FRAME, onFrame); stage.addEventListener(MouseEvent.CLICK, onClick); var i:int = 0; function onClick(e:MouseEvent):void {     switch(i)     {         case 0:             gravity.x = -0.8;             gravity.y = 0;         break;                  case 1:             gravity.x = 0;             gravity.y = -0.8;         break;                  case 2:             gravity.x = 0.8;             gravity.y = 0;         break;                  case 3:             gravity.x = 0;             gravity.y = 0.8;         break;     }     i++     if(i>3)     {         i = 0;     } } // enterframe function function onFrame(e:Event):void {     movement.x += mass*gravity.x;     movement.y += mass*gravity.y;          ballDist.x = bigBall.x - smallBall.x;     ballDist.y = bigBall.y - smallBall.y;          // if the smaller ball is outside the bigger ball     // perform reaction     if(ballDist.len+smallBall.width/2 > bigBall.width / 2)     {             var wall:Vector2D = ballDist.leftNormal;         var dp:Number = dot(movement, wall.normal);         var dp2:Number = dot(movement, wall.leftNormal.normal);         projection.x = dp*wall.normal.x;         projection.y = dp*wall.normal.y;         proj2.x = dp2*(wall.leftNormal.x/wall.len);         proj2.y = dp2*(wall.leftNormal.y/wall.len);         proj2.x *= -1;         proj2.y *= -1;         movement.x = (projection.x + proj2.x);         movement.y = (projection.y + proj2.y);         movement.x *= 0.3;         movement.y *= 0.3;              }     else {           //Friction is applied           movement.x *= 0.95;           movement.y *= 0.95;      }               smallBall.x+=movement.x;     smallBall.y+=movement.y; } // finds the dot product between two vectors function dot(v1:Vector2D, v2:Vector2D):Number {     return (v1.x*v2.x + v1.y*v2.y); }  ```
Here is the updated version. I added a mouse click listener to change the gravity clockwise. You can see that if you click fast, the ball goes all crazy and outside the larger ball.

I think something bigger is wrong with my code. Haha.
Show 40 post(s) from this thread on one page
Page 1 of 2 12 Last