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

How to know if an object is on top of another object?

Discussion in 'Editor & General Support' started by SanSolo, Jan 5, 2015.

  1. SanSolo

    SanSolo

    Joined:
    Feb 25, 2014
    Posts:
    85
    I'm creating a 2D puzzle. Here, the rule is that, player clicks on puzzle pieces. If the piece is not under another piece, it will be cleared. However, if there is another piece on top of it, a penalty is applied.

    One approach i thought was to use renderer.SortingOrder. But, problem is, it is common for two a higher order piece to exist in the scene and not be on top of a lower order object. In such a case, the lower order piece can be removed.
    For example, this image:

    Here, yellow is on top of blue, blue is on top of green and green is on top of red. So to solve the puzzle, the player has to first remove yello, then blue,green and finally red. Now if there was another orange piece not above or below any of these pieces, it can be removed first.
     
    Last edited: Jan 5, 2015
  2. SanSolo

    SanSolo

    Joined:
    Feb 25, 2014
    Posts:
    85
    Raycasting is another idea that i considered. But it works only if center of the object is overlapped by another object.
     
  3. N1warhead

    N1warhead

    Joined:
    Mar 12, 2014
    Posts:
    3,884
    You could always try adding triggers to each side of the blocks.
     
  4. ludiares

    ludiares

    Joined:
    Jul 14, 2011
    Posts:
    242
    Using colliders that are "higher" than your planes, and checking if they are colliding with another of these colliders, and if so, check if the other object is below or above your plane. I think that's the best idea
     
  5. kburkhart84

    kburkhart84

    Joined:
    Apr 28, 2012
    Posts:
    910
    You could simply keep track of the positions using your own depth order of sorts. If you are using a 3d view(even if 2d game) you can simply use an axis position, say if they are further in the z direction. And if you are using an ortho camera things won't get smaller no matter how far away they are.

    I assume you are already in some fashion sorting the objects by distance(or something) because they are getting rendered that way. Just use the same system.
     
  6. SanSolo

    SanSolo

    Joined:
    Feb 25, 2014
    Posts:
    85
    @N1warhead , it's 2D with Orthographic camera. I don't think i can add trigger to both sides of the blocks.

    @kburkhart84 , with that problem is, it is possible say one block has depth order 1 and another 0. Now, it is possible that both these objects are not in contact with each other. In that case, either of them can be cleared any time.
     
  7. Tomnnn

    Tomnnn

    Joined:
    May 23, 2013
    Posts:
    4,148
    If there is only ever 1 top-most item, you can check for clicks on just that object. If you receive a click event on an object that is not that one, apply your penalty. When you do click on the top one, remove it and set the next most-top one to be checked.
     
    angrypenguin likes this.
  8. N1warhead

    N1warhead

    Joined:
    Mar 12, 2014
    Posts:
    3,884
    That shouldn't matter. Unity is always 3d, the Camera is just Orthographic, that's it. It just puts your sprites across world space coordinates;

    I've never done 2D, but what I said above is true;
     
  9. SanSolo

    SanSolo

    Joined:
    Feb 25, 2014
    Posts:
    85
    @Tomnnn , there can be multiple topmost items.

    @N1warhead , thanks i'll implement that method and see.

    Hmmm i can't use rigidbody. So can't use triggers
     
  10. Tomnnn

    Tomnnn

    Joined:
    May 23, 2013
    Posts:
    4,148
    Is the issue choosing & determining the top or having them visually in order? If you're using an orthographic camera and you use 3D planes for the objects, you can just order their transforms. Since it's an orthographic camera, the sizes will never change, but the visual layering should. Then a raycast should work, and you can check the z distance from the camera to determine if it is a top item.
     
  11. SanSolo

    SanSolo

    Joined:
    Feb 25, 2014
    Posts:
    85
    @Tomnnn the problem is, it is possible that two pieces of varying transforms are on top. For example, in the image i have attached, say there is an orange block with same transforms ordering as the blue block. However, this orange block is placed separately without being in contact with any other piece. In such a case, blue block cannot be removed unless yellow is removed. But, orange block can be removed at any time since it is not in contact with any other block.
     
  12. Tomnnn

    Tomnnn

    Joined:
    May 23, 2013
    Posts:
    4,148
    Isn't that intended? I was already assuming your game was being inspired by mahjong :p

    What you're describing sounds like an issue with design. If it isn't intended for the orange block to be removed yet, why is it placed in the same layer as the blue block with nothing above it? What is supposed to happen? If your game is not supposed to have cases like this, do not consider them while designing your other systems. Focus on making sure your level generator does not bug out and produce incorrect layouts ;)

    If this kind of situation is intended to happen, how is the user supposed to handle it?
     
  13. DryTear

    DryTear

    Joined:
    Nov 30, 2012
    Posts:
    312
    Sorry, i didnt really understand your question.
     
  14. SanSolo

    SanSolo

    Joined:
    Feb 25, 2014
    Posts:
    85
    @Tomnnn this is embarrassing :x i did not know how Mahjong is played. Since you brought up the similarity, it's easier to find the required solution. Thanks a lot.
     
  15. Tomnnn

    Tomnnn

    Joined:
    May 23, 2013
    Posts:
    4,148
    Oh, haha. Is it similiar? Good luck then ^-^
     
  16. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,834
    I would just set the Z order of all objects based on the order they are layered, then with a raycast again triggers/colliders, if the player chooses one with a Z order that is `further away` than the closest remaining Z coordinate, it's an error. Keep track of the closest object's Z coord, use an array of object positions. etc

    The game is basically `pick up sticks` :)
     
  17. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,500
    You need to use a sweep rather than a ray, though from memory that only works for certain shapes.

    Edit: Checked the docs. Works for anything that's a Rigidbody as long as it's not using a mesh collider. You might also have to do an overlap test if there's any possibility that your objects can penetrate one another, as I don't know if the sweep will report pre-existing collisions.

    If there are non-overlapping stacks of different heights that won't work, though.
     
    AdamScura likes this.
  18. AdamScura

    AdamScura

    Joined:
    Mar 25, 2012
    Posts:
    55
    Why not use Physics2D.OverlapAreaAll to find all the overlapping sticks?

    Here is a working example:

    Code (CSharp):
    1. [RequireComponent(typeof(BoxCollider2D))]
    2. public class Stick : MonoBehaviour
    3. {
    4.     void OnMouseDown()
    5.     {
    6.         // Find all colliders that overlap
    7.         BoxCollider2D myCollider = GetComponent<BoxCollider2D>();
    8.         Collider2D[] otherColliders = Physics2D.OverlapAreaAll(myCollider.bounds.min, myCollider.bounds.max);
    9.  
    10.         // Check for any colliders that are on top
    11.         bool isUnderneath = false;
    12.         foreach (var otherCollider in otherColliders)
    13.         {
    14.             if (otherCollider.transform.position.z < this.transform.position.z)
    15.             {
    16.                 isUnderneath = true;
    17.                 break;
    18.             }
    19.         }
    20.  
    21.         // Take the appropriate action
    22.         if (!isUnderneath)
    23.         {
    24.             Debug.Log("HOORAY!");
    25.             Destroy(this.gameObject);
    26.         }
    27.         else
    28.         {
    29.             Debug.Log("OOPS!");
    30.         }
    31.     }
    32. }
     
    angrypenguin likes this.
  19. SanSolo

    SanSolo

    Joined:
    Feb 25, 2014
    Posts:
    85
    @AdamScura Thanks a lot. I'll try it.
    @angrypenguin i can't use rigidbody in this case.
    @imaginaryhuman i checked pickup sticks. Except for the pairing part, it does look similar. Thanks.
     
  20. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,500
    Why not?
     
  21. SanSolo

    SanSolo

    Joined:
    Feb 25, 2014
    Posts:
    85
    2D game, orthographic camera. Don't need rigidbody. If i make them rigidbody, they will all fall down
     
  22. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,500
    That just means the Rigidbody isn't set up properly.

    Also, I haven't looked into the 2D stuff much so it could be different, but it doesn't matter what type of game it is or what camera you have - if you've got an object with collider and it moves it should have a Rigidbody. Check the docs. Don't be fooled by the fact that the default Rigidbody config uses physics and gravity.
     
    AdamScura likes this.
  23. SanSolo

    SanSolo

    Joined:
    Feb 25, 2014
    Posts:
    85
    My objects don't move :)
     
  24. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,500
    Do they spawn/unspawn at runtime? Same thing. ;)

    Regardless, an existing solution has been demonstrated using only a built-in component. It's no pain to anyone else if you choose not to use it.
     
  25. SanSolo

    SanSolo

    Joined:
    Feb 25, 2014
    Posts:
    85
    Oh sorry i didn't pay attention to the sweep part. That certainly looks like the easiest way.
    All i need to do is check if it fits in it's current.Z+1. Thanks :)
     
    angrypenguin likes this.
  26. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,500
    Haha, no problem and you're welcome. :)
     
  27. AdamScura

    AdamScura

    Joined:
    Mar 25, 2012
    Posts:
    55
    Now that I think about it, @angrypenguin's approach is simpler than mine.

    Here is the code:

    Code (CSharp):
    1.  
    2. [RequireComponent(typeof(Rigidbody))]
    3. [RequireComponent(typeof(BoxCollider))]
    4. public class Stick3D : MonoBehaviour
    5. {
    6.     void OnMouseDown()
    7.     {
    8.         RaycastHit hit;
    9.         rigidbody.SweepTest(Vector3.back, out hit);
    10.         if (hit.collider == null)
    11.         {
    12.             Debug.Log("HOORAY!");
    13.             Destroy(this.gameObject);
    14.         }
    15.         else
    16.         {
    17.             Debug.Log("OOPS!");
    18.         }
    19.     }
    20. }
    21.  
    Enable "Is Kinematic" on the Rigidbody if you don't want them to move.
     
    SanSolo and angrypenguin like this.
  28. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,500
    You don't even have to check the hit collider. It also returns a bool, so you could do this...
    Code (csharp):
    1. bool didHit = rigidbody.SweepTest(Vector3.back, out hit);
    2. if (!didHit)
    3. {
    4.     Debug.Log("HOORAY!");
    5.     Destroy(this.gameObject);
    6. }
    7. else
    8. {
    9.     Debug.Log("OOPS!");
    10. }
    What was your other solution? (I remember looking at it and not thinking there was anything wrong with it.)

    Also disable gravity.
     
    SanSolo and AdamScura like this.
  29. AdamScura

    AdamScura

    Joined:
    Mar 25, 2012
    Posts:
    55
    Nice.

    My first solution was to use BoxCollider2D and Physics2D.OverlapAreaAll. Yours is more simple because it does not require a for loop. IMO SweepTest is the way to go.
     
  30. SanSolo

    SanSolo

    Joined:
    Feb 25, 2014
    Posts:
    85
    Thank you so much. I was stuck going nowhere on this :)
     
    angrypenguin likes this.
  31. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,500
    Hope it helps!
     
  32. SanSolo

    SanSolo

    Joined:
    Feb 25, 2014
    Posts:
    85
    Tried this script and it didn't work as expected.

    void Update () {

    if (Input.GetMouseButtonDown(0)) {
    Debug.Log("Pressed left click, casting ray.");
    CastRay();
    }
    }
    void CastRay() {
    Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
    RaycastHit hit,hit1;
    if (Physics.Raycast (ray, out hit, 100)) {
    didHit=GameObject.Find (hit.collider.name).GetComponent<Rigidbody>().SweepTest(Vector3.back,out hit1);

    if(didHit)Debug.Log ("Nope. Can't");
    else
    Destroy(GameObject.Find (hit.collider.name));
    }
    }

    For this, arranged objects as shown in the image:


    The horizontal bar is under the vertical bars and should not be destroyed. But, it is getting destroyed.

    However, i can combine ideas provided here to get a work around. One way would be to attach triggers, then transform.position.back to see if trigger is fired.

    Thanks for helping :)
     
  33. SanSolo

    SanSolo

    Joined:
    Feb 25, 2014
    Posts:
    85
    Hmmm it worked like a charm when i used -Vector3.forward instead of vector3.back. Thank you. That was a big help.
     
  34. AdamScura

    AdamScura

    Joined:
    Mar 25, 2012
    Posts:
    55
    That's really bizarre. -Vector3.forward == Vector3.back. Make sure your pieces are thin enough that they don't intersect each other on the Z-Axis or you will get unpredictable results. (example: use a z-spacing of 1 and a BoxCollider thickness of 0.9)

    FYI: You can simplify your code by taking advantage of OnMouseDown(). When the user clicks the mouse, Unity does a camera-raycast internally and only calls OnMouseDown() on the closest clicked GameObject. That means you don't need to do your own raycast if you use OnMouseDown(). See my previous example.

    Happy Coding! :)
     
    SanSolo likes this.
  35. SanSolo

    SanSolo

    Joined:
    Feb 25, 2014
    Posts:
    85
    With that approach, i'd have to attach a script to every piece. That's why i'm using my own raycasting :)
     
    angrypenguin likes this.