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

Question How to move Box across ground seams

Discussion in 'Physics' started by nopropsneeded, Aug 13, 2022.

  1. nopropsneeded

    nopropsneeded

    Joined:
    Jun 27, 2019
    Posts:
    20
    Hello.
    I'm making a 3D game in Unity.



    ss.png

    The player can push the box to move it, but it can get stuck at seams in the ground.
    The different colored grounds have the same height, but sometimes the box will stop at the seams and you won't be able to move it.

    The problem is that the Box doesn't stop every time, it usually crosses the seam smoothly, but sometimes it stops.
    The player is moved by the Character Controller, so Step Offset allows them to walk over bumps, but not the Box.

    Is there a way to make the Box always move smoothly without worrying about seams in the ground?

    The code for the player to push and move the Box is as follows.
    Thank you.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. namespace GameCreator.Characters
    6. {
    7.     public class MyPlayerCharacter : PlayerCharacter
    8.     {
    9.         const float pushSpeed = 4f;
    10.  
    11.         override protected void OnControllerColliderHit(ControllerColliderHit hit)
    12.         {
    13.             if (!Application.isPlaying) return;
    14.  
    15.             if (hit.gameObject.tag != "Box")
    16.             {
    17.                 base.OnControllerColliderHit(hit);
    18.                 return;
    19.             }
    20.  
    21.             if (!IsGrounded())
    22.             {
    23.                 base.OnControllerColliderHit(hit);
    24.                 return;
    25.             }
    26.  
    27.             float coefficient = this.characterLocomotion.pushForce;
    28.             if (coefficient < float.Epsilon) return;
    29.  
    30.             float angle = Vector3.Angle(hit.normal, Vector3.up);
    31.             if (angle < 90f) this.characterLocomotion.terrainNormal = hit.normal;
    32.  
    33.             Rigidbody hitRigidbody = hit.collider.attachedRigidbody;
    34.             //Debug.Log("angle = " + angle.ToString());
    35.             if (angle <= 91f && angle >= 5f && hitRigidbody != null && !hitRigidbody.isKinematic)
    36.             {
    37.                 var forceDirection = hit.gameObject.transform.position - transform.position;
    38.                 forceDirection.y = 0;
    39.                 forceDirection.z = 0;
    40.                 forceDirection.Normalize();
    41.  
    42.                 //Debug.Log("forceDirection = " + forceDirection.ToString());
    43.                 //Debug.Log("pushSpeed = " + pushSpeed.ToString());
    44.                 //Debug.Log("hitRigidbody.velocity = " + (forceDirection * pushSpeed).ToString());
    45.                 hitRigidbody.velocity = forceDirection * pushSpeed;
    46.             }
    47.         }
    48.     }
    49.  
    50. }
     
  2. K-e-n

    K-e-n

    Joined:
    Jul 1, 2020
    Posts:
    3
    Try adding all objects the box can be pushed over under a parent empty game object "ground" Then, instead of comparing tags, you could set the tag to the parent game object. and check if the parent game object tag is equivalent
     
  3. Augustinas-Simkus

    Augustinas-Simkus

    Unity Technologies

    Joined:
    Mar 9, 2020
    Posts:
    84
    My bet is that the right "ground" box is generating contacts that are opposite to the direction that you're trying to push the movable box in. See the attached image. If you imagine the scenario without the "green" box, that contact normal makes sense.

    The easiest solution is to just consolidate those 2 boxes and use 1 single collider for that platform. Another solution is to use Contact Modification API to discard contacts that you're aware are bad.
     

    Attached Files:

  4. nopropsneeded

    nopropsneeded

    Joined:
    Jun 27, 2019
    Posts:
    20
    In a real game, I move the box over the moving platform or other box.
    So it seems difficult to combine them into one big box collider.

    ss1.png ss2.png

    I'm interested in the Contact Modification API, but is it available for mobile devices?
     
  5. nopropsneeded

    nopropsneeded

    Joined:
    Jun 27, 2019
    Posts:
    20
    I did a google search about the Contact Modification API but didn't get enough information.
    Is it available now?
     
  6. Augustinas-Simkus

    Augustinas-Simkus

    Unity Technologies

    Joined:
    Mar 9, 2020
    Posts:
    84
    Yep, and it's fast.

    It's available in Unity 2021.2 and newer. So 2021.3 LTS has it.

    There's a long forum thread with examples and ideas for this feature:
    https://forum.unity.com/threads/experimental-contacts-modification-api.924809/

    And of course the documentation:
    https://docs.unity3d.com/ScriptReference/Physics.ContactModifyEvent.html
    https://docs.unity3d.com/ScriptReference/ModifiableContactPair.html
     
  7. nopropsneeded

    nopropsneeded

    Joined:
    Jun 27, 2019
    Posts:
    20
    Augustinas-Simkus likes this.
  8. nopropsneeded

    nopropsneeded

    Joined:
    Jun 27, 2019
    Posts:
    20
    画面収録_2022-08-26_22_11_59_AdobeExpress.gif

    I ended up attaching the script below to an empty object in the Scene.
    This works fine and allows me to move the Box left and right ignoring steps of less than 0.1.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Unity.Collections;
    5.  
    6. public class ContactHandler : MonoBehaviour
    7. {
    8.     const float BoxSize = 2f;
    9.     const float Threshold = 0.1f;
    10.  
    11.     public void OnEnable()
    12.     {
    13.         GameObject[] boxes = GameObject.FindGameObjectsWithTag("Box");
    14.  
    15.         foreach (GameObject box in boxes)
    16.         {
    17.             BoxCollider collider = box.GetComponent<BoxCollider>();
    18.             if (collider)
    19.             {
    20.                 collider.hasModifiableContacts = true;
    21.             }
    22.         }
    23.  
    24.         Physics.ContactModifyEvent += ModificationEvent;
    25.     }
    26.  
    27.     public void OnDisable()
    28.     {
    29.         Physics.ContactModifyEvent -= ModificationEvent;
    30.     }
    31.  
    32.     public void ModificationEvent(PhysicsScene scene, NativeArray<ModifiableContactPair> pairs)
    33.     {
    34.         foreach (var pair in pairs)
    35.         {
    36.             for (int i = 0; i < pair.contactCount; ++i)
    37.             {
    38.                 Vector3 n = pair.GetNormal(i);
    39.                 Vector3 p = pair.GetPoint(i);
    40.                 if ((n == Vector3.left || n == Vector3.right) && p.y <= pair.position.y - BoxSize / 2 + Threshold)
    41.                 {
    42.                     pair.IgnoreContact(i);
    43.                 }
    44.             }
    45.         }
    46.     }
    47. }