Search Unity

Scripted Movement with collision detection

Discussion in 'Physics' started by SoxwareInteractive, Mar 13, 2015.

  1. SoxwareInteractive

    SoxwareInteractive

    Joined:
    Jan 31, 2015
    Posts:
    541
    Hi,

    for my game, I need to move game objects (rigidbodies) per script (C#) with collision detection. I think this is a common issue and since Unity only has PhysX and no other collission detection system, I'm wondering how you guys make scripted movements with collission detection (without all the stuff that simulates physical correctness like forces, rebounce,...). Other game engines like Game Studio offer these functionalities beside the physic engine they use.

    To be a little bit more precise:
    Similar to Move() of the CharacterController class, I want to pass the desired movement distance and direction as a vector. The function should perform the movement, but if the object collides with an obstacle, the movement stopps until the obstacle is not in the way anymore. It's important that the collision doesn't generate any rebouncing, resulting torque or short intersections (as it would be with the regular physic functions when forces are applied). It must be a "deterministic" behaviour just like an oldfashioned collision detection system that is not based on a physic engine like all the other engines have (e.g. AABB).

    In the past (Unity 4) I was using the Physic.SweepTest() method and a "cheap trick" to get exactly this behaviour:
    Code (CSharp):
    1. public bool Move(Vector3 dir)
    2. {
    3.       RaycastHit hit = new RaycastHit();
    4.       bool collided = GetComponent<Rigidbody>().SweepTest(transform.TransformDirection(dir), out hit, dir.magnitude);
    5.            
    6.       if(collided)
    7.       {
    8.            dir.Normalize();
    9.            // "Cheap Trick": Move it slightly away from the collision point,
    10.            // otherwise it will pass through it
    11.            dir *= hit.distance - 0.0001f;
    12.       }
    13.            
    14.       transform.Translate(dir);
    15.            
    16.       return collided;
    17. }
    This worked great in Unity 4, but since Unity 5 and the change to PhysX 3.3 I need to find a better solution than this as it is not working anymore.
    The problem is, that I needed to stop the rigid body before they intersect. Thus I had to subtract 0.0001f so that the rigid body would stop before intersecting. If I'm not doing this, the rigidbody would intersect with the obstacle and the the SweepTest can't detect the obstacle anymore.
    Playing around with this value (0.0001f) by making it bigger solved the problem in some cases. But sometimes it does not (it's either frame rate dependent, or a rounding problem of the float value). Anyway, I think that the whole approach isn't very clean at all.

    Do you guys have a better idea on how to solve this issue? How are you moving GameObjects per script with collission detection?

    Thanks for your help!
     
  2. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,834
    e.g. instead of moving the object, change the velocity of its rigid body, or `add a force` to make it move, then it will still react to other objects. if you make it kinematic or forceable change the position every frame, it will not react to anything.
     
  3. SoxwareInteractive

    SoxwareInteractive

    Joined:
    Jan 31, 2015
    Posts:
    541
    Thanks for your answer.

    I know that I could use the physic engine for the movement by applying forces. But then I would get all those things I don't want to have: Rebound, angular drag, short intersections,... Also the moving object should not affect the hit obstacle in any way (it stays where it is, no force applied).

    I want to have a Tetris like behaviour. A block is falling with constant velocity and when it hits an obstacle, it stops immediately until the obstacle is not in the way anymore, then it will continue with the movement. Physical correct movement is exactly not what I desire.

    I know that you can get rid of some of the physical reactions but I never managed to get this completely deterministic Tetris like movement by using the regular physic functions.
     
  4. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,834
    So your obstacles are just static colliders, maybe set to kinematic. Then they are immune to the moving object. Then use forces on the moving object or change its velocity directly. In my game I change the rigid body velocity because that's kind of like saying 'apply force to move in this direction by this distance', which stops when the object hits a static collider.
     
  5. SoxwareInteractive

    SoxwareInteractive

    Joined:
    Jan 31, 2015
    Posts:
    541
    I gave it a try with setting the velocity directly. I used the following script::
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class test : MonoBehaviour
    5. {
    6.  
    7.     // Use this for initialization
    8.     void Start () {
    9.    
    10.     }
    11.    
    12.     // Update is called once per frame
    13.     void Update () {
    14.    
    15.     }
    16.  
    17.     void FixedUpdate()
    18.     {
    19.         GetComponent<Rigidbody>().AddForce(new Vector3(0, -0.2f, 0), ForceMode.VelocityChange);
    20.     }
    21. }
    I checked "Freeze Rotation" and unchecked "Use Gravity" of the rigid body.

    The result is near to what I need, but what i don't like is that when the rigid body hits an obstacle, it moves into the obstacle for a few frames and then moves out of it. This short intersection doesn't look good at all. Is there any way to avoid it?

    With my SweepTest based function posted in my first post, there was no such short intersection.

    I really appreciate your help!
     
  6. imaginaryhuman

    imaginaryhuman

    Joined:
    Mar 21, 2010
    Posts:
    5,834
    increase the number of position and angle iterations in the physics settings? the object shouldn't penetrate the other object at all. Maybe also try `continuous collision detection` with interpolation, on the rigidbody/collider's settings.
     
  7. SoxwareInteractive

    SoxwareInteractive

    Joined:
    Jan 31, 2015
    Posts:
    541
    I made a test project from scratch now. There is only a cube acting as static ground and my rigid body (also a cube) falling with the script and the settings from my post above onto the ground. I set the Physic Solver Iteration Count to 100 (i.e. maximum) and there is still a short intersection noticeable.
    I'm pretty sure that this is in the nature of a physics engine. AFAIK a physics engine isn't working that precisely because it would be to slow.

    Thus I think using SweepTest() would be the most convenient method for me. But there is still this unsolved problem I was talking in my first post (the 0.001f substraction thing).