Search Unity

Restrict Z movement on character controller without breaking into colliders?

Discussion in 'Editor & General Support' started by helios, May 31, 2011.

Thread Status:
Not open for further replies.
  1. helios

    helios

    Joined:
    Oct 5, 2009
    Posts:
    308
    I figured this would be as simple as setting the z property of the Vector3 being passed into the controller's Move() function, but when I do this, and the player is moving (and keeps trying to move) into a collider, it just "forces" its way right through it. This does the same thing when I just try and force the transform.position.z to zero as well. If I don't restrict the Z movement, this doesn't happen, but the character just follows the path of the collider, thus moving its Z off of zero. Anyone know how to achieve this? Thanks.
     
  2. HarvesteR

    HarvesteR

    Joined:
    May 22, 2009
    Posts:
    531
    There is a physics component called Configurable Joint, that you can attach to your character, and anything you want to restrict to 2D movement.

    It has options for blocking translation and rotation on each axis, so to constrain an object to an XY aligned plane, you have to restrict Z translation and X and Y rotations. This way, the character gets locked to the 2D plane without relying on scripting, and it never moves away from it.

    Just remember to check the 'configured in world space' flag too ;)

    Hope this helps

    Cheers
     
  3. helios

    helios

    Joined:
    Oct 5, 2009
    Posts:
    308
    Thanks for the reply, I'll try this out. Probably better suited for the iOS forum, but do you think there are any performance implications for using this solution on iOS devices?
     
  4. HarvesteR

    HarvesteR

    Joined:
    May 22, 2009
    Posts:
    531
    Hmm, there shouldn't be, since that component is quite simple... There might be some overhead, but it should be negligible even on iOS.

    Good luck!

    Cheers
     
  5. helios

    helios

    Joined:
    Oct 5, 2009
    Posts:
    308
    Ok, so I've tried using a configurable joint, and I get the same results..it locks the Z, but you can "force" your way through another collider. Has anyone else had this issue?
     
  6. helios

    helios

    Joined:
    Oct 5, 2009
    Posts:
    308
    Still can't figure this one out. Can I not do this without the character controller breaking through an angled collider? If I don't restrict z, then the player will "follow" the shape of the collider, and move along the z. If I restrict the z, the player "breaks through" the collider if you keep moving into it.
     
  7. progpixel

    progpixel

    Joined:
    Oct 19, 2011
    Posts:
    2
    I know you posted this a while ago, but I also just ran into this problem myself. Here's what I came up with in case it helps you or anyone else.

    Essentially if you are mainly moving in the X-axis, you just need to gather the X-position of your character when aligned with the Z-axis, and if the Z-axis changes (if the character is pushed forward or back by the slope), then you revert back to that X-value and reset the Z-position back to zero. The Y-value would not be affected, as to not interfere with jumping.

    A quick code example:
    Code (csharp):
    1.  
    2. void Update()
    3. {
    4. // gathers current x coordinate if aligned with z axis
    5. float previousX = 0;
    6. if (transform.position.z == 0)
    7. previousX = transform.position.x;
    8.  
    9. /* EXAMPLE CHARACTER CONTROLLER MOVEMENT (Replace this section with your own movement code)
    10. // sets amount to move each call
    11. Vector3 movementOffset = new Vector3(X Velocity, Y Velocity, 0);
    12. // makes framerate independent
    13. movementOffset *= Time.deltaTime;
    14. // sets transform position
    15. collisionFlags = characterController.Move(movementOffset);*/
    16.  
    17. // resets x and z if misaligned with z axis
    18. if (transform.position.z != 0)
    19. {
    20.     Vector3 newPosition = transform.position;
    21.     newPosition.x = previousX;
    22.     newPosition.z = 0;
    23.     transform.position = newPosition;
    24. }
    25. }
    26.  
     
  8. rflagg

    rflagg

    Joined:
    Jan 18, 2013
    Posts:
    30
    Another potential solution for others encountering this:
    I came across this exact problem today. I want movement restricted to X/Y. I have two gameobjects. A Player GameObject with a CharacterController and a GameObject with a Sphere Collider. For the life of me I could not get the moving Player GameObject to stay at z=0. I tried the following:

    1. Setting transform position z to 0 on every Update. This would result in the exact behavior helios was seeing. The Player would just move right through the other Sphere collider.

    2. Freeze z position using rigidbodies/configurable joints. No luck. z position would still change.

    3. I then tried progpixel's recommendation. It works but then the Player GameObject wasn't sliding along the edge of the other sphere collider. It would just stay still until you move the Player away from the Collider.

    Finally, I just gave up on using a Sphere Collider and used a Capsule collider with it's axis aligned to Z. Then give it a nice sized height. If your Player now collides with that, it won't push it in the z direction. Just pushes back in the X/Y direction.
     
  9. Lythom

    Lythom

    Joined:
    Jun 29, 2013
    Posts:
    1
    Hi rflagg,

    got stuck by this problem too with the same "no sliding" problem from progpixel solution. I wrote a variation that allow sliding :

    in main loop :
    Code (csharp):
    1.  
    2. Vector3 oldPosition = transform.position;
    3. CollisionFlags collisionFlags = controller.Move(moveVect);
    4.  
    5. // freeze Z axis
    6. Vector3 position = transform.position;
    7. lockZAxis(collisionFlags, oldPosition, ref position);
    8. transform.position = position;
    9.  
    lockZAxis function :
    Code (csharp):
    1.  
    2. private void lockZAxis(CollisionFlags collisionFlags, Vector3 oldPosition, ref Vector3 position) {
    3.     if (position.z != 0) {
    4.         // is there any y-axis displacement during splice ? if yes assume the controlled object can slice on y-axis
    5.         if (Math.Abs(oldPosition.y - position.y) > 0) {
    6.             // slice up if collision below
    7.             if ((collisionFlags  CollisionFlags.Below) != 0) {
    8.                 position.y += Mathf.Abs(position.z);
    9.  
    10.             // slice down if collision above
    11.             } else if ((collisionFlags  CollisionFlags.Above) != 0) {
    12.                 position.y -= Mathf.Abs(position.z);
    13.             }
    14.         // no y-axis slice possible : stuck
    15.         } else {
    16.             position.x = oldPosition.x;
    17.         }
    18.         // reset z to 0
    19.         position.z = 0;
    20.     }
    21. }
    22.  
    it simply look if there was a y-axis change (even small) indicating that the object can slice this way, then report the z slice to the y-axis. It looks at collisionFlags to know if it must report the z move up or down.

    Hope it helps !
    Lythom
     
  10. Kastar-Troy

    Kastar-Troy

    Joined:
    Jul 18, 2012
    Posts:
    29
    I've actually found a much cleaner solution which works in a well designed platformer. Basically just before your move, add this code:

    Code (CSharp):
    1.             if(transform.position.z != zPosition)
    2.             {
    3.                 movementOffSet.z = (zPosition - transform.position.z) * 0.05f;
    4.             }
    5.              controller.Move (movementOffSet);
    Where zPosition is the one which you wish to lock.

    This will actually apply pressure back towards your z index, the further away, the more pressure. The 0.05f is a good amount per z value away, it pulls back with a decent force. This is working very well for my platformer with a lot of moving platforms, with complex mesh colliders as well. Only drama has been where the mesh is basically a triangle pointing upwards and then outwards towards the z axis, so you will slide off on bad meshes. Obviously need to change the collider, and have it to be a wide triangle on the z axis so it doesnt slide off towards the z axis, but still points up.

    Very happy with this solution! Working like a dream for collision, even on rotating platforms.
     
    Lucas-tg likes this.
  11. Lawrence760

    Lawrence760

    Joined:
    Jul 8, 2014
    Posts:
    1

    This works amazingly even in the most recent version of Unity. Thanks a lot Kastar! I spend a while searching for a solution to this.
     
  12. HAMSTAR_RISING

    HAMSTAR_RISING

    Joined:
    Jan 20, 2018
    Posts:
    5
    Old thread, but i found this also work really well:

    1. void LockPlayersZPos()
    2. {
    3. if (transform.position.z != 0f)
    4. {
    5. transform.SetPositionAndRotation(new Vector3(transform.position.x, transform.position.y, 0f), transform.rotation);
    6. }
    7. }
     
  13. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,459
    Closing this 12 year old thread due to all the necroing going on.
     
Thread Status:
Not open for further replies.