Search Unity

Picked up item moving through colliders

Discussion in 'Scripting' started by Zymes, Feb 21, 2017.

  1. Zymes

    Zymes

    Joined:
    Feb 8, 2017
    Posts:
    118
    I made a simple pickup script but all the examples make it kinematic, meaning they can move items through walls.

    This seems to be a common problem but all my efforts to search has found no solid answer.

    Some say make triggers. So that means I should have 10000 triggers in my world on every single collider??

    And even if I trigger my collider I made my script drop the item by making kinematic false. But I would rather have it just push it away from the object instead of dropping it. How can that be done?

    Just like the Half-Life gun, if you move it around a wall it doesn't just drop, the wall pushes the object away.
     
  2. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    Don't make it kinematic and use collision layers. You stick all the colliders of your player in one layer, and world objects can be in another layer. You can do collisions with your player normally, and on the script with your gun:
    Code (CSharp):
    1. void OnCollisionEnter(Collision collision)
    2.      {
    3.          if (collision.gameObject.layer == 10)
    4.          {
    5.                // Code here to push the gun away
    6.                // rotate it.  maybe move it back
    7.                // if it moves to far push the player back?
    8.           }
    9. }
    This way if all the world colliders are in layer 10, and your player's colliders are in layer 8.. you can make sure you only collide with world objects and and not all the pieces of the player

    Now if your question is how to make a gun push away from a wall. thats more complicated but you would roughly get the surface normal of the mesh your colliding with and then figure out which way to push your object based on that.
     
  3. kubajs

    kubajs

    Joined:
    Apr 18, 2015
    Posts:
    58
    One solution is to use spring joint or configurable joint. I made something similar to Roblox game (Lumber Tycoon) pickup and carry mechanic using spring joint. Rigidbody is still non kinematic during the carry. I set up the spring joint to break when exceeding some force threshold so you can hit a wall a bit and not lose the carried object but when trying hard to force the object against a wall or another object you can break the joint. Usually you don't lose the item when pushing against the wall. The effect is kind of elastic. You can set infinite break force so you'll never lose the object is needed. Then count with increased elasticity however. I guess you usually don't want to have the carried item blocked / stucked behind another object (static object) and then try to pull the object and realize you're 10 meters far from it and it still kind of moves... Therefore IMO the break force is necessary.
    Or another solution is to set the break force to infinite and just control break of the joint purely by script (e.g. when some distance between player and object is exceeded, then you'll break the joint manually).
    Nice effect using break force is you can set up how heavy items you can afford to pick up and carry. E.g. if you try to pick up a heavy object, the joint would be broken immediately and you'll not be able to move it. Similar to real life situation.
    BTW I update the rotation of the object based on player local rotation so it doesn't rotate freely but follows player rotation and the movement is directed by spring joint purely.
    It is a bit tricky to tweak the spring joint to the desired state (you need to play with the object's rigidbody settings (set drag to 5 or something like that) and of course spring joint / configurable joint settings, otherwise the object will bounce heavily like on pretty flexible spring which is not desired. Spring joint is created dynamically in the script and is attached on demand ( on player pickup).
    I can provide a script or some test scene later when I'm back at home today if desired.
     
  4. Zymes

    Zymes

    Joined:
    Feb 8, 2017
    Posts:
    118
    Yes please. I don't think I can figure all that out.
     
  5. kubajs

    kubajs

    Joined:
    Apr 18, 2015
    Posts:
    58
    PlayerGrabItem.cs (attached to Player - in this case player is standard asset RigidBodyFPSController with MainCamera as a child object of player) =>
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3.  
    4. public class PlayerGrabItem : MonoBehaviour
    5. {
    6.     [SerializeField] private float _max_reach = 2f;
    7.  
    8.     private IGrabable _grabableObject;
    9.  
    10.     private void Update()
    11.     {
    12.         if (_grabableObject != null)
    13.         {
    14.             _grabableObject.Carry();
    15.         }
    16.         else if (Input.GetButtonDown("Fire1"))
    17.         {
    18.             var ray = Camera.main.ScreenPointToRay(new Vector3(Screen.width * .5f, Screen.height * .5f));
    19.             RaycastHit hit;
    20.             if (Physics.Raycast(ray, out hit, _max_reach))
    21.             {
    22.                 _grabableObject = hit.collider.GetComponent<IGrabable>();
    23.  
    24.                 if (_grabableObject != null)
    25.                 {
    26.                     _grabableObject.Grab(hit.point, Camera.main.transform);
    27.                 }
    28.             }
    29.             return;
    30.         }
    31.         if (_grabableObject != null && (Input.GetButtonUp("Fire1") || _grabableObject.JointWasBroken))
    32.         {
    33.             _grabableObject.Drop();
    34.             _grabableObject = null;
    35.         }
    36.     }
    37. }
    38.  
    IGrabable.cs interface =>
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. internal interface IGrabable
    4. {
    5.     bool JointWasBroken { get; }
    6.     void Grab(Vector3 grabPoint, Transform grabber);
    7.     void Carry();
    8.     void Drop();
    9. }
    ItemGrab.cs (attached to any object which is desired to be grabbed and carried - e.g. cube) =>
    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3.  
    4. [RequireComponent(typeof(Rigidbody), typeof(Collider))]
    5. public class ItemGrab : MonoBehaviour, IGrabable
    6. {
    7.     private Rigidbody _rigidbody;
    8.     private Vector3 _targetLocalPosition;
    9.     private Quaternion _rotationOffset;
    10.     private SpringJoint _springJoint;
    11.     private GameObject _itemHolder;
    12.     private float _originalObjectDrag;
    13.     private float _originalObjectAngularDrag;
    14.     private bool _originalRigidBodyIsKinematic;
    15.     private Transform _grabber;
    16.  
    17.     public bool JointWasBroken { get; private set; }
    18.  
    19.     public void Grab(Vector3 grabPoint, Transform grabber)
    20.     {
    21.         JointWasBroken = false;
    22.  
    23.         _grabber = grabber;
    24.  
    25.         _rigidbody = GetComponent<Rigidbody>();
    26.  
    27.         _originalObjectDrag = _rigidbody.drag;
    28.         _originalObjectAngularDrag = _rigidbody.angularDrag;
    29.         _originalRigidBodyIsKinematic = _rigidbody.isKinematic;
    30.         _rigidbody.isKinematic = false;
    31.         _rigidbody.angularDrag = 10f;
    32.         _rigidbody.drag = 10f;
    33.  
    34.         _targetLocalPosition = grabber.InverseTransformPoint(transform.position);
    35.         _rotationOffset = Quaternion.Inverse(transform.rotation) * grabber.rotation;
    36.  
    37.         _springJoint = gameObject.AddComponent<SpringJoint>();
    38.         _springJoint.anchor = new Vector3(0f, .5f, 0f);
    39.         _springJoint.autoConfigureConnectedAnchor = true;
    40.         // _springJoint.connectedAnchor = transform.InverseTransformPoint(grabber.position);
    41.         _springJoint.spring = 100f;
    42.         _springJoint.damper = 10f;
    43.         _springJoint.breakForce = 100f;
    44.         _springJoint.enableCollision = false;
    45.  
    46.         //create go to connect Rigidbody to on grabPoint
    47.         _itemHolder = new GameObject("Item Holder");
    48.         _itemHolder.transform.position = grabPoint;
    49.         _itemHolder.transform.SetParent(grabber);
    50.         Rigidbody holderRigidbody = _itemHolder.AddComponent<Rigidbody>();
    51.         holderRigidbody.isKinematic = true;
    52.  
    53.         _springJoint.connectedBody = holderRigidbody;
    54.     }
    55.  
    56.     public void Carry()
    57.     {
    58.         //_rigidbody.MovePosition(_grabber.TransformPoint(_targetLocalPosition));
    59.         //transform.position = _grabber.TransformPoint(_targetLocalPosition);
    60.         transform.rotation = _grabber.rotation * Quaternion.Inverse(_rotationOffset);
    61.     }
    62.  
    63.     public void Drop()
    64.     {
    65.         _rigidbody.drag = _originalObjectDrag;
    66.         _rigidbody.angularDrag = _originalObjectAngularDrag;
    67.         _rigidbody.isKinematic = _originalRigidBodyIsKinematic;
    68.         _rigidbody.AddForce(Vector3.down * .1f);
    69.         Destroy(_springJoint);
    70.         Destroy(_itemHolder);
    71.     }
    72.  
    73.     private void OnJointBreak(float breakForce)
    74.     {
    75.         JointWasBroken = true;
    76.     }
    77. }
    All the spring joint settings is hardcoded in the ItemGrab script. Sorry for the hardcoded values all over the code. This is just a first test draft.

    Hope this helps a bit.
    If you guys come up with some interesting solution or improvement(s), please let me know.
     
    brainwipe likes this.
  6. Zymes

    Zymes

    Joined:
    Feb 8, 2017
    Posts:
    118
    Thank you that is really good. I increased the break force and it works good as starting point.
     
  7. Zymes

    Zymes

    Joined:
    Feb 8, 2017
    Posts:
    118
    Also what if I want to get rid of the "dangling" aspect and have it fixed in position? As fixed as if you are holding a weapon in your hand for example.

    I tried to change it to Fixed Joint but it was very choppy when you move the object.
     
  8. kubajs

    kubajs

    Joined:
    Apr 18, 2015
    Posts:
    58
    Maybe try configurable joint instead of spring joint. There are more parameters which can be tweaked so the effect could be probably improved. But I haven't tried yet.
    Don't know how to manage this in other way. You could restrict player movement when grabbed item hits something. When the object collides with another collider, you might raycast to all 3 directions (forward, left, right) and find out in what direction the object hit another one. You might then restrict player to move to that direction. But this is not natural as well. There are many possibilities but every one has some drawback.
    I'd like to know the approach as well.

    Probably to program the solution is not that hard as to figure out the logic how it should behave naturally....
    In real life when you carry some item and hit some obstacle by mistake, you usually drop the item (if the force is high enough) or minimally the item and your hands are pushed from the desired direction / position so it's similar to the spring joint approach.
     
    Last edited: Feb 22, 2017
  9. kubajs

    kubajs

    Joined:
    Apr 18, 2015
    Posts:
    58
    You could also increase the player's collider range on demand so the carried item is protected by the player's collider like a shield ;). Then if you bump to any obstacle with the grabbed item, player will stop as well.
     
  10. Zymes

    Zymes

    Joined:
    Feb 8, 2017
    Posts:
    118
    Hey again.

    Is it possible to rotate the object while on a joint? I tried to bind my alt key and rotate with the mouse but it doesn't work. It just stutters. Probably because the joint force will not allow it.

    Maybe I need to disable the joint or something while rotating?
     
  11. kubajs

    kubajs

    Joined:
    Apr 18, 2015
    Posts:
    58
    Hi sorry for late answere, haven't received notification. I think it should be possible as I directly rotated the carried object in the script to follow player's rotation. Without it it haven't rotated at all. I could also use configurable joint in case of problems.
     
  12. Paaske77

    Paaske77

    Joined:
    Jun 25, 2017
    Posts:
    6
    Hey i know this was posted a long time ago. I tried your solution. But nothing happens. I tried with some debug.log and i turns out that in line 26 in playergrabitem - _grabableobject is empty. How come? The object has the Itemgrab script
     
  13. kubajs

    kubajs

    Joined:
    Apr 18, 2015
    Posts:
    58
    Does the ItemGrab implement IGrabable interface and does the grabable item contain trigger collider?
    Should have since there is requiredComponent of collider type. Also max reach is by default set to 2 meters only. Are you close enough?
     
  14. Paaske77

    Paaske77

    Joined:
    Jun 25, 2017
    Posts:
    6
    Thanks for the reply! I think i forgot to implement the interface on Itemgrab. But now when i walk around with the cube, the cube just kind of jump up and down and from side to side. Looks pretty weird :D