Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

2d simple bounce issue

Discussion in '2D' started by xtoc, Sep 25, 2017.

  1. xtoc

    xtoc

    Joined:
    Jun 3, 2017
    Posts:
    15
    Hi,

    I'm using unity2D for creating a breakout game.
    It seems that often when the ball bounces to a brick, it will bounce the opposite site again (see red line, while the ball still should be going upwards)

    Below info about how it's been setup



    Bricks
    - have a boxCollider2d
    - bounce material (no fraction)
    - important : there are no gaps between those bricks

    Ball
    - have a boxCollider2d (also tried circle collider, but same issue)
    - ridgedbody with a vellocity (tried also continually collision dectection)
    - bounce material (no fraction)

    How can we solve something like this?

    Thank you in advance!

    Kind regards,
    Kurt
     
    Last edited: Sep 25, 2017
  2. hlw

    hlw

    Joined:
    Aug 12, 2017
    Posts:
    250
    You should check if the ball goes the wrong direction when the angle is within a range of angles, or one direction. Maybe the problem comes from your code having trouble with angles that should be lower than zero or something. (are you using raycasts to calculate the angle? )
    Zoom in and check also if it goes the opposite way when the ball's collider is hitting two bricks colliders at the same time.
     
  3. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,842
    Breakout (or any of the many games inspired by it — including mine, years ago) did not use a physics engine. Consider ditching Rigidbody, and doing it yourself. Then you can easily ensure that you flip only one axis when you hit something (i.e., flip velocity.y when you hit a horizontal edge, and velocity.x when you hit a vertical one).
     
  4. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,802
    I agree, but for simplicity's sake I would keep using the Rigidbody for the collision events. It also keeps your options open for more fancy physics stuff later on if you wanted.

    I used this code previously for reflecting a projectile, it's just a straight up velocity reflection with no friction or anything so it should work for your ball:
    Code (CSharp):
    1. private Vector2 lastVelocity;
    2. private Rigidbody2D rb;
    3.  
    4. private void Awake()
    5. {
    6.     rb = GetComponent<Rigidbody2D>();
    7. }
    8.  
    9. // save velocity for use after a collision
    10. private void FixedUpdate()
    11. {
    12.     lastVelocity = rb.velocity;
    13. }
    14.  
    15. // reflect velocity off the surface
    16. private void OnCollisionEnter2D(Collision2D collision)
    17. {
    18.     Vector2 surfaceNormal = collision.contacts[0].normal;
    19.     rb.velocity = Vector2.Reflect(lastVelocity, surfaceNormal);
    20. }
    You would just need to give the object the initial push in this script or elsewhere to start.
     
  5. xtoc

    xtoc

    Joined:
    Jun 3, 2017
    Posts:
    15
    Thank You all for all your answers.
    @hlw, i'm not using raycast (did try that first before moving over to using material instead)
    @JoeStrout how do you know when the ball go's up/left, that it will bounce on the upperside or on the left side of the block?
    @jeffreyschoch ch, thanks, i tried this code. The first what i notice is that it bounces against the wall like real psycics instead of keep going upwards like a real breakout game. I've tried normalize the vector with multiplying with a fixed speed -> but doing that, it will have some strange angles and the issue remains.

    My setup :
    The code to start the ball
    Code (CSharp):
    1. private Vector2 newVelocity;
    2. newVelocity.x = 10;
    3. newVelocity.y = 70;
    4. GetComponent<Rigidbody2D>().velocity = newVelocity.normalized * _settingsBallSpeed.speed;

    The bounce material will calculate the bounce angle by his own and will keep the same speed.
    There is also a gif animation added to show you the issue that can occur.
    setup.png
    You can see here that the ball sometimes go's up while it should go down.
    issue.gif

    (keep in mind that there are no gaps within the collisions between blocks, don't go off of the image, example :

    examplanogaps.jpg

    Can this be solved with the current setup, or should i find another solution?

    Thank you in advance!

    Kind regards,
    Kurt
     
  6. xtoc

    xtoc

    Joined:
    Jun 3, 2017
    Posts:
    15
    @jeffreyschoch
    I forgot the disable the gravity & mass on my ridgedbody2d with your code.
    This is what i get :
    example2.gif

    It will do strange things in the end of the gif (going vertical)
     
  7. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,802
    Looks like it's reversing in direction when it hits the very edge of a brick, and somehow results in the lower face being returned as the collision surface. That's a pretty common issue with edge-to-edge colliders. It's not a seamless transition between bricks.

    You could try to raycast towards the brick being hit on collision, and use the raycast's hit as the collision surface. That will likely guarantee that the visually correct face is returned. It's a little hacky but should be reliable.

    Code (CSharp):
    1. private Vector2 lastVelocity;
    2. private Rigidbody2D rb;
    3.  
    4. private void Awake()
    5. {
    6.     rb = GetComponent<Rigidbody2D>();
    7. }
    8.  
    9. // save velocity for use after a collision
    10. private void FixedUpdate()
    11. {
    12.     lastVelocity = rb.velocity;
    13. }
    14.  
    15. // reflect velocity off the surface
    16. private void OnCollisionEnter2D(Collision2D collision)
    17. {
    18.     Vector2 towardsCollision = collision.transform.position - transform.position;
    19.     Ray2D ray = new Ray2D(transform.position, towardsCollision);
    20.     RaycastHit2D hit = Physics2D.Raycast(ray.origin, ray.direction, 1f);
    21.     if(hit.transform != null)
    22.     {
    23.         Vector2 surfaceNormal = hit.normal;
    24.         rb.velocity = Vector2.Reflect(lastVelocity, surfaceNormal);
    25.     }
    26. }
     
    Last edited: Sep 25, 2017
  8. xtoc

    xtoc

    Joined:
    Jun 3, 2017
    Posts:
    15
    Hi @jeffreyschoch,

    I've tried and this is the result :

    this is the script on the ball (no other scripts are used in the demo)
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class ball : MonoBehaviour {
    6.  
    7.     private Vector2 lastVelocity;
    8.     private Rigidbody2D rb;
    9.     private Vector2 newVelocity;
    10.  
    11.     private void Awake()
    12.     {
    13.         rb = GetComponent<Rigidbody2D>();
    14.         newVelocity.x = 40;
    15.         newVelocity.y = 70;
    16.         GetComponent<Rigidbody2D>().velocity = newVelocity.normalized * 7f;
    17.     }
    18.  
    19.     // save velocity for use after a collision
    20.     private void FixedUpdate()
    21.     {
    22.         lastVelocity = rb.velocity;
    23.     }
    24.  
    25.     // reflect velocity off the surface
    26.     private void OnCollisionEnter2D(Collision2D collision)
    27.     {
    28.         Vector2 towardsCollision = collision.transform.position - transform.position;
    29.         Ray2D ray = new Ray2D(transform.position, towardsCollision);
    30.         RaycastHit2D hit = Physics2D.Raycast(ray.origin, ray.direction, 1f);
    31.         if (hit.transform != null)
    32.         {
    33.             Vector2 surfaceNormal = hit.normal;
    34.             rb.velocity = Vector2.Reflect(lastVelocity, surfaceNormal).normalized * 7f;
    35.            
    36.         }
    37.     }
    38. }
    39.  
    The result of that is strange :
    example3.gif

    It doesn't go like fixed angles and the issue is also there that it still go's the opposite direction.
    (i had fixed the vertical issue by checking the new velocity values (if one of them were 0 for example)

    One of the things i tried is to only enable the closest collision to the ball , so it will get merged with a collision of another block. But that wasn't also a possibility. Is there any way to check which side the ball hits the block?

    Many thanks already!
     
  9. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,802
    Alright, so I decided to recreate your project to figure out what the issue was. Turns out the main issue was that the raycast was detecting the Ball, and providing the wrong surface normal.

    Then I discovered other issues with bouncing perfectly into corners, etc, which could be alleviated by letting the physics system do the bouncing, which then reintroduces the seam-bouncing issue.

    So I managed to create a total solution here for a Unity-physics based game, but it's not as simple as I hoped it would be. Were going to let the physics system do the bouncing instead of managing velocity ourselves, and use CompositeCollider2D to make the bricks seamless. Try to follow these steps, and you should have a pretty good base for a brick breaker game.
    1. Create a new PhysicsMaterial2D, set the friction to 0, and the bounce to 1, name it "PerfectlyBouncy" or something.
    2. Make a parent object for all the bricks in the scene, and give it a CompositeCollider2D. It will automatically get a Rigidbody2D with it. You can set the Rigidbody to Static, and add the PerfectlyBouncy material to the Rigidbody. Set the CompositeCollider to Outlines and Synchronous.
    3. Make each one of your brick objects as two objects:
      1. A parent object, which has the sprite and the box collider, with "used by composite collider" checked. That collider will be merged with all the other bricks.
      2. Then a child object, with another box collider, slightly smaller than the parent object, and no checkboxes ticked. That collider will represent the individual brick to destroy, and won't be merged with the composite. Assign only this child gameobject to a new layer called "Bricks" or something appropriate.
    4. With all your bricks as children of the composite collider object, they will be combined into a seamless collider. In order to still detect individual bricks, the ball will raycast only for those child objects on the layer you made before, "Bricks".
    5. Apply the PerfectlyBouncy material to the ball's Rigidbody as well. After copying the changes to the ball script, setup the Ball in the inspector.
    So here is the revised ball script which can work with this system:
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class ball : MonoBehaviour
    4. {
    5.     public float initialSpeed = 7f;
    6.     public Vector2 startingDirection = new Vector2(4f, 7f);
    7.     public LayerMask brickMask; // assign the "Bricks" layer in the inspector
    8.  
    9.     private Rigidbody2D rb;
    10.  
    11.     private void Awake()
    12.     {
    13.         rb = GetComponent<Rigidbody2D>();
    14.         rb.velocity = startingDirection.normalized * initialSpeed;
    15.     }
    16.  
    17.     private void OnCollisionEnter2D(Collision2D collision)
    18.     {
    19.         Vector2 towardsCollision = collision.contacts[0].point - (Vector2)transform.position;
    20.         Ray2D ray = new Ray2D(transform.position, towardsCollision);
    21.  
    22.         // raycast for bricks
    23.         RaycastHit2D hit = Physics2D.Raycast(ray.origin, ray.direction, 1f, brickMask);
    24.         if(hit.collider != null)
    25.         {
    26.             // destroy the brick object here, keeping in mind that it's the parent object we want to destroy
    27.             Destroy(hit.collider.transform.parent.gameObject);
    28.         }
    29.     }
    30. }
     
  10. xtoc

    xtoc

    Joined:
    Jun 3, 2017
    Posts:
    15
    Hi @jeffreyschoch,

    First of all, really thanks for putting so much effort into it!
    I've tried now the setup from above.
    This is the result :
    example4.gif

    It seems that this setup will have the same kind off issue.

    I've added my source within this post.

    But i'll going to show you my settings as well :
    The bounce material :
    thing1.jpg

    The parent "group" brick
    thing2.jpg

    The brick (image & collider2d)
    thing3.jpg

    to be continue : ...
     

    Attached Files:

    bytasdelen likes this.
  11. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,802
    Make sure your brick child collider is smaller than the parent, your ball may be hitting the children, which are only used for the Raycast detection.

    I'll check out your zip. Thanks for the screenshots that makes it very clear.

    Edit:

    Upon opening your attached project, it appears to be working fine, and I do see that you've set it up correctly. I would suggest perhaps making the child colliders into triggers, and enabling "Queries Hit Triggers"
    Code (CSharp):
    1. Physics2D.queriesHitTriggers = true;
    You can set that once on start, or enable before the raycast and disable after, or enable it globally in the Physics2D settings.
     
    Last edited: Sep 26, 2017
  12. xtoc

    xtoc

    Joined:
    Jun 3, 2017
    Posts:
    15
    Part 2 :

    The child from a brick:
    thing4.jpg
    (the layer brick is also set. But i don't use it for now ,because i don't want the bricks to disappear (the game will have static bricks & breakable bricks). For testing that the angle go's like it should be, its better to don't remove the bricks for now :)

    The child collider has a smaller box as the brick
    thing5.jpg

    The ball :
    ball.jpg

    Brick Mask, is now set to "Nothing" for now.

    Is there any easy way to check which side the ball is coming from?
    Or is there another way to solve that?

    !! important, sometimes you have to move the ball a little bit to the right or left in the source example to re-procedure the issue.

    Many Thanks!

    Kind regards,
    Kurt
     
  13. xtoc

    xtoc

    Joined:
    Jun 3, 2017
    Posts:
    15
    Hi @jeffreyschoch,

    Thank you for your fast reply.
    Sorry about this, but you'll have to move the ball a little bit the the left or right from his start position.
    You'll see that the issue remains.

    (i could send you a new version where i set the ball in the correct position if you prefer)

    Thanks!

    Kind regards,
    Kurt
     
  14. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,802
    I'm unable to reproduce your issue even with the adjustments you suggested, but in my last post I detailed a potential fix.

    You could also try setting your ball to Continuous collision mode, preventing any unwanted moments of overlap due to high speed.
     
  15. xtoc

    xtoc

    Joined:
    Jun 3, 2017
    Posts:
    15
    Hi Jeffreyschoch,

    I think it's been solved now :
    example5.gif

    Turning on Continuous collision mode seems to doing his thing.
    I did turned this on before, but didn't solve my issue back then. For some reasons with this setup it does :)

    I'm really thankful for this! This way we still use unity physics in our game.
    (Working with Orioto art designer on this game)

    Going to some extra testing.

    Kind regards,
    Kurt
     
    LiterallyJeff likes this.