Search Unity

Player should stop if hitting another collider

Discussion in '2D' started by Traijan, Sep 4, 2019.

  1. Traijan

    Traijan

    Joined:
    Sep 4, 2019
    Posts:
    9
    Hello, first I want to say that my english is bad, sry!

    I wanted to make a little simple game to learn Unity a bit.
    The player is a circle, and is moving with the mouse position.
    My problem is, that I don't know how to stop the player if hitting another collider.

    The Code:

    Code (CSharp):
    1. public class PlayerMovement : MonoBehaviour {
    2.  
    3.     public Rigidbody2D rigidbody;
    4.  
    5.     bool col = false;
    6.  
    7.     void Start() {
    8.         Cursor.visible = false;
    9.         var mousePos = Input.mousePosition;
    10.  
    11.         var wantedPos = Camera.main.ScreenToWorldPoint(new Vector3(mousePos.x, mousePos.y, 0f));
    12.  
    13.         rigidbody.position = wantedPos;
    14.     }
    15.  
    16.     void Update() {
    17.         var mousePos = Input.mousePosition;
    18.  
    19.         var wantedPos = Camera.main.ScreenToWorldPoint(new Vector3(mousePos.x, mousePos.y, 0f));
    20.  
    21.         if (!col) {
    22.             rigidbody.position = wantedPos;
    23.         }
    24.     }
    25.  
    26.     void OnCollisionEnter2D(Collision2D collision) {
    27.         col = true;
    28.     }
    29.  
    30.     void OnCollisionStay2D(Collision2D collision) {
    31.         if(Input.mousePosition != collision.collider.transform.position) {
    32.             col = false;
    33.         }
    34.     }
    35. }
    I appreciate any help!
     
  2. bloodwolftico

    bloodwolftico

    Joined:
    Nov 13, 2017
    Posts:
    100
    Sounds like you are making a pinball game. Question, do you want your circle to move towards the mouse position at all times? Or just some times? Cause at this time your code is forcing the circle to always match the current mouse position (which is invisible but that's ok). This never stops. There's no code in your script to stop this. The circle is always following your cursor.

    If you want the circle to stop moving, you need a toggle to tell the game when the circle should move and shouldn't move. I see you have tried to accomplish this by adding a bool called "col". Seems to me that the problem here is that when "col" is false, you are moving the rigidbody's position towards wantedPos (which is the current mouse position), and when "col" is true, you are also moving the rigidbody's position towards wantedPos.

    If you want the circle to stop when hitting another collider, I would add on OnCollisionEnter2D a new vector 3, which would be the circle's current position, not the mouse's current position, so it stays there. Then if you want the circle to move again, when you are no longer colliding, have it do the normal follow mouse thing.
     
  3. Traijan

    Traijan

    Joined:
    Sep 4, 2019
    Posts:
    9

    First, thanks for your help!

    It's not really like pinball, I want that the circle is hitting enemies and destroys them. The enemies should spawn randomly. But that's not the current issue.

    Indeed I want that the circle is always on the current mouse position.

    But can you show me some code example? For better understanding, because I'm very new to Unity ^^'
     
  4. bloodwolftico

    bloodwolftico

    Joined:
    Nov 13, 2017
    Posts:
    100
    The best way to learn the code is to do research, project examples (tutorials) and then apply these to your own code :). Unless you are not interested in learning code, then I guess you can just use plugins or something like that.

    So, if I understand correctly, if you want the circle to always chase the current mouse position, that part is already done. What happens next depends on what you want to happen when the circle hits a collider. Do you want to just hit the collider (enemy), destroy it, and keep moving? Do you want a little bump? Have you tried adding a rigidbody to the circle so 2d Physics are applying upon collision?
     
  5. Traijan

    Traijan

    Joined:
    Sep 4, 2019
    Posts:
    9

    Yes my circle has a RigidBody (that's what the code uses) and I wanted to do the Enemy Collision with the "OnTriggerEnter2D" method. But my only issue at the moment is that the circle is not stop moving if htitting a wall (wall has Box Collider2D)

    And I do not understand how to fix this issue.
     
  6. bloodwolftico

    bloodwolftico

    Joined:
    Nov 13, 2017
    Posts:
    100
    I mean, if your circle is ALWAYS following your mouse cursor, it wont stop moving cause that's what you told the engine to do, keep the circle ALWAYS chasing and following your current mouse position. You need to define what your expected behavior is for "circle doesn't stop moving when hitting a wall" and what you are actually experiencing. Is the circle going through the wall? Cause that shouldn't happen if you have collidable objects. Is the circle just trying to keep chasing the mouse even when hitting the wall (and getting stuck to it)? Cause that sounds like what should happen as long as the circle is given the order to perma-chase your cursor and as long as you have collidable objects.

    For OnTriggerEnter2D the collider your player is colliding against has to have the "Trigger" check marked on its collider, otherwise it won't run any of the commands under this function. But if you do this, the collider won't be "collidable", as in... your player will go through it. If you want the player to collide with this object, and do something, you need to use OnCollisionEnter2D instead.

    So, question, what do you want to happen when the player's collider hits a wall collider? Or an enemy collider?
     
  7. Traijan

    Traijan

    Joined:
    Sep 4, 2019
    Posts:
    9

    When hitting the wall the player is going through the wall. Yes. And that's what I want to change. It should stop moving when hitting the wall, but when going in the opposite direction it should move. The Enemy is actually unimportant, because it works with killing the enemy. (I know that "isTrigger" should be checkmarked, when using "OnTriggerEnter2D").


    I only want at the moment that the Player stop moving with the mouse if the mouse is behind (or in) the wall.
    But I do not know how to make this.
     
  8. bloodwolftico

    bloodwolftico

    Joined:
    Nov 13, 2017
    Posts:
    100
    If you want the Player to stop moving if the mouse is behind or in a wall, the easiest way to do this is to have a collider in your player object, "Trigger" unchecked, and a Rigidbody, and then have another game object (wall) with a collider with "Trigger" unchecked as well. This way the circle will still try to reach the cursor, but it should collide with the wall and be unable to move into it.

    You should only use OnCollisionEnter2D if you need something else to happen when your player collides with another collider.

    You should only use OnTriggerEnter2D for specific scenarios like colliders used as triggers for other stuff to happen (for example, if a player falls/moves out of bounds and you want to kill him/her, you can use an empty object as a "killplane", with a trigger collider attached to it, that can then be used to "destroy" the player's gameObject when the player's collider hits it and make it look like the player died).
     
    Last edited: Sep 4, 2019
  9. Traijan

    Traijan

    Joined:
    Sep 4, 2019
    Posts:
    9

    My only problem is that the player still moves to the mouse, both wall and player has colliders and both has "isTrrigger" unchecked. That's my problem and I do not know how to change this... ._.
     
  10. bloodwolftico

    bloodwolftico

    Joined:
    Nov 13, 2017
    Posts:
    100
    I think it has to do on how you are moving the player. Per your code, you are telling the engine to move the player's rigidbody to a specific position. I think if you were to move the player differently, like using transform instead, the rigidbody can normally collid without actually being "forced" into the mouse cursor.
     
  11. Traijan

    Traijan

    Joined:
    Sep 4, 2019
    Posts:
    9
    Thanks, but moving the player with the rigidbody or with the transform does not help... Don't you know a solution where I can say in code that the player should stop when hitting the wall (which is the end of the camera)?
     
  12. bloodwolftico

    bloodwolftico

    Joined:
    Nov 13, 2017
    Posts:
    100
    Easiest way to do this that I've found out is to attach colliders to both objects (player and wall) and have one of them have a Rigidbody (usually the player).

    I haven't bothered into other ways to do this as this seems to work efficiently on all tests/projects I've tried, so no need to reinvent the wheel.

    I've actually moved game objects with code that makes them follow the player (like, chasing him, and even on a 2D sideway project, they still chase the player), and when they hit a wall, they stop (cause they have rigidbodies and both them and the wall have a collider).

    This is why I suggested a different way to move your circle as it seems this particular function is overriding the game's physics. I haven't had a chance to test your code myself so im not sure how this looks like, unless you have a video of it. Is it just ignoring the entire collision box and going through?
     
    Last edited: Sep 5, 2019
  13. Traijan

    Traijan

    Joined:
    Sep 4, 2019
    Posts:
    9

    I recorded a video where you can see what the problem is, here the link:
     
  14. bloodwolftico

    bloodwolftico

    Joined:
    Nov 13, 2017
    Posts:
    100
    I watched the video and I can see what you mean. I initially thought the circle was being "dragged" towards the mouse position, but now I see it is basically "attached" to the pointer, so whatever move you make, the circle is basically there, as it acts as a "fat pointer" so to speak. I wonder if Unity is not able to simulate collision fast enough for your circle collider to get stopped, or something along those lines (like, even w super fast frames the hand movement is just that fast). Or... this method of moving the circle just wont work with colliders. To be honest Ive never tried this (moving an object w the mouse directly), and given our tests, I am not sure what the best solution is.

    From some research, it looks like you should play around how the circle moves (velocity, drag, raycast, etc) and see if any one of those have a different result. That or just tell the circle to stop when you hit the wall, but then you have to create another way of "re-activating" the circle the way you want to.

    Here's a few things I found out:
    https://answers.unity.com/questions/179322/throw-an-object-on-collision-with-mouse-cursor.html
    https://gamedev.stackexchange.com/questions/81315/detect-mouse-collision-over-an-object
    https://forum.unity.com/threads/how...-it-collides-with-wall-also-rigidbody.212971/
    https://forum.unity.com/threads/move-object-with-mouse-but-stop-when-collision.135687/
     
    Traijan likes this.
  15. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,497
    Don't ever set the transform on a GameObject that has a Rigidbody (or any physics component) on it as you're completely stomping over the primary reason why it's there which is to control the Transform. That Rigidbody isn't moving at all, it's just being instantly teleported from position to position. You can easily be one side of a collider then past it but you're assuming that it's passing through it which isn't the case at all.

    You should use Rigidbody2D.MovePosition and Rigidbody2D.MoveRotation to adjust the pose. This at least tries to move through space from the old position to the new. This isn't guanteed to contact a collider unless the Rigidbody has continuous collision detection set. Note that MovePosition/MoveRotation are compatibile with Rigidbody interpolation too whereas setting the pose directly obviously isn't. Also, MovePosition/MoveRotation update the Transform when the simulation runs which is during fixed-update or if you're manually simulating, when you call simulate. Note that when the simulation runs during the fixed-update, you might have many frames in-between fixed-updates so you'll be asking to move to a position each frame but it'll just use the last one that was called when the simulation runs.

    For mouse movement you should take a look at the TargetJoint2D which moves a body to a specific target (or you can just use MovePosition above). This and other stuff is demonstrated here, specifically in this scene.

    Of course, you don't have to use or even manipulate the position of a Rigidbody; you can always just query any world position with, for example, an overlap circle query and use this rather than waiting for collision callbacks etc.
     
    Last edited: Sep 6, 2019
    bloodwolftico likes this.
  16. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,497
    bloodwolftico likes this.
  17. bloodwolftico

    bloodwolftico

    Joined:
    Nov 13, 2017
    Posts:
    100
    Great stuff MelvMay! I never thought about the Overlap circle, but makes total sense. Hope OP reads this and tests the options provided.

    Quick question, so if in my game I have enemies that have Rigidbodies, I should never use transform.Translate and should, instead, use MovePosition?
     
  18. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,497
    You should never touch the transform ever. If you add a Rigidbody then you're asking for it to do that so you should not touch it. When the simulation runs the rigidbodies write their poses to the transform based upon their movement.
     
    bloodwolftico likes this.
  19. bloodwolftico

    bloodwolftico

    Joined:
    Nov 13, 2017
    Posts:
    100
    Makes sense! Thanks!
     
  20. Traijan

    Traijan

    Joined:
    Sep 4, 2019
    Posts:
    9

    Hey many thanks! I had no time yesterday.

    I testet Rigidbody2D.MovePosition out and it works, with the collision detection! (I didn't know that exists..)

    But at a certain speed it will go through the walls, like this:


    Is this a issue with moving with the mouse? Because I'm thinking to only let the player move to the mouse position..
     
  21. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,497
    That's because of what I said above:
    Discrete collision detection moves to the new position and then the solver tries to solve the overlap. If you're slightly intersecting it on one side then it'll move it out of the closest overlap. That might be on the side you want but it might also be on the other side. Just as likely is that the new integrated position is doesn't overlap at all and is on the other side. In this case, there's no overlap to solve. This is called tunnelling and it's what you get with fast moving rigidbodies in physics engines. This is why continuous collision detection exists and performs a sweep to detect the time of impact. This is much better but it's not the default because it's much more expensive to calculate.
     
    Traijan likes this.
  22. Traijan

    Traijan

    Joined:
    Sep 4, 2019
    Posts:
    9

    Ah thanks!
     
  23. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,497
    If you're not actually interested in interacting with other colliders though you're much better off simply using queries like I mentioned above. If you want to perform sweeps like continuous collision detection then you can use Physics2D.CircleCast from the last mouse position to the current one. This is much quicker, you can do it per-frame and it doesn't impact any other colliders.