Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Making a ball bounce (programming custom movements)

Discussion in 'Scripting' started by darkgriffin, Nov 30, 2009.

  1. darkgriffin

    darkgriffin

    Joined:
    Nov 2, 2009
    Posts:
    113
    I'm trying to figure out how to make a bouncy ball. Currently, I'm stuck on getting a collision check to work. See below:

    Code (csharp):
    1. // On collision function
    2. function OnCollisionEnter(Collision : Collision) {
    3.     //Bounce off ground if collision was below ball
    4.     if(Collision.whatdoIputHere < collider.center.y) {
    5.         ySpeed = 0 - ySpeed;
    6.     }
    7. }
    The whatdoIputHere in the if statement is where I am stuck. Unity's documentation is a bit vague on how I can access the point of collision.

    What I am trying to do is create several if statement checks on the collision point, and compare them to the middle of the ball. The idea is that I have xSpeed, ySpeed, and zSpeed, and I only reverse the speed if the collision that called OnCollisionEnter actually is in the correct direction.

    I can get the direction, or at least I thought I could, by comparing the position of the collision and the middle of the ball(the center of the collider, in this case). But I need to figure out how to get just the x, the y, and the z of the collision point.

    I keep getting errors basically along the likes of "whatever path you are using to find the data isn't working". I have no idea what structure the Collision object has, or why I can't seem to find the point object/variable(which should be a vector3 type according to the documentation) inside it.

    Can someone show me the right object path to get to the position of the collision point?
     
  2. darkgriffin

    darkgriffin

    Joined:
    Nov 2, 2009
    Posts:
    113
    According to the documentation, the object structure is like so:

    collision.contacts

    "contacts" is supposed to be a ContactPoint, which means it contains .point. .point is supposed to allow me to then get .point.y, because .point is of the Vector3 type.

    However, doing this:

    collision.contacts.point.y

    Results in this error:

    Assets/Scripts/Ball Player Control.js(24,35): BCE0019: 'point' is not a member of '(UnityEngine.ContactPoint)'.

    What am I supposed to do to get the y of the point of collision? I'd say the documentation is out of date, but it's more then likely I just don't understand the structure of the objects, being new to Unity Engine. How can I get this variable from the collision object?
     
  3. darkgriffin

    darkgriffin

    Joined:
    Nov 2, 2009
    Posts:
    113
    Ok, let me be a bit more generic since I didn't get a reply. I probably am going about this the totally wrong way or something.

    How can I test if the ball hit something going down, up, left, right, back, or forwards? I know OnCollisionEnter is fired for a collision, and passes data back, but I don't know how to use the data properly without getting compile errors.

    I simply need to know which of the three axis the ball hit something on, so that I can reverse that axis accordingly.
     
  4. darkgriffin

    darkgriffin

    Joined:
    Nov 2, 2009
    Posts:
    113
    Umm...I've now studied some maths on what vector3 is, and I'm probably going about this the totally wrong way with the seperate movements for each direction.

    From what I understand, all vector3 objects store the seperate x,y,z positions and the movement vector for me. Which means I'm now reinventing the wheel.

    However, what I don't understand is how to apply this info to a collision between objects. I can get the collision fired off by using OnCollisionEnter, but because this function is not run in my main movement, I have no way of telling which way I just tried to move something(and thus where the collision is relative to the object).

    I gather the solution is to use the vector3 of both the collision object passed in by OnCollisionEnter, and the vector3 position of the object the script is attached to(which should be my ball, obveously).

    However, all the examples deal with vague concepts like "up" and "down" and the like, that arn't applicable to the sphere collider object, only the demonstrated player collide objects. I need to do this with a sphere collider, which apparently doesn't support those fancy functions.

    Is there a simple example of a custom collision using basic primitives I can look at?
     
  5. ivkoni

    ivkoni

    Joined:
    Jan 26, 2009
    Posts:
    978
    Not sure I followed everything in your thread, but
    you can use a normalized velocity vector and a speed (float) to represent how your ball moves around. To make it bounce off something you can reflect the velocity vector based on the normal of the contact...

    for example:

    Code (csharp):
    1. function OnCollisionEnter(collision : Collision) {
    2.  for (var contact : ContactPoint in collision.contacts) {
    3.         ballVelocity = Vector3.Reflect (ballVelocity,contact.normal);
    4.     }
    5. }
    if this is not what you are asking, then sorry I misunderstood.
     
  6. darkgriffin

    darkgriffin

    Joined:
    Nov 2, 2009
    Posts:
    113
    Actually...that might get me on track. At least I know how a collision loop should be structured, and how to handle multiple collisions in the array with the for loop.

    The way my physics are working right now is with three values, xSpeed, ySpeed, and zSpeed. When a collision happens, I've been trying to work out how to change only the axis that is affected. For example, if a floor is hit, only the y axis should reverse.

    The initial reasoning for the seperate speed values is that the ball itself will spin, and the player can move it along the axis using the arrow keys. Because of this combination, I need a way to control the ball along each axis seperately, instead of just using one vector to move it.

    One problem I have with reflect though. I need to know if the collision was with a floor, and reset the ball's y speed if it is. The ball bounces along z and x as normal, and off of roofs it bounces down along y as normal, but every new "bounce" it needs to be set to an exact amount of upwards thrust(determined by different factors like surface types and air in the ball). I don't think a reflect function is going to let me do that, since I'd need to know if the collision was below the ball.

    Any ideas how I could go about that special case?

    Edit: I've been trying to use

    Code (csharp):
    1. ballPosition = contact[0].thisCollider.transform.position;
    2. hitPosition = contact[0].point;
    And then compare the positions like so:

    Code (csharp):
    1.     //Bounce off ground if collision was below ball
    2.     if(ballPosition.y > hitPosition.y) {
    3.         ySpeed = bounce;
    4.         print("collision with floor at " + hitPosition + " new bounce started");
    5.     }
    6.     //Bounce off roof if collision was above ball
    7.     if(ballPosition.y < hitPosition.y){
    8.         ySpeed = 0-ySpeed;
    9.         print("collision with roof at " + hitPosition + " reversed y movement");
    10.     }
    11.    
    12.     //bounce off walls along x
    13.     if(ballPosition.x != hitPosition.x){
    14.         xSpeed = 0-xSpeed;
    15.         print("collision with wall on x at" + hitPosition + " x movement reversed");
    16.     }
    But unfortunetly the point of collision is not always exactly on the same x or y as the ball, even with flat surfaces(probably something to do with the ball moving along x and y at the same time but at differing speeds causing odd collision trigger layouts). Because of this, my x if statement is still firing on flat ground, reversing the x movement when it shouldn't. Is there another way to compare the positions of the collided point and the ball object that would give me better results?
     
  7. burnumd

    burnumd

    Joined:
    May 27, 2008
    Posts:
    367
    Collision.contacts actually contains an array of ContactPoint structs (denoted by the square brackets after ContactPoint in the documentation). What that error is telling you is that you can't access point without specifying an item in the array (I don't know the reasoning behind it, but parentheses around a type in error messages means it's an array of that type, so "(UnityEngine.ContactPoint)" means an array of ContactPoint where "UnityEngine.ContactPoint" would refer to the actual ContactPoint type). Since you can always be certain there is at least one contact point in a collision (per the documentation), you can safely use collision.contacts[0].point.y
     
  8. darkgriffin

    darkgriffin

    Joined:
    Nov 2, 2009
    Posts:
    113
    Ah, I think I get it now. .contacts is an array of all the collisions that triggered the OnCollisionEnter function in the first place.

    Also, by using the normal of the collision, I can get values telling me which direction the collision occured. For example, hitting a floor(falling along the y) gives a value of "0,1,0" for the collision.normal. This means that the surface hit was facing UP, or positive along the Y axis and nothing on the X or Z axis. At least as far as I understand it. I still have to run some more tests to be sure.

    Thus, instead of comparing the positions of the ball and point of impact, I can simply test for if the normal is equal to the normal of whatever wall/floor/roof I want to test for. Which should make the ball react properly instead of turning around on the x when it hits a floor with sideways x momentum.

    Off to play with values and recode the collision loops.
     
  9. darkgriffin

    darkgriffin

    Joined:
    Nov 2, 2009
    Posts:
    113
    Ok, I've got the following script now, attached to a 2 width ball object, with standard floors and walls to collide into. It's rather long, but pretty basic. I'm sure there are tons of better ways to code this, but I'm new to both java and unity's objects, so this is what I've scrapped together.

    Code (csharp):
    1. var pControl = 1;
    2. var bounce = 10;
    3. var air = 100;
    4. var airWidth = 1;
    5. var ySpeed = 0;
    6. var yGrav = 100;
    7. var xSpeed = 0;
    8. var xGrav = 0;
    9. var zSpeed = 0;
    10. var zGrav = 0;
    11.  
    12. //player control physics
    13. var pMaxSpeed = 5;
    14. private var pInputx = 0;
    15. private var pInputz = 0;
    16.  
    17. // Update function run every frame of the ball's existance.
    18. function Update () {
    19.     //calculate gravity into the ball speed
    20.     ySpeed = ySpeed - (yGrav);
    21.     xSpeed = xSpeed - (xGrav);
    22.     zSpeed = zSpeed - (zGrav);
    23.    
    24.     //read input and add to speeds
    25.     pInputz = Input.GetAxis("Vertical");
    26.     pInputx = Input.GetAxis("Horizontal");
    27.    
    28.     //add input to speed
    29.     xSpeed += pInputx;
    30.     zSpeed += pInputz;
    31. }
    32.  
    33. function FixedUpdate() {
    34.     //move the ball based on speeds, in units based on Time.deltatime
    35.     // This keeps the ball moving at the same speeds regardless of frame rates.
    36.     transform.position.Normalize();
    37.     transform.Translate(xSpeed*Time.deltaTime,ySpeed*Time.deltaTime,zSpeed*Time.deltaTime);
    38.     transform.position.Normalize();
    39. }
    40.  
    41.  
    42. // On collision function
    43. function OnCollisionEnter(collision : Collision) {
    44.     var floor = Vector3(0,1,0);
    45.     var roof = Vector3(0,-1,0);
    46.     var wallx = Vector3(1,0,0);
    47.     var wallx2 = Vector3(-1,0,0);
    48.     var wallz = Vector3(0,0,1);
    49.     var wallz2 = Vector3(0,0,-1);
    50.    
    51.     for (var contact : ContactPoint in collision.contacts) {
    52.         ballPosition = contact.thisCollider.transform.position;
    53.         hitPosition = contact.point;
    54.         ballPosition.Normalize();
    55.         hitPosition.Normalize();
    56.         print("ball position " + ballPosition);
    57.         print("hit position " + hitPosition);
    58.         print("normal of collision is " + contact.normal);
    59.         //Bounce off ground if collision was below ball
    60.         if(contact.normal == floor) {
    61.             ySpeed = bounce;
    62.             print("collision with floor at " + hitPosition + " new bounce started");
    63.         }
    64.         //Bounce off roof if collision was above ball
    65.         if(contact.normal == roof){
    66.             ySpeed = 0-ySpeed;
    67.             print("collision with roof at " + hitPosition + " reversed y movement");
    68.         }
    69.        
    70.         //bounce off walls along x
    71.         if(contact.normal == wallx){
    72.             xSpeed = 0-xSpeed;
    73.             print("collision with wall on x at" + hitPosition + " x movement reversed");
    74.         }
    75.         if(contact.normal == wallx2){
    76.             xSpeed = 0-xSpeed;
    77.             print("collision with wall on x at" + hitPosition + " x movement reversed");
    78.         }
    79.         if(contact.normal == wallz){
    80.             zSpeed = 0-zSpeed;
    81.             print("collision with wall on z at " + hitPosition + " z movement reversed");
    82.         }
    83.         if(contact.normal == wallz2){
    84.             zSpeed = 0-zSpeed;
    85.             print("collision with wall on z at " + hitPosition + " z movement reversed");
    86.         }
    87.     }
    88.    
    89. }
    A couple of problems:

    -When I let go of a direction, the ball doesn't stop moving. This is sort of like what I want, but without a limit, the ball can be sped up way too fast and fly through walls. I tried adding a limit, but my logic was probably broken cause the ball started doing really weird stuff and got lost in space.

    -The ball will randomly sort of just decide to have it's logic snap. The results of this are it either flies out through a wall, or it suddenly gets lots of momentum along the x coordinates instead of the y coordinates. I have absolutely no clue why it does this or how I can fix the behavior.

    If anyone has ideas how to fix the above issues, please post. I'm really stumped by the "random behavior" of the ball collision, especally.
     
  10. ivkoni

    ivkoni

    Joined:
    Jan 26, 2009
    Posts:
    978
    Ususally it is not a good idea to compare vectors with ==

    I am attaching a simple test case with the solution I gave you above. It seems to work:
    http://www.ivkoni.net/testBounce.html
    Normally it is not recommended to update the position of a rigid body directly (you should do it through adding forces or changing velocity) but depending on what you are trying to accomplish it might be ok.

    the attachment didn't work. You can download the scene here:
    http://www.ivkoni.net/TestBounce.zip
     
  11. Sam Welker

    Sam Welker

    Joined:
    Dec 23, 2010
    Posts:
    110
    I love how whenever someone posts a code and needs help the final product magically is twice as long... :) I may have to try this out. bouncy balls are fun gimicky toys.. they could make a good game!